MON Price: $0.017904 (-6.25%)

Contract

0x3c6d09eb0e55495F7541f78fBdCDCb53CeaD5f93

Overview

MON Balance

Monad Chain LogoMonad Chain LogoMonad Chain Logo0 MON

MON Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
449852022025-12-28 11:34:1228 days ago1766921652  Contract Creation0 MON
Loading...
Loading

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

Contract Name:
AccountableAsyncRedeemVault

Compiler Version
v0.8.27+commit.40a35a09

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import "../constants/Errors.sol";

import {IStrategyVaultHooks, IUpdateLateStatus} from "../interfaces/IAccountableStrategy.sol";
import {PermissionLevel} from "../interfaces/IAccess.sol";

import {
    IAccountableAsyncRedeemVault,
    IControllerOperator,
    IAsyncRedeem,
    IAsyncCancelRedeem,
    IAccountableVault,
    VaultState
} from "../interfaces/IAccountableAsyncVault.sol";

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

import {AccountableVault} from "./AccountableVault.sol";
import {AccountableWithdrawalQueue} from "./queue/AccountableWithdrawalQueue.sol";

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @title AccountableAsyncRedeemVault
/// @notice An ERC4626-compatible vault with ERC7540 asynchronous redemptions
/// @dev Implements IAccountableAsyncRedeemVault interface for async operations and access control
/// @custom:security-contact [email protected]
contract AccountableAsyncRedeemVault is IAccountableAsyncRedeemVault, AccountableWithdrawalQueue, AccountableVault {
    using Math for uint256;

    uint128 private constant INSTANT_REQUEST_ID = 0;

    /// @notice AccountableAsyncVault constructor
    /// @param asset_ The underlying asset token for the vault
    /// @param strategy_ The loan manager contract address
    /// @param sharesTransferable_ Whether shares are transferable
    /// @param name_ The name of the vault issued share token
    /// @param symbol_ The symbol of the vault issued share token
    constructor(
        IERC20 asset_,
        address strategy_,
        bool sharesTransferable_,
        PermissionLevel permissionLevel_,
        string memory name_,
        string memory symbol_,
        uint256 precision_
    ) AccountableWithdrawalQueue(precision_) AccountableVault(asset_, permissionLevel_) ERC20(name_, symbol_) {
        if (strategy_ == address(0)) revert ZeroAddress();

        strategy = IStrategyVaultHooks(strategy_);
        transferableShares = sharesTransferable_;
        precision = precision_;
    }

    /// @inheritdoc IAccountableVault
    function deposit(uint256 assets, address receiver, address controller)
        public
        onlyAuth(receiver, controller)
        returns (uint256 shares)
    {
        _checkMinAmount(assets);
        _checkController(controller);

        uint256 price = strategy.onDeposit(address(this), assets, receiver, controller);
        shares = _convertToShares(assets, price, Math.Rounding.Floor);

        _mint(receiver, shares);
        _deposit(controller, assets);

        emit Deposit(controller, receiver, assets, shares);
    }

    /// @inheritdoc IERC4626
    function deposit(uint256 assets, address receiver) public override returns (uint256 shares) {
        shares = deposit(assets, receiver, msg.sender);
    }

    /// @inheritdoc IAccountableVault
    function mint(uint256 shares, address receiver, address controller)
        public
        onlyAuth(receiver, controller)
        returns (uint256 assets)
    {
        _checkMinAmount(shares);
        _checkController(controller);

        uint256 price = strategy.onMint(address(this), shares, receiver, controller);
        assets = _convertToAssets(shares, price, Math.Rounding.Ceil);

        _mint(receiver, shares);
        _deposit(controller, assets);

        emit Deposit(controller, receiver, assets, shares);
    }

    /// @inheritdoc IERC4626
    function mint(uint256 shares, address receiver) public override returns (uint256 assets) {
        assets = mint(shares, receiver, msg.sender);
    }

    /// @inheritdoc IAsyncRedeem
    function requestRedeem(uint256 shares, address controller, address owner)
        public
        onlyAuth(owner, controller)
        returns (uint256 requestId)
    {
        _checkOperator(owner);
        _checkController(controller);
        _checkMinAmount(shares);
        _checkShares(owner, shares);

        VaultState storage state = _vaultStates[controller];

        state.pendingRedeemRequest += shares;

        (bool canFulfill, uint256 price) = strategy.onRequestRedeem(address(this), shares, controller, owner);
        if (canFulfill) {
            requestId = INSTANT_REQUEST_ID;
            _fulfillRedeemRequest(INSTANT_REQUEST_ID, controller, shares, price);
        } else {
            requestId = _push(controller, shares);
        }

        _transfer(owner, address(this), shares);

        /// @dev RequestId is non-zero if in queue, 0 otherwise (instant fulfill)
        emit RedeemRequest(controller, owner, requestId, msg.sender, shares);
    }

    /// @inheritdoc IAsyncRedeem
    function pendingRedeemRequest(uint256, address controller) public view returns (uint256 pendingShares) {
        pendingShares = _vaultStates[controller].pendingRedeemRequest;
    }

    /// @inheritdoc IAsyncRedeem
    function claimableRedeemRequest(uint256, address controller) public view returns (uint256 claimableShares) {
        claimableShares = maxRedeem(controller);
    }

    /// @inheritdoc IERC4626
    function redeem(uint256 shares, address receiver, address controller)
        public
        onlyAuth(receiver, controller)
        returns (uint256 assets)
    {
        _checkMinAmount(shares);
        _checkController(controller);
        if (shares > maxRedeem(controller)) revert ExceedsMaxRedeem();

        VaultState storage state = _vaultStates[controller];
        uint256 redeemPrice = state.redeemPrice;
        assets = _convertToAssets(shares, redeemPrice, Math.Rounding.Floor);
        uint256 assetsUp = _convertToAssets(shares, redeemPrice, Math.Rounding.Ceil);

        strategy.onRedeem(address(this), shares, receiver, controller);

        _updateReservedLiquidity(assets);
        _updateRedeemState(state, assets, assetsUp, shares, shares);

        _burn(address(this), shares);
        _withdraw(receiver, assets);

        emit Withdraw(msg.sender, receiver, controller, assets, shares);
    }

    /// @inheritdoc IERC4626
    function withdraw(uint256 assets, address receiver, address controller)
        public
        onlyAuth(receiver, controller)
        returns (uint256 shares)
    {
        _checkMinAmount(assets);
        _checkController(controller);
        if (assets > maxWithdraw(controller)) revert ExceedsMaxRedeem();

        VaultState storage state = _vaultStates[controller];
        uint256 withdrawPrice = state.withdrawPrice;
        shares = _convertToShares(assets, withdrawPrice, Math.Rounding.Floor);
        uint256 sharesUp = _convertToShares(assets, withdrawPrice, Math.Rounding.Ceil);

        strategy.onWithdraw(address(this), assets, receiver, controller);

        _updateReservedLiquidity(assets);
        _updateRedeemState(state, assets, assets, shares, sharesUp);

        _burn(address(this), shares);
        _withdraw(receiver, assets);

        emit Withdraw(msg.sender, receiver, controller, assets, shares);
    }

    function _updateRedeemState(
        VaultState storage state,
        uint256 assets,
        uint256 assetsUp,
        uint256 shares,
        uint256 sharesUp
    ) private {
        uint256 maxWithdraw_ = state.maxWithdraw;
        uint256 redeemShares_ = state.redeemShares;

        if (assets > maxWithdraw_) revert ExceedsRedeemLimit();
        state.maxWithdraw = maxWithdraw_ > assetsUp ? maxWithdraw_ - assetsUp : 0;

        if (shares > redeemShares_) revert ExceedsRedeemLimit();
        state.redeemShares = redeemShares_ > sharesUp ? redeemShares_ - sharesUp : 0;
        if (state.maxWithdraw == 0) state.redeemShares = 0;
        if (state.redeemShares == 0) state.maxWithdraw = 0;
    }

    /// @inheritdoc IAsyncCancelRedeem
    function cancelRedeemRequest(address controller, address receiver) public onlyAuth(receiver, controller) {
        _checkController(controller);
        VaultState storage state = _vaultStates[controller];
        if (state.pendingRedeemRequest == 0) revert NoPendingRedeemRequest();

        bool canCancel = strategy.onCancelRedeemRequest(address(this), controller);
        if (!canCancel) revert CancelRedeemRequestFailed();

        uint256 requestId = _requestIds[controller];
        uint256 pendingShares = state.pendingRedeemRequest;

        state.pendingRedeemRequest = 0;

        _transfer(address(this), receiver, pendingShares);
        _reduce(controller, pendingShares);

        try IUpdateLateStatus(address(strategy)).updateLateStatus() {} catch {}

        emit CancelRedeemRequest(controller, receiver, requestId, msg.sender);
    }

    // ========================================================================== //
    //                             Queue operations                               //
    // ========================================================================== //

    /// @inheritdoc IAccountableWithdrawalQueue
    function fulfillRedeemRequest(address controller, uint256 shares) public onlyStrategy {
        _fulfillRedeemRequest(_requestIds[controller], controller, shares, sharePrice());
        _reduce(controller, shares);
    }

    /// @inheritdoc IAccountableWithdrawalQueue
    function processUpToShares(uint256 maxShares) public onlyStrategy returns (uint256 assetsUsed) {
        assetsUsed = _processUpToShares(maxShares, _liquidity(), sharePrice());
    }

    /// @inheritdoc IAccountableWithdrawalQueue
    function processUpToRequestId(uint256 maxRequestId)
        public
        onlyStrategy
        returns (uint256 processedShares, uint256 assetsUsed)
    {
        (processedShares, assetsUsed) = _processUpToRequestId(maxRequestId, _liquidity(), sharePrice());
    }

    /// @inheritdoc IAccountableWithdrawalQueue
    function previewRequiredShares(uint256 maxAssets)
        public
        view
        returns (uint256 processedShares, uint256 assetsUsed)
    {
        (processedShares, assetsUsed) = _previewRequiredShares(maxAssets, sharePrice());
    }

    /// @inheritdoc IAccountableWithdrawalQueue
    function previewMaxRequestId(uint256 maxAssets) public view returns (uint256 maxRequestId, uint256 assetsUsed) {
        (maxRequestId, assetsUsed) = _previewMaxRequestId(maxAssets, sharePrice());
    }

    /// @dev Computes free liquidity excluding accrued locked assets and reserved queue liquidity
    function _liquidity() internal view returns (uint256) {
        uint256 excludedAssets = strategy.accruedAssets(address(this)) + reservedLiquidity;
        return totalAssets() > excludedAssets ? totalAssets() - excludedAssets : 0;
    }

    /// @dev Fulfills a redeem request
    function _fulfillRedeemRequest(uint128 requestId, address controller, uint256 shares, uint256 price)
        internal
        override
    {
        VaultState storage state = _vaultStates[controller];
        uint256 pendingRedeemRequest_ = state.pendingRedeemRequest;

        if (pendingRedeemRequest_ == 0) revert NoRedeemRequest();
        if (pendingRedeemRequest_ < shares) revert InsufficientAmount();

        uint256 assets = _convertToAssets(shares, price, Math.Rounding.Floor);
        if (assets > _liquidity()) revert InsufficientLiquidity();

        reservedLiquidity += assets;
        state.maxWithdraw += assets;
        state.redeemShares += shares;
        state.pendingRedeemRequest -= shares;

        state.redeemPrice = state.maxWithdraw.mulDiv(precision, state.redeemShares);
        state.withdrawPrice = state.maxWithdraw.mulDiv(precision, state.redeemShares, Math.Rounding.Ceil);

        /// @dev RequestId is non-zero if in queue, 0 otherwise (instant fulfill)
        emit RedeemClaimable(controller, requestId, assets, shares);
    }

    /// @inheritdoc IERC4626
    function maxDeposit(address controller) public view override returns (uint256 maxAssets) {
        maxAssets = strategy.maxDeposit(address(this), controller);
    }

    /// @inheritdoc IERC4626
    function maxMint(address controller) public view override returns (uint256 maxShares) {
        maxShares = _convertToShares(maxDeposit(controller), sharePrice(), Math.Rounding.Floor);
    }

    /// @inheritdoc IERC4626
    function maxRedeem(address controller) public view override returns (uint256 maxShares) {
        VaultState storage state = _vaultStates[controller];
        maxShares = state.redeemShares;
        if (maxShares == 0) return 0;
    }

    /// @inheritdoc IERC4626
    function maxWithdraw(address controller) public view override returns (uint256 maxAssets) {
        VaultState storage state = _vaultStates[controller];
        maxAssets = state.maxWithdraw;
        if (state.redeemShares == 0) return 0;
    }

    /// @inheritdoc IERC4626
    /// @notice Preview deposit works for sync deposit vaults
    function previewDeposit(uint256 assets) public view returns (uint256 shares) {
        shares = _convertToShares(assets, sharePrice(), Math.Rounding.Floor);
    }

    /// @inheritdoc IERC4626
    /// @notice Preview mint works for sync deposit vaults
    function previewMint(uint256 shares) public view returns (uint256 assets) {
        assets = _convertToAssets(shares, sharePrice(), Math.Rounding.Ceil);
    }

    /// @inheritdoc IERC4626
    /// @notice Preview functions are not supported for async vaults
    function previewRedeem(uint256) public pure returns (uint256) {
        revert();
    }

    /// @inheritdoc IERC4626
    /// @notice Preview functions are not supported for async vaults
    function previewWithdraw(uint256) public pure returns (uint256) {
        revert();
    }

    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
        return interfaceId == type(IAccountableAsyncRedeemVault).interfaceId || interfaceId == type(IERC165).interfaceId
            || interfaceId == type(IAsyncRedeem).interfaceId || interfaceId == type(IAsyncCancelRedeem).interfaceId
            || interfaceId == type(IAccountableVault).interfaceId || interfaceId == type(IERC4626).interfaceId
            || interfaceId == type(IControllerOperator).interfaceId || interfaceId == type(IERC20).interfaceId
            || interfaceId == type(IAccountableWithdrawalQueue).interfaceId
            || interfaceId == type(IERC20Metadata).interfaceId;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import {PermissionLevel} from "../interfaces/IAccess.sol";
import {IAsyncVaultFactory} from "../interfaces/IAsyncVaultFactory.sol";

import {FailedDeployment} from "../constants/Errors.sol";
import {AccountableAsyncRedeemVault} from "../vault/AccountableAsyncRedeemVault.sol";

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

/// @title AsyncVaultFactory
/// @notice Factory for creating async vaults
contract AsyncVaultFactory is IAsyncVaultFactory {
    string internal constant ZERO_VAULT_ADDRESS = "zero vault address";

    /// @inheritdoc IAsyncVaultFactory
    function createAsyncRedeemVault(
        IERC20 asset_,
        address proxy_,
        bool sharesTransferable_,
        PermissionLevel permissionLevel_,
        string memory name_,
        string memory symbol_,
        uint256 precision_
    ) external returns (address vault) {
        vault = address(
            new AccountableAsyncRedeemVault(
                asset_, proxy_, sharesTransferable_, permissionLevel_, name_, symbol_, precision_
            )
        );
        if (vault == address(0)) revert FailedDeployment(ZERO_VAULT_ADDRESS);

        emit AsyncRedeemVaultCreated(vault);
    }
}

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

/// @notice Struct containing transaction authentication data (EIP-712 typed)
/// @dev Chain ID is part of the EIP-712 domain separator, not this struct
struct TxAuthData {
    /// @notice The calldata of the function being called (selector + encoded args)
    bytes functionCallData;
    /// @notice The address of the contract where the transaction is being executed
    address contractAddress;
    /// @notice The address of the account executing/controlling the transaction
    address account;
    /// @notice The per-account nonce (prevents replay)
    uint256 nonce;
    /// @notice The block number after which the transaction is considered expired
    uint256 blockExpiration;
}

/// @notice Struct containing verification data for verifyMany function
struct VerifyManyData {
    /// @notice The function call data extracted from msgData
    bytes argsWithSelector;
    /// @notice The block expiration timestamp
    uint256 blockExpiration;
    /// @notice The start position of signatures in msgData
    uint256 signaturesStart;
    /// @notice The number of signatures to verify
    uint256 numSignatures;
}

/// @notice Enum representing different permission levels
enum PermissionLevel {
    /// @notice No permission
    None,
    /// @notice KYC based permission
    KYC,
    /// @notice Whitelist based permission
    Whitelist
}

/// @title Whitelistable Interface
/// @notice Interface for managing whitelisted accounts
interface IWhitelistable {
    /// @notice Emitted when allowed status is set for an account
    /// @param account The address of the account
    /// @param allowed The allowed status
    event AllowedSet(address indexed account, bool allowed);

    /// @notice Set allowed accounts
    /// @param accounts Array of account addresses
    /// @param allowed Array of allowed status corresponding to acounts
    function setAllowed(address[] calldata accounts, bool[] calldata allowed) external;

    /// @notice Check if an account is allowed
    /// @param account The address to check allowed status for
    /// @return bool True if the account is allowed, false otherwise
    function allowed(address account) external view returns (bool);

    /// @notice Check if multiple accounts are allowed
    /// @param accounts Array of account addresses
    /// @return bool True if the accounts are allowed, false otherwise
    function allowedMany(address[] calldata accounts) external view returns (bool);
}

/// @title Authorizable Interface
/// @notice Interface for managing authorizable accounts
interface IAuthorizable {
    /// @notice Emitted when the signer address is changed
    /// @param oldSigner The old signer address
    /// @param newSigner The new signer address
    event SignerChanged(address indexed oldSigner, address indexed newSigner);

    /// @notice Emitted when a transaction authentication data is verified
    /// @param chainID The chain ID where the transaction is intended to be processed
    /// @param nonce The nonce of the user being verified to prevent replay attacks
    /// @param blockExpiration The block number after which the transaction is considered expired
    /// @param contractAddress The address of the contract where the transaction is being executed
    /// @param userAddress The address of the user executing the transaction
    /// @param functionCallData The calldata of the function being called
    event TxAuthDataVerified(
        uint256 chainID,
        uint256 nonce,
        uint256 blockExpiration,
        address indexed contractAddress,
        address indexed userAddress,
        bytes functionCallData
    );

    /// @notice Set the signer address
    /// @param signer The address of the signer
    function setSigner(address signer) external;

    /// @notice Get the signer address
    /// @return The signer address
    function signer() external view returns (address);

    /// @notice Get the nonce for a given user
    /// @param user The address of the user
    /// @return The nonce of the user
    function nonces(address user) external view returns (uint256);

    /// @notice Get the message hash for a given transaction authentication data
    /// @param txAuthData The transaction authentication data
    /// @return The message hash
    function getMessageHash(TxAuthData calldata txAuthData) external view returns (bytes32);
}

/// @title Access Interface
/// @notice Interface for managing access to a contract
interface IAccess is IAuthorizable, IWhitelistable {
    /// @notice Get the permission level for the loan
    /// @return The permission level enum value
    function permissionLevel() external view returns (PermissionLevel);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import {PermissionLevel} from "./IAccess.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title IAsyncVaultFactory
/// @notice Interface for creating async vaults
interface IAsyncVaultFactory {
    /// @notice Emitted when a new async redeem vault is created
    /// @param vault The address of the created vault
    event AsyncRedeemVaultCreated(address indexed vault);

    /// @notice Creates a new async redeem vault
    /// @param asset The asset token address for the vault
    /// @param proxy The proxy address for the vault
    /// @param sharesTransferable Whether shares are transferable
    /// @param permissionLevel The permission level for the vault
    /// @param name The name of the vault
    /// @param symbol The symbol of the vault
    /// @param precision The precision for the vault
    function createAsyncRedeemVault(
        IERC20 asset,
        address proxy,
        bool sharesTransferable,
        PermissionLevel permissionLevel,
        string memory name,
        string memory symbol,
        uint256 precision
    ) external returns (address);
}

File 5 of 34 : Errors.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

// ========================================================================== //
//                             Authorization Errors                           //
// ========================================================================== //

/// @notice Error thrown when unauthorized
error Unauthorized();

/// @notice Thrown when a signature verification fails due to invalid signer
error InvalidSigner();

/// @notice Thrown when an operation is attempted with invalid operator permissions
error InvalidOperator();

/// @notice Error thrown when invalid pending borrower is provided
error InvalidPendingBorrower();

/// @notice Thrown when KYC status is not verified
error KYCNotVerified();

/// @notice Thrown when a block expiration is invalid (e.g. in the past)
error InvalidBlockExpiration();

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

/// @notice Thrown when a message data length is invalid
error InvalidMsgDataLength();

/// @notice Thrown when an accounts array is empty
error EmptyAccountsArray();

// ========================================================================== //
//                             Vault operations errors                        //
// ========================================================================== //

/// @notice Error thrown when trying to cancel a deposit request failed
error CancelDepositRequestFailed();

/// @notice Error thrown when trying to cancel a redeem request failed
error CancelRedeemRequestFailed();

/// @notice Error thrown when deposit amount exceeds max deposit
error ExceedsMaxDeposit();

/// @notice Error thrown when assets exceed redemption limits
error ExceedsRedeemLimit();

/// @notice Error thrown when redeem amount exceeds max redeem
error ExceedsMaxRedeem();

/// @notice Error thrown when deposit/withdraw amount is insufficient
error InsufficientAmount();

/// @notice Error thrown when redeem share amount is insufficient
error InsufficientShares();

/// @notice Error thrown when no redeem request exists
error NoRedeemRequest();

/// @notice Error thrown when no pending redeem request exists
error NoPendingRedeemRequest();

/// @notice Error thrown when no cancel redeem request exists
error NoCancelRedeemRequest();

/// @notice Thrown when attempting to transfer shares that are not transferable
error SharesNotTransferable();

/// @notice Error thrown when no queue request exists
error NoQueueRequest();

/// @notice Error thrown when redeem amount exceeds liquidity
error InsufficientLiquidity();

/// @notice Error thrown when no queue requests exist
error NoQueueRequests();

/// @notice Error thrown when asset is not whitelisted
error AssetNotWhitelisted();

// ========================================================================== //
//                             Loan operations errors                         //
// ========================================================================== //

/// @notice Error thrown when loan terms are already set
error LoanTermsAlreadySet();

/// @notice Error thrown when loan terms are not set
error LoanTermsNotSet();

/// @notice Error thrown when loan is ongoing
error LoanOngoing();

/// @notice Error thrown when loan is not ongoing
error LoanNotOngoing();

/// @notice Error thrown when loan is not in default
error LoanNotInDefault();

/// @notice Error thrown when loan terms are not met
error LoanTermsNotMet();

/// @notice Error thrown when loan has not matured
error LoanNotMatured();

/// @notice Error thrown when repayment terms are not met
error LoanCannotBeRepaid();

/// @notice Error thrown when interest is already claimed
error InterestAlreadyClaimed();

/// @notice Error thrown when there are outstanding interest payments
error OutstandingInterestPayments();

/// @notice Thrown when an operation is attempted with invalid controller permissions
error InvalidController();

/// @notice Thrown when an operation is attempted with invalid vault manager permissions
error InvalidVaultManager();

/// @notice Thrown when an operation is attempted with invalid interval duration pair
error InvalidIntervalDurationPair();

/// @notice Thrown when an operation is attempted with invalid interval length
error InvalidIntervalLength();

/// @notice Thrown when an operation is attempted with invalid withdrawal period
error InvalidWithdrawalPeriod();

/// @notice Error thrown when capacity is set to a lower value than acceptable
error CapacityTooLow();

/// @notice Error thrown when accept grace period is longer than acceptable for the loan
error AcceptGracePeriodTooLong();

/// @notice Error thrown when a threshold exceeds the max bound
error ThresholdTooHigh();

/// @notice Error thrown when attempting to pay when no payment is due
error NoPaymentDue();

// ========================================================================== //
//                            General errors                                  //
// ========================================================================== //

/// @notice Thrown when an index is out of bounds
error OutOfBounds();

/// @notice Thrown when an invalid range is provided
error InvalidRange();

/// @notice Thrown when an operation is requested with zero amount
error ZeroAmount();

/// @notice Thrown when an input address is address(0)
error ZeroAddress();

/// @notice Thrown when deployment of a loan proxy contract fails
/// @param reason The reason for the failed deployment
error FailedDeployment(string reason);

/// @notice Thrown when a deposit is not allowed
error DepositNotAllowed();

/// @notice Thrown when a redeem is not allowed
error RedeemNotAllowed();

/// @notice Thrown when input arrays lengths don't match
error ArrayLengthMismatch();

/// @notice Error thrown when an operation is not required/allowed for the current permission level
error PermissionLevelMismatch();

/// @notice Error thrown when an operation is not supported by the strategy
error NotSupportedByStrategy();

// ========================================================================== //
//                            Fee Manager errors                              //
// ========================================================================== //

/// @notice Error thrown when manager split is invalid
error InvalidManagerSplit();

/// @notice Error thrown when performance fee is invalid
error InvalidPerformanceFee();

/// @notice Error thrown when establishment fee is invalid
error InvalidEstablishmentFee();

// ========================================================================== //
//                            Rewards errors                                  //
// ========================================================================== //

/// @notice Error thrown when a root is already set
error RootAlreadySet();

/// @notice Error thrown when no root is set
error RootNotSet();

/// @notice Error thrown when a root is already pending
error RootAlreadyPending();

/// @notice Error thrown when no pending root is set
error NoPendingRoot();

/// @notice Error thrown when timelock is not expired
error TimelockNotExpired();

/// @notice Error thrown when a proof is invalid
error InvalidProof();

/// @notice Error thrown when the amount to claim is not enough
error NotEnoughClaimableAmount();

/// @notice Error thrown when an invalid rewards type is provided
error InvalidRewardsType();

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

pragma solidity ^0.8.20;

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

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

    /**
     * @dev Returns the 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);
}

File 7 of 34 : IAccountableStrategy.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

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

/// @notice Struct containing loan terms and state
struct Loan {
    /// @notice Minimum deposit amount allowed
    uint256 minDeposit;
    /// @notice Minimum redeem amount allowed
    uint256 minRedeem;
    /// @notice Maximum loan amount that can be drawn
    ///         The full threshold can be met after deposit period if the loan is not locked
    uint256 maxCapacity;
    /// @notice Minimum loan amount for the loan to be accepted
    ///         The threshold needs to be met during deposit period or loan can be rejected
    uint256 minCapacity;
    /// @notice Liquidity ratio that has to be maintained in the vault at all times
    ///         Expressed in basis points and cannot exceed 1e6, defaults to 0
    uint256 reserveThreshold;
    /// @notice Principal amount still to be repaid
    uint256 outstandingPrincipal;
    /// @notice Interest amount still to be paid
    uint256 outstandingInterest;
    /// @notice all funds accepted and available for borrowing
    uint256 drawableFunds;
    /// @notice Annual interest rate in basis points
    uint256 interestRate;
    /// @notice Late interest penalty in basis points
    uint256 lateInterestPenalty;
    /// @notice Total claimable interest available for users
    uint256 claimableInterest;
    /// @notice Time interval between interest payments
    uint256 interestInterval;
    /// @notice Time of loan start
    uint256 startTime;
    /// @notice Time of terms set
    uint256 termsSetTime;
    /// @notice Time of terms update
    uint256 termsUpdateTime;
    /// @notice Loan duration in seconds
    uint256 duration;
    /// @notice Deposit period in seconds
    uint256 depositPeriod;
    /// @notice Grace period after deposit period for borrower to accept loan
    uint256 acceptGracePeriod;
    /// @notice Withdrawal window period in seconds
    ///         If set it allows redemption requests
    uint256 withdrawalPeriod;
    /// @notice Late interest payment grace period in seconds
    ///         This is the time period before a loan is considered delinquent
    uint256 lateInterestGracePeriod;
}

/// @notice Struct containing loan terms
struct LoanTerms {
    /// @notice Minimum deposit amount allowed
    uint256 minDeposit;
    /// @notice Minimum redeem amount allowed
    uint256 minRedeem;
    /// @notice Maximum loan capacity
    uint256 maxCapacity;
    /// @notice Minimum loan capacity
    uint256 minCapacity;
    /// @notice Annual interest rate in basis points
    uint256 interestRate;
    /// @notice Late interest penalty in basis points
    uint256 lateInterestPenalty;
    /// @notice Late interest grace period in seconds
    uint256 lateInterestGracePeriod;
    /// @notice Time interval between interest payments
    uint256 interestInterval;
    /// @notice Duration of the loan in seconds
    uint256 duration;
    /// @notice Deposit period in seconds
    uint256 depositPeriod;
    /// @notice Grace period after deposit period for borrower to accept loan
    uint256 acceptGracePeriod;
    /// @notice Withdrawal window period in seconds
    uint256 withdrawalPeriod;
}

/// @notice Struct containing DVN proof data
struct DVNProof {
    /// @notice Merkle root of the DVN merkle tree
    bytes32 root;
    /// @notice Signature of the DVN payload
    bytes32 signature;
    /// @notice Message hash that was signed
    bytes32 messageHash;
}

/// @notice Enum representing different loan states
enum LoanState {
    /// @notice Loan terms are not set
    Initialized,
    /// @notice Loan terms are set
    TermsSet,
    /// @notice Loan is accepted by borrower and locked
    OngoingLocked,
    /// @notice Loan is accepted by borrower and dynamic deposits are allowed
    OngoingDynamic,
    /// @notice Loan is rejected
    Rejected,
    /// @notice Loan is repaid
    Repaid,
    /// @notice Loan is in default
    InDefault,
    /// @notice Loan is in default claims
    InDefaultClaims
}

/// @notice Interface for managing strategy vault hooks
interface IStrategyVaultHooks {
    /// @notice Hook for handling deposit requests
    /// @param share The address of the share token
    /// @param assets The amount of assets requested
    /// @param controller The address of the controller
    /// @param owner The address of the owner of the assets
    /// @return canFulfill Whether the deposit can be fulfilled instantly
    /// @return price The price of the share
    function onRequestDeposit(address share, uint256 assets, address controller, address owner)
        external
        returns (bool canFulfill, uint256 price);

    /// @notice Hook for handling redeem requests
    /// @param share The address of the share token
    /// @param shares The amount of shares requested
    /// @param controller The address of the controller
    /// @param owner The address of the owner of the shares
    /// @return canFulfill Whether the redeem can be fulfilled instantly
    /// @return price The price of the share
    function onRequestRedeem(address share, uint256 shares, address controller, address owner)
        external
        returns (bool canFulfill, uint256 price);

    /// @notice Hook for handling cancel deposit requests
    /// @param share The address of the share token
    /// @param controller The address of the controller
    /// @return canCancel Whether the deposit can be cancelled instantly
    function onCancelDepositRequest(address share, address controller) external returns (bool canCancel);

    /// @notice Hook for handling cancel redeem requests
    /// @param share The address of the share token
    /// @param controller The address of the controller
    /// @return canCancel Whether the redeem can be cancelled instantly
    function onCancelRedeemRequest(address share, address controller) external returns (bool canCancel);

    /// @notice Hook for handling deposit requests
    /// @param share The address of the share token
    /// @param assets The amount of assets to deposit
    /// @param receiver The address of the receiver
    /// @param controller The address of the controller
    function onDeposit(address share, uint256 assets, address receiver, address controller)
        external
        returns (uint256 price);

    /// @notice Hook for handling mint requests
    /// @param share The address of the share token
    /// @param shares The amount of shares to mint
    /// @param receiver The address of the receiver
    /// @param controller The address of the controller
    function onMint(address share, uint256 shares, address receiver, address controller)
        external
        returns (uint256 price);

    /// @notice Hook for handling redeem requests
    /// @param share The address of the share token
    /// @param shares The amount of shares to redeem
    /// @param receiver The address of the receiver
    function onRedeem(address share, uint256 shares, address receiver, address controller) external;

    /// @notice Hook for handling withdraw requests
    /// @param share The address of the share token
    /// @param assets The amount of assets to withdraw
    /// @param receiver The address of the receiver
    /// @param controller The address of the controller
    function onWithdraw(address share, uint256 assets, address receiver, address controller) external;

    /// @notice Hook for handling vault transfers
    /// @param from The address of the sender
    /// @param to The address of the receiver
    /// @param amount The amount of shares to transfer
    function onTransfer(address share, address from, address to, uint256 amount) external;

    /// @notice Operator authorization
    /// @param operator The address of the operator
    /// @dev Reverts for unauthorized operator. Operator should not be an input value of another call
    function authOperator(address operator) external;

    /// @notice Centralized share price
    /// @param share The address of the share token
    function sharePrice(address share) external view returns (uint256 price);

    /// @notice Centralized accrued assets
    /// @param share The address of the share token
    /// @return assets The accrued assets to be distributed
    function accruedAssets(address share) external view returns (uint256 assets);

    /// @notice Centralized max deposit capacity
    /// @param share The address of the share token
    /// @param controller The address of the controller
    /// @return maxAssets The maximum amount of assets that can be deposited
    function maxDeposit(address share, address controller) external view returns (uint256 maxAssets);

    /// @notice Centralized max redeem capacity
    /// @param share The address of the share token
    /// @param controller The address of the controller
    /// @return maxShares The maximum amount of shares that can be redeemed
    function maxRedeem(address share, address controller) external view returns (uint256 maxShares);
}

/// @notice Interface for managing fee manager hooks
interface IFeeManagerHooks {
    /// @notice Hook for handling fee structure changes
    function onFeeStructureChange() external;
}

interface IUpdateLateStatus {
    /// @notice Updates the late status of the loan
    function updateLateStatus() external;
}

/// @notice Interface for managing base strategy components
interface IAccountableStrategy is IStrategyVaultHooks, IFeeManagerHooks, IERC165 {
    /// @notice Event emitted when the borrower is changed
    event BorrowerChanged(address indexed oldBorrower, address indexed newBorrower);

    /// @notice Event emitted when the pending borrower is set
    event PendingBorrowerSet(address indexed pendingBorrower);

    /// @notice Event emitted when the safety module is set
    event SafetyModuleSet(address indexed safetyModule);

    /// @notice Event emitted when the investment manager is set
    event InvestmentManagerSet(address indexed investmentManager);

    /// @notice Event emitted when the proof signer is set
    event ProofSignerSet(address indexed proofSigner);

    /// @notice Event emitted when the DVN proof is published
    event DVNProofPublished(bytes32 indexed root, bytes32 indexed signature, bytes32 indexed messageHash);

    /// @notice Event emitted when the default is rejected
    event LoanDefaultRejected();

    /// @notice Event emitted when the default is accepted
    event LoanDefaulted(uint256 principal, uint256 collateral);

    /// @notice Event emitted when the penalties enabled flag is set
    event PenaltiesEnabledSet(bool enabled);

    /// @notice Event emitted when the security admin enabled flag is set
    event SecurityAdminEnabledSet(bool enabled);

    /// @notice Event emitted when the operations admin enabled flag is set
    event OperationsAdminEnabledSet(bool enabled);

    /// @notice Event emitted when the reserve threshold is set
    event ReserveThresholdSet(uint256 threshold);

    /// @notice Event emitted when the rewards distributor is set
    event RewardsDistributorSet(address indexed rewardsDistributor);

    /// @notice Event emitted when the price oracle is set
    event PriceOracleSet(address indexed priceOracle);

    /// @notice Set a new pending borrower
    /// @param newBorrower Address of the new pending borrower
    function setPendingBorrower(address newBorrower) external;

    /// @notice Accept the borrower role as pending borrower
    function acceptBorrowerRole() external;

    /// @notice Set a new safety module contract
    /// @dev It can act as a factory in a deploy-and-set manner
    /// @param safetyModule Address of the new safety module
    function setSafetyModule(address safetyModule) external;

    /// @notice Set a new rewards distributor contract
    /// @dev It can act as a factory in a deploy-and-set manner
    /// @param rewards Address of the new rewards distributor
    function setRewardsDistributor(address rewards) external;

    /// @notice Set a new price oracle contract
    /// @param priceOracle Address of the new price oracle
    function setPriceOracle(address priceOracle) external;

    /// @notice Set a new investment manager contract
    /// @param investmentManager Address of the new investment manager
    function setInvestmentManager(address investmentManager) external;

    /// @notice Set the auth signer address
    /// @dev This is function relays data to the vault's access module
    ///      It sets the kyc verification signer when permission level is `KYC`
    /// @param authSigner Address of the new auth signer
    function setAuthSigner(address authSigner) external;

    /// @notice Set the lenders addresses and permissions
    /// @dev This is function relays data to the vault's access module
    ///      It whitelists lenders when permission level is `Whitelist`
    /// @param lenders Addresses of the lenders to be whitelisted
    /// @param allowed Whether the lenders are allowed to perform actions
    function setLenders(address[] calldata lenders, bool[] calldata allowed) external;

    /// @notice Set the proof signer address
    /// @param proofSigner Address of the new proof signer
    function setProofSigner(address proofSigner) external;

    /// @notice Publish a new DVN proof
    /// @param proof Latest DVN proof data
    function publishDVNProof(DVNProof memory proof) external;

    /// @notice Accept the default of the loan
    function acceptDefault() external;

    /// @notice Reject the default of the loan
    function rejectDefault() external;

    /// @notice Set the penalties enabled flag
    /// @param enabled Whether penalties are enabled
    function setPenaltiesEnabled(bool enabled) external;

    /// @notice Set the security admin enabled flag
    /// @param enabled Whether security admin is enabled
    function setSecurityAdminEnabled(bool enabled) external;

    /// @notice Set the operations admin enabled flag
    /// @param enabled Whether operations admin is enabled
    function setOperationsAdminEnabled(bool enabled) external;

    /// @notice Set the shares transferable flag
    /// @param transferable Whether shares are transferable
    function setSharesTransferable(bool transferable) external;

    /// @notice Set the min reserve threshold required in the vault
    /// @param threshold The threshold expressed in basis points
    function setReserveThreshold(uint256 threshold) external;

    /// @notice Get the global registry contract address
    /// @return The global registry address
    function globals() external view returns (address);

    /// @notice Get the current borrower address
    /// @return The borrower address
    function borrower() external view returns (address);

    /// @notice Get the pending borrower address
    /// @return The pending borrower address
    function pendingBorrower() external view returns (address);

    /// @notice Get the investment manager contract address
    /// @return The investment manager address
    function investmentManager() external view returns (address);

    /// @notice Get the fee manager contract address
    /// @return The fee manager address
    function feeManager() external view returns (address);

    /// @notice Get the safety module contract address
    /// @return The safety module address
    function safetyModule() external view returns (address);

    /// @notice Get the rewards distributor contract address
    /// @return The rewards distributor address
    function rewards() external view returns (address);

    /// @notice Get the price oracle contract address
    /// @return The price oracle address
    function priceOracle() external view returns (address);

    /// @notice Get the proof signer address
    /// @return The proof signer address
    /// @dev Returns the pub key of the node signing proofs
    function proofSigner() external view returns (address);

    /// @notice Get the vault contract address
    /// @return The vault address
    function vault() external view returns (address);

    /// @notice Get the current loan state
    /// @return The loan struct
    function loan() external view returns (Loan memory);

    /// @notice Get the DVN proof
    /// @return The latest DVN proof data
    function dvnProof() external view returns (DVNProof memory);

    /// @notice Get the loan state
    /// @return The loan state enum value
    function loanState() external view returns (LoanState);

    /// @notice Get the penalties enabled flag
    /// @return The penalties enabled flag
    function penaltiesEnabled() external view returns (bool);

    /// @notice Get the security admin enabled flag
    /// @return The security admin enabled flag
    function securityAdminEnabled() external view returns (bool);

    /// @notice Get the operations admin enabled flag
    /// @return The operations admin enabled flag
    function operationsAdminEnabled() external view returns (bool);

    /// @notice Get the precision for the strategy
    /// @return The precision
    function PRECISION() external view returns (uint256);

    /// @notice Get the basis points for the strategy
    /// @return The basis points
    function BASIS_POINTS() external view returns (uint256);

    /// @notice Get the version of the strategy
    /// @dev Increment when upgrading the strategy
    function version() external view returns (uint256);
}

/// @notice Interface for managing loan lifecycle and payments
interface IAccountableLoan is IAccountableStrategy {
    /// @notice Event emitted when the loan is initialized
    /// @param minCapacity The minimum loan capacity
    /// @param maxCapacity The maximum loan capacity
    /// @param interestRate The interest rate of the loan
    /// @param lateInterestPenalty The late interest penalty
    /// @param interestInterval The interest interval
    /// @param duration The duration of the loan
    event LoanTermsSet(
        uint256 minCapacity,
        uint256 maxCapacity,
        uint256 interestRate,
        uint256 lateInterestPenalty,
        uint256 interestInterval,
        uint256 duration
    );

    /// @notice Event emitted when the default is initiated
    event LoanDefaultInitiated();

    /// @notice Event emitted when a default is covered by safety collateral
    /// @param safetyModule The address of the safety module
    /// @param provider The address of the provider
    /// @param collateral The amount of collateral covered
    event DefaultCovered(address indexed safetyModule, address indexed provider, uint256 collateral);

    /// @notice Event emitted when funds are borrowed
    /// @param borrower The address of the borrower
    /// @param assets The amount of funds borrowed
    event Borrowed(address indexed borrower, uint256 assets);

    /// @notice Event emitted when the loan is repaid
    /// @param assets The amount of funds repaid
    event LoanRepaid(uint256 assets);

    /// @notice Event emitted when the loan is partially repaid
    /// @param amount The amount of funds repaid to close loan
    /// @param remainingInterest The remaining interest amount
    event LoanPrepaid(uint256 amount, uint256 remainingInterest);

    /// @notice Event emitted when interest is paid
    /// @param borrower The address of the borrower
    /// @param totalAmount The total amount paid
    /// @param interestPenalty The interest penalty amount paid
    /// @param performanceFee The performance fee amount paid
    /// @param establishmentFee The establishment fee amount paid
    /// @param paymentInterval The interval of the payment
    event InterestPaid(
        address indexed borrower,
        uint256 totalAmount,
        uint256 interestPenalty,
        uint256 performanceFee,
        uint256 establishmentFee,
        uint256 paymentInterval
    );

    /// @notice Initialize a new loan with the given terms
    /// @param terms The loan terms to initialize with
    function setTerms(LoanTerms memory terms) external;

    /// @notice Update an existing loan with new terms
    /// @param terms The new loan terms to update to
    function updateTerms(LoanTerms memory terms) external;

    /// @notice Initialize loan default state
    function defaultLoan() external;

    /// @notice Cover the default of the loan
    /// @param assets The amount of assets to cover
    function coverDefault(uint256 assets) external;

    /// @notice Borrow funds from the loan
    /// @param assets The amount of funds to borrow
    function borrow(uint256 assets) external;

    /// @notice Repay the loan amount
    /// @param assets The amount of funds to repay
    function repay(uint256 assets) external;

    /// @notice Make a partial loan repayment
    function prepay() external;

    /// @notice Make an interest payment
    /// @param assets The interest amount to pay
    function pay(uint256 assets) external;

    /// @notice Get the period of time the loan has been delinquent
    /// @return The period of time the loan has been delinquent
    function timeDelinquent() external view returns (uint256);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @notice Struct tracking vault state for a controller
struct VaultState {
    /// @notice Maximum shares that can be minted
    uint256 maxMint;
    /// @notice Maximum assets that can be withdrawn
    uint256 maxWithdraw;
    /// @notice Total accumulated assets unclaimed
    uint256 depositAssets;
    /// @notice Total accumulated shares unclaimed
    uint256 redeemShares;
    /// @notice Share price floored for deposit
    uint256 depositPrice;
    /// @notice Share price ceiled for mint
    uint256 mintPrice;
    /// @notice Share price floored for redeem
    uint256 redeemPrice;
    /// @notice Share price ceiled for withdraw
    uint256 withdrawPrice;
    /// @notice Pending deposit amount
    uint256 pendingDepositRequest;
    /// @notice Pending redeem amount
    uint256 pendingRedeemRequest;
}

interface IControllerOperator {
    /// @notice Emitted when an operator's approval status is updated
    /// @param controller The controller address that approved/revoked
    /// @param operator The operator address being approved/revoked
    /// @param approved The new approval status
    event OperatorSet(address indexed controller, address indexed operator, bool approved);

    /// @notice Sets or revokes operator approval for a caller
    /// @param operator The address to set approval for
    /// @param approved True to approve, false to revoke
    /// @return bool True if successful
    function setOperator(address operator, bool approved) external returns (bool);

    /// @notice Checks if an address is an approved operator for a controller
    /// @param controller The controller address to check
    /// @param operator The operator address to check
    /// @return status True if operator is approved for controller
    function isOperator(address controller, address operator) external view returns (bool status);
}

interface IAsyncDeposit {
    /// @notice Emitted when a deposit request is created
    /// @param controller The controller address for the deposit
    /// @param owner The owner address that will receive shares
    /// @param requestId The unique ID for tracking the request
    /// @param sender The address that initiated the request
    /// @param assets The amount of assets being deposited
    event DepositRequest(
        address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    );

    /// @notice Requests a deposit of assets
    /// @param assets Amount of assets to deposit
    /// @param controller Address controlling the deposit
    /// @param owner Address that will own the shares
    /// @return uint256 Request ID for tracking the deposit
    function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256);

    /// @notice Gets pending deposit amount for a request
    /// @param requestId ID of the deposit request
    /// @param controller Controller address for the request
    /// @return uint256 Amount of assets pending deposit
    function pendingDepositRequest(uint256 requestId, address controller) external view returns (uint256);

    /// @notice Gets claimable deposit amount for a request
    /// @param requestId ID of the deposit request
    /// @param controller Controller address for the request
    /// @return uint256 Amount of assets claimable
    function claimableDepositRequest(uint256 requestId, address controller) external view returns (uint256);
}

interface IAsyncRedeem {
    /// @notice Emitted when a redeem request is created
    /// @param controller The controller address for the redemption
    /// @param owner The owner of the shares being redeemed
    /// @param requestId The unique ID for tracking the request
    /// @param sender The address that initiated the request
    /// @param assets The amount of assets being redeemed
    event RedeemRequest(
        address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    );

    /// @notice Requests redemption of shares
    /// @param shares Amount of shares to redeem
    /// @param controller Controller address for redemption
    /// @param owner Owner of the shares
    /// @return uint256 Request ID for tracking redemption
    function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256);

    /// @notice Gets pending redeem amount for a request
    /// @param requestId ID of the redeem request
    /// @param controller Controller address for the request
    /// @return uint256 Amount of shares pending redemption
    function pendingRedeemRequest(uint256 requestId, address controller) external view returns (uint256);

    /// @notice Gets claimable redeem amount for a request
    /// @param requestId ID of the redeem request
    /// @param controller Controller address for the request
    /// @return uint256 Amount of shares claimable
    function claimableRedeemRequest(uint256 requestId, address controller) external view returns (uint256);
}

interface IAsyncCancelDeposit {
    /// @notice Emitted when a deposit request is cancelled
    /// @param controller The controller address for the deposit
    /// @param receiver The address that received the cancelled deposit
    /// @param requestId The ID of the cancelled request
    /// @param sender The address that initiated the cancellation
    event CancelDepositRequest(address indexed controller, address indexed receiver, uint256 requestId, address sender);

    /// @notice Cancels a pending deposit request
    /// @param controller Controller address for the request
    /// @param receiver The address that received the cancelled deposit
    function cancelDepositRequest(address controller, address receiver) external;
}

interface IAsyncCancelRedeem {
    /// @notice Emitted when a redeem request is cancelled
    /// @param controller The controller address for the redemption
    /// @param receiver The address that received the cancelled redeem
    /// @param requestId The ID of the cancelled request
    /// @param sender The address that initiated the cancellation
    event CancelRedeemRequest(address indexed controller, address indexed receiver, uint256 requestId, address sender);

    /// @notice Cancels a pending redeem request
    /// @param controller Controller address for the request
    /// @param receiver The address that received the cancelled redeem
    function cancelRedeemRequest(address controller, address receiver) external;
}

interface IAccountableVault is IERC20, IERC20Metadata, IERC4626, IControllerOperator {
    /// @notice Emitted when assets are locked in the vault
    /// @param caller The address that locked the assets
    /// @param assets The amount of assets locked
    event LockAssets(address indexed caller, uint256 assets);

    /// @notice Emitted when locked assets are released
    /// @param caller The address that released the assets
    /// @param assets The amount of assets released
    event ReleaseAssets(address indexed caller, uint256 assets);

    /// @notice Emitted when shares transferability is changed
    /// @param oldStatus The old transferability status
    /// @param newStatus The new transferability status
    event SharesTransferableSet(bool oldStatus, bool newStatus);

    /// @notice Deposits assets and mints shares to receiver
    /// @dev Added for ERC7540 compatibility
    /// @param assets Amount of assets to deposit
    /// @param receiver Address receiving the shares
    /// @param controller Controller address for the deposit
    /// @return uint256 Amount of shares minted
    function deposit(uint256 assets, address receiver, address controller) external returns (uint256);

    /// @notice Mints exact amount of shares by depositing assets
    /// @dev Added for ERC7540 compatibility
    /// @param shares Amount of shares to mint
    /// @param receiver Address receiving the shares
    /// @param controller Controller address for the mint
    /// @return uint256 Amount of assets deposited
    function mint(uint256 shares, address receiver, address controller) external returns (uint256);

    /// @notice Locks assets in the vault
    /// @param assets Amount of assets to lock
    function lockAssets(uint256 assets, address sender) external;

    /// @notice Releases previously locked assets
    /// @param assets Amount of assets to release
    function releaseAssets(uint256 assets, address receiver) external;

    /// @notice Issues shares to a receiver
    /// @param shares Amount of shares to issue
    /// @param receiver Address to receive the shares
    function mintShares(uint256 shares, address receiver) external;

    /// @notice Repossesses shares from a sender
    /// @param shares Amount of shares to repossess
    /// @param sender Address to repossess the shares from
    function burnShares(uint256 shares, address sender) external;

    /// @notice Forces a transfer of shares from one address to another
    /// @param from The address to transfer shares from
    /// @param to The address to transfer shares to
    /// @param shares The amount of shares to transfer
    function forceTransferShares(address from, address to, uint256 shares) external;

    /// @notice Sets the transferability of shares
    /// @param transferable True to set shares transferable, false to set them non-transferable
    function setSharesTransferable(bool transferable) external;

    /// @notice Checks if shares can be transferred
    /// @return bool True if shares are transferable
    function transferableShares() external view returns (bool);

    /// @notice Current asset share ratio
    /// @return uint256 Asset share ratio
    function assetShareRatio() external view returns (uint256);

    /// @notice Gets the current share price
    /// @return uint256 Price per share in assets
    function sharePrice() external view returns (uint256);

    /// @notice The share token address
    /// @return address Share token address
    function share() external view returns (address);

    /// @notice Gets the state of the vault
    /// @param controller The controller address for the vault
    /// @return state The state of the vault given the controller
    function getState(address controller) external view returns (VaultState memory state);
}

interface IAccountableAsyncRedeemVault is IAccountableVault, IAsyncRedeem, IAsyncCancelRedeem, IERC165 {
    /// @notice Emitted when a redeem becomes claimable
    /// @param controller The controller address for the redemption
    /// @param requestId The ID of the redeem request
    /// @param assets The amount of assets to be received
    /// @param shares The amount of shares redeemed
    event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
}

interface IAccountableAsyncVault is
    IAccountableVault,
    IAsyncDeposit,
    IAsyncRedeem,
    IAsyncCancelDeposit,
    IAsyncCancelRedeem,
    IERC165
{
    /// @notice Emitted when a deposit becomes claimable
    /// @param controller The controller address for the deposit
    /// @param requestId The ID of the deposit request
    /// @param assets The amount of assets deposited
    /// @param shares The amount of shares to be minted
    event DepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);

    /// @notice Emitted when a redeem becomes claimable
    /// @param controller The controller address for the redemption
    /// @param requestId The ID of the redeem request
    /// @param assets The amount of assets to be received
    /// @param shares The amount of shares redeemed
    event RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

/// @notice Struct tracking a withdrawal request
struct WithdrawalRequest {
    /// @notice The number of shares requested
    uint256 shares;
    /// @notice The controller of the request
    address controller;
}

/// @notice Struct tracking the withdrawal queue
struct WithdrawalQueue {
    /// @notice The next request ID to be processed
    uint128 nextRequestId;
    /// @notice The last request ID to be processed
    uint128 lastRequestId;
    /// @notice The mapping of request IDs to withdrawal requests
    mapping(uint128 requestId => WithdrawalRequest request) requests;
}

/// @notice Interface for the withdrawal queue
interface IAccountableWithdrawalQueue {
    /// @notice Fulfill a redeem request
    /// @param controller The controller of the request
    /// @param shares The number of shares to fulfill
    function fulfillRedeemRequest(address controller, uint256 shares) external;

    /// @notice Process up to `maxShares` shares
    /// @param maxShares The maximum number of shares to process
    /// @return assetsUsed The number of assets used to process the shares
    function processUpToShares(uint256 maxShares) external returns (uint256 assetsUsed);

    /// @notice Process up to `maxRequestId` request
    /// @param maxRequestId The last request ID to process
    /// @return processedShares The number of shares that were processed
    /// @return assetsUsed The number of assets used to process the shares
    function processUpToRequestId(uint256 maxRequestId)
        external
        returns (uint256 processedShares, uint256 assetsUsed);

    /// @notice Preview required shares that can be processed with `maxAssets`
    /// @param maxAssets The maximum number of assets to be matched with `maxAssets`
    /// @return processedShares The number of shares that can be processed with the assets
    /// @return assetsUsed The number of assets used to process the shares
    function previewRequiredShares(uint256 maxAssets)
        external
        view
        returns (uint256 processedShares, uint256 assetsUsed);

    /// @notice Preview the last request ID that can be processed with `maxAssets`
    /// @param maxAssets The maximum number of assets to be matched with `maxAssets`
    /// @return maxRequestId The last request ID that can be processed with the assets
    /// @return assetsUsed The number of assets used to process the shares
    function previewMaxRequestId(uint256 maxAssets) external view returns (uint256 maxRequestId, uint256 assetsUsed);

    /// @notice Get the total number of shares queued
    /// @return totalQueuedShares The total number of shares queued
    function totalQueuedShares() external view returns (uint256 totalQueuedShares);

    /// @notice Get the reserved liquidity
    /// @return reservedLiquidity The reserved liquidity
    function reservedLiquidity() external view returns (uint256 reservedLiquidity);

    /// @notice Get the withdrawal queue indices
    /// @return nextRequestId The next request ID to be processed
    /// @return lastRequestId The last request ID to be processed
    function queue() external view returns (uint128 nextRequestId, uint128 lastRequestId);

    /// @notice Get the withdrawal request for a controller
    /// @param controller The controller of the request
    /// @return request The withdrawal request
    function withdrawalRequest(address controller) external view returns (WithdrawalRequest memory request);

    /// @notice Get the withdrawal requests
    /// @param start The start index of the queue
    /// @param end The end index of the queue
    /// @return requests The withdrawal requests
    function withdrawalRequests(uint128 start, uint128 end)
        external
        view
        returns (WithdrawalRequest[] memory requests);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import "../constants/Errors.sol";

import {IStrategyVaultHooks} from "../interfaces/IAccountableStrategy.sol";
import {IAuthorizable, IWhitelistable, PermissionLevel} from "../interfaces/IAccess.sol";
import {IAccountableVault, IControllerOperator, VaultState} from "../interfaces/IAccountableAsyncVault.sol";

import {AccessBase} from "../access/AccessBase.sol";
import {Authorizable} from "../access/Authorizable.sol";
import {Whitelistable} from "../access/Whitelistable.sol";

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @title AccountableVault
/// @notice Base contract for Accountable async vaults
/// @custom:security-contact [email protected]
abstract contract AccountableVault is IAccountableVault, ERC20, AccessBase {
    using Math for uint256;
    using SafeERC20 for IERC20;

    /// @notice Underlying asset
    IERC20 internal immutable _asset;

    /// @notice Decimals of the underlying asset
    uint8 internal immutable _underlyingDecimals;

    /// @notice Total assets in the vault
    uint256 internal _totalAssets;

    /// @notice Mapping of vault states by controller
    mapping(address controller => VaultState state) internal _vaultStates;

    /// @notice Minimum amount in wei to prevent dust attacks
    uint256 public constant MIN_AMOUNT_WEI = 10_000;

    /// @notice The loan manager contract that handles deposit/redeem requests
    IStrategyVaultHooks public strategy;

    /// @notice Precision for share calculations
    uint256 public precision;

    /// @notice Mapping of owner addresses to their approved operators
    mapping(address => mapping(address => bool)) public isOperator;

    /// @notice Whether the vault is transferable
    bool public transferableShares;

    /// @notice Ensures only the loan manager can call certain functions
    modifier onlyStrategy() {
        if (msg.sender != address(strategy)) {
            revert Unauthorized();
        }
        _;
    }

    /// @notice Ensures only authorized accounts can call certain functions
    modifier onlyAuth(address receiverOwner, address controller) {
        if (receiverOwner == controller) {
            if (!_isVerified(controller, msg.data)) revert Unauthorized();
        } else {
            address[] memory accounts = new address[](2);
            accounts[0] = receiverOwner;
            accounts[1] = controller;

            if (!_areVerified(accounts, msg.data)) revert Unauthorized();
        }
        _;
    }

    constructor(IERC20 asset_, PermissionLevel permissionLevel_) AccessBase(permissionLevel_) {
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        _underlyingDecimals = success ? assetDecimals : 18;
        _asset = asset_;
    }

    /// @dev Try to get the decimals of the underlying asset
    function _tryGetAssetDecimals(IERC20 asset_) private view returns (bool ok, uint8 assetDecimals) {
        (bool success, bytes memory encodedDecimals) =
            address(asset_).staticcall(abi.encodeCall(IERC20Metadata.decimals, ()));

        if (success && encodedDecimals.length >= 32) {
            uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
            if (returnedDecimals <= type(uint8).max) {
                return (true, uint8(returnedDecimals));
            }
        }
        return (false, 0);
    }

    /// @inheritdoc IControllerOperator
    function setOperator(address operator, bool approved) public returns (bool) {
        if (msg.sender == operator) {
            revert InvalidOperator();
        }
        isOperator[msg.sender][operator] = approved;
        emit OperatorSet(msg.sender, operator, approved);
        return true;
    }

    /// @inheritdoc IERC20Metadata
    function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
        return _underlyingDecimals;
    }

    /// @inheritdoc IERC4626
    function asset() public view virtual returns (address) {
        return address(_asset);
    }

    /// @inheritdoc IERC4626
    function totalAssets() public view returns (uint256) {
        return _totalAssets;
    }

    /// @inheritdoc IERC20
    function transfer(address to, uint256 amount) public virtual override(ERC20, IERC20) returns (bool) {
        _checkTransfer(amount, msg.sender, to);
        strategy.onTransfer(address(this), msg.sender, to, amount);
        return super.transfer(to, amount);
    }

    /// @inheritdoc IERC20
    function transferFrom(address from, address to, uint256 amount)
        public
        virtual
        override(ERC20, IERC20)
        returns (bool)
    {
        _checkTransfer(amount, from, to);
        strategy.onTransfer(address(this), from, to, amount);
        return super.transferFrom(from, to, amount);
    }

    /// @dev Checks transfer restrictions before executing the underlying transfer
    function _checkTransfer(uint256, address, address to) private {
        if (!transferableShares) revert SharesNotTransferable();
        if (!_isVerified(to, msg.data)) revert Unauthorized();
    }

    /// @inheritdoc IERC4626
    function convertToShares(uint256 assets) public view returns (uint256) {
        return _convertToShares(assets, sharePrice(), Math.Rounding.Floor);
    }

    /// @inheritdoc IERC4626
    function convertToAssets(uint256 shares) public view returns (uint256) {
        return _convertToAssets(shares, sharePrice(), Math.Rounding.Floor);
    }

    /// @inheritdoc IAccountableVault
    function assetShareRatio() public view returns (uint256) {
        uint256 shares = totalSupply();
        uint256 reserved = IStrategyVaultHooks(strategy).accruedAssets(address(this));
        return shares == 0 ? precision : (totalAssets() - reserved).mulDiv(precision, shares, Math.Rounding.Floor);
    }

    /// @dev Computes the assets based on shares and price
    function _convertToAssets(uint256 shares, uint256 price, Math.Rounding rounding)
        internal
        view
        virtual
        returns (uint256 assets)
    {
        assets = shares.mulDiv(price, precision, rounding);
    }

    /// @dev Computes the shares based on assets and price
    function _convertToShares(uint256 assets, uint256 price, Math.Rounding rounding)
        internal
        view
        virtual
        returns (uint256 shares)
    {
        shares = assets.mulDiv(precision, price, rounding);
    }

    /// @inheritdoc IAccountableVault
    function sharePrice() public view returns (uint256) {
        return strategy.sharePrice(address(this));
    }

    /// @inheritdoc IAccountableVault
    function share() public view returns (address) {
        return address(this);
    }

    /// @inheritdoc IAccountableVault
    function getState(address controller) public view returns (VaultState memory) {
        return _vaultStates[controller];
    }

    /// @inheritdoc IAccountableVault
    function lockAssets(uint256 assets, address sender) public onlyStrategy {
        _deposit(sender, assets);
        emit LockAssets(sender, assets);
    }

    /// @inheritdoc IAccountableVault
    function releaseAssets(uint256 assets, address receiver) public onlyStrategy {
        _withdraw(receiver, assets);
        emit ReleaseAssets(receiver, assets);
    }

    /// @inheritdoc IAccountableVault
    function mintShares(uint256 shares, address receiver) public onlyStrategy {
        _mint(receiver, shares);
    }

    /// @inheritdoc IAccountableVault
    function burnShares(uint256 shares, address sender) public onlyStrategy {
        _burn(sender, shares);
    }

    /// @inheritdoc IAccountableVault
    function forceTransferShares(address from, address to, uint256 shares) public onlyStrategy {
        strategy.onTransfer(address(this), from, to, shares);
        _transfer(from, to, shares);
    }

    /// @inheritdoc IAccountableVault
    function setSharesTransferable(bool sharesTransferable_) public onlyStrategy {
        bool oldStatus = transferableShares;
        transferableShares = sharesTransferable_;
        emit SharesTransferableSet(oldStatus, sharesTransferable_);
    }

    /// @inheritdoc IAuthorizable
    function setSigner(address signer_) public override(IAuthorizable, Authorizable) onlyStrategy {
        if (permissionLevel != PermissionLevel.KYC) revert PermissionLevelMismatch();
        super.setSigner(signer_);
    }

    /// @inheritdoc IWhitelistable
    function setAllowed(address[] calldata accounts, bool[] calldata statuses)
        public
        override(IWhitelistable, Whitelistable)
        onlyStrategy
    {
        if (permissionLevel != PermissionLevel.Whitelist) revert PermissionLevelMismatch();
        super.setAllowed(accounts, statuses);
    }

    /// @dev Deposit assets into the vault and increment total assets
    function _deposit(address caller, uint256 assets) internal virtual {
        _totalAssets += assets;
        _asset.safeTransferFrom(caller, address(this), assets);
    }

    /// @dev Withdraw assets from the vault and decrement total assets
    function _withdraw(address receiver, uint256 assets) internal virtual {
        _totalAssets -= assets;
        _asset.safeTransfer(receiver, assets);
    }

    /// @dev Checks if the caller is an approved operator for the owner
    /// @param owner The owner address to check against
    function _checkOperator(address owner) internal view {
        if (owner != msg.sender && !isOperator[owner][msg.sender]) {
            revert InvalidOperator();
        }
    }

    /// @dev Checks if the caller is an approved operator for the controller
    /// @param controller The controller address to check against
    function _checkController(address controller) internal view {
        if (controller != msg.sender && !isOperator[controller][msg.sender]) {
            revert InvalidController();
        }
    }

    /// @dev Checks if the owner has sufficient shares
    /// @param owner The owner address to check
    /// @param shares The amount of shares to check for
    function _checkShares(address owner, uint256 shares) internal view {
        if (balanceOf(owner) < shares || shares == 0) {
            revert InsufficientShares();
        }
    }

    /// @dev Checks if the amount is greater than the minimum amount
    function _checkMinAmount(uint256 amount) internal pure {
        if (amount < MIN_AMOUNT_WEI) revert InsufficientAmount();
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import {InsufficientShares, InvalidRange, OutOfBounds, NoQueueRequest} from "../../constants/Errors.sol";

import {
    IAccountableWithdrawalQueue,
    WithdrawalRequest,
    WithdrawalQueue
} from "../../interfaces/IAccountableWithdrawalQueue.sol";

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

/// @title AccountableWithdrawalQueue
/// @notice A contract that manages the withdrawal queue
/// @custom:security-contact [email protected]
abstract contract AccountableWithdrawalQueue is IAccountableWithdrawalQueue {
    using Math for uint256;

    /// @notice Price precision
    uint256 internal _precision;

    /// @notice Withdrawal queue
    WithdrawalQueue internal _queue;

    /// @notice Mapping of controller to request ID
    mapping(address controller => uint128 requestId) internal _requestIds;

    /// @inheritdoc IAccountableWithdrawalQueue
    uint256 public totalQueuedShares;

    /// @inheritdoc IAccountableWithdrawalQueue
    /// @dev The reserved liquidity is the amount of assets that are reserved for withdrawals
    /// @dev Has to be excluded from total liquidity calculations to avoid double counting
    uint256 public reservedLiquidity;

    constructor(uint256 precision_) {
        _precision = precision_;

        _queue.nextRequestId = 1;
    }

    /// @inheritdoc IAccountableWithdrawalQueue
    function queue() external view returns (uint128 nextRequestId, uint128 lastRequestId) {
        (nextRequestId, lastRequestId) = (_queue.nextRequestId, _queue.lastRequestId);
    }

    /// @inheritdoc IAccountableWithdrawalQueue
    function withdrawalRequest(address controller) external view returns (WithdrawalRequest memory request) {
        request = _queue.requests[_requestIds[controller]];
    }

    /// @inheritdoc IAccountableWithdrawalQueue
    function withdrawalRequests(uint128 start, uint128 end)
        external
        view
        returns (WithdrawalRequest[] memory requests)
    {
        if (start == 0 || start > end) revert InvalidRange();
        if (end > _queue.lastRequestId) revert OutOfBounds();

        uint128 len = end - start + 1;
        requests = new WithdrawalRequest[](len);

        for (uint128 i = 0; i < len;) {
            requests[i] = _queue.requests[start + i];
            unchecked {
                ++i;
            }
        }
    }

    /// @dev Pushes a new withdrawal request to the queue
    function _push(address controller, uint256 shares) internal returns (uint128 requestId) {
        uint128 requestId_ = _requestIds[controller];
        if (requestId_ == 0) {
            requestId = ++_queue.lastRequestId;

            _requestIds[controller] = requestId;
            _queue.requests[requestId] = WithdrawalRequest(shares, controller);
        } else {
            requestId = requestId_;
            _queue.requests[requestId_].shares += shares;
        }

        totalQueuedShares += shares;
    }

    /// @dev Reduces or removes the shares of a withdrawal request
    function _reduce(address controller, uint256 shares) internal returns (uint256 remainingShares) {
        uint128 requestId = _requestIds[controller];
        if (requestId == 0) revert NoQueueRequest();

        uint256 currentShares = _queue.requests[requestId].shares;
        if (shares > currentShares || currentShares == 0) revert InsufficientShares();

        remainingShares = currentShares - shares;
        totalQueuedShares -= shares;

        if (remainingShares == 0) {
            _delete(controller, requestId);
        } else {
            _queue.requests[requestId].shares = remainingShares;
        }
    }

    /// @dev Deletes a withdrawal request and its controller from the queue
    function _delete(address controller, uint128 requestId) private {
        delete _queue.requests[requestId];
        delete _requestIds[controller];
    }

    /// @dev Processes up to the given number of shares
    function _processUpToShares(uint256 maxShares, uint256 liquidity, uint256 currentPrice)
        internal
        returns (uint256 assetsUsed)
    {
        if (maxShares == 0 || totalQueuedShares == 0) return 0;

        uint256 precision_ = _precision;
        uint128 nextRequestId = _queue.nextRequestId;
        uint128 lastRequestId = _queue.lastRequestId;

        while (maxShares > 0 && nextRequestId <= lastRequestId) {
            WithdrawalRequest memory request_ = _queue.requests[nextRequestId];

            (uint256 shares_, uint256 assets_, bool processed_) =
                _processRequest(request_, liquidity, maxShares, currentPrice, precision_);

            if (shares_ == 0) {
                if (processed_) {
                    ++nextRequestId;
                    continue;
                }
                break;
            }

            _fulfillRedeemRequest(nextRequestId, request_.controller, shares_, currentPrice);

            assetsUsed += assets_;
            liquidity -= assets_;
            maxShares -= shares_;

            if (!processed_) break;

            ++nextRequestId;
        }

        _queue.nextRequestId = nextRequestId;
    }

    /// @dev Processes requests up to the given request ID
    function _processUpToRequestId(uint256 maxRequestId, uint256 liquidity, uint256 currentPrice)
        internal
        returns (uint256 processedShares, uint256 assetsUsed)
    {
        uint256 maxShares_ = totalQueuedShares;
        if (maxRequestId == 0 || maxShares_ == 0) return (0, 0);

        uint256 precision_ = _precision;
        uint128 nextRequestId = _queue.nextRequestId;
        uint128 lastRequestId = _queue.lastRequestId;

        uint256 cappedMaxRequestId = maxRequestId > lastRequestId ? lastRequestId : maxRequestId;

        while (nextRequestId <= cappedMaxRequestId) {
            WithdrawalRequest memory request_ = _queue.requests[nextRequestId];
            (uint256 shares_, uint256 assets_, bool processed_) =
                _processRequest(request_, liquidity, maxShares_, currentPrice, precision_);

            if (shares_ == 0) {
                if (processed_) {
                    ++nextRequestId;
                    continue;
                }
                break;
            }

            _fulfillRedeemRequest(nextRequestId, request_.controller, shares_, currentPrice);

            processedShares += shares_;
            assetsUsed += assets_;
            liquidity -= assets_;

            if (!processed_) break;

            ++nextRequestId;
        }

        _queue.nextRequestId = nextRequestId;
    }

    /// @dev Processes a withdrawal request updating all relevant state
    function _processRequest(
        WithdrawalRequest memory request,
        uint256 liquidity,
        uint256 maxShares,
        uint256 currentPrice,
        uint256 precision
    ) internal returns (uint256 processedShares, uint256 assetsUsed, bool processed) {
        if (request.controller == address(0)) return (0, 0, true);

        uint256 liquidShares = liquidity.mulDiv(precision, currentPrice);
        uint256 sharesToProcess = _min3(request.shares, liquidShares, maxShares);

        if (sharesToProcess == 0) return (0, 0, false);

        processedShares = sharesToProcess;
        assetsUsed = sharesToProcess.mulDiv(currentPrice, precision);

        processed = (sharesToProcess == request.shares);

        _reduce(request.controller, processedShares);
    }

    /// @dev Returns the number of shares that can be processed with the given assets
    function _previewRequiredShares(uint256 maxAssets, uint256 currentPrice)
        internal
        view
        returns (uint256 processedShares, uint256 assetsUsed)
    {
        uint256 precision_ = _precision;
        uint128 nextRequestId = _queue.nextRequestId;
        uint128 lastRequestId = _queue.lastRequestId;

        uint256 remainingShares = totalQueuedShares;

        while (maxAssets > 0 && remainingShares > 0 && nextRequestId <= lastRequestId) {
            WithdrawalRequest memory request_ = _queue.requests[nextRequestId];
            uint256 requestAssets = request_.shares.mulDiv(currentPrice, precision_);

            if (requestAssets > maxAssets) {
                uint256 maxAvailable = Math.min(maxAssets, requestAssets);
                uint256 maxShares = maxAvailable.mulDiv(precision_, currentPrice);

                if (maxShares > 0) {
                    processedShares += maxShares;
                    assetsUsed += maxShares.mulDiv(currentPrice, precision_);
                }
                break;
            }

            processedShares += request_.shares;
            remainingShares -= request_.shares;

            assetsUsed += requestAssets;
            maxAssets -= requestAssets;

            ++nextRequestId;
        }
    }

    /// @dev Returns the maximum request ID that can be processed with the given assets
    function _previewMaxRequestId(uint256 maxAssets, uint256 currentPrice)
        internal
        view
        returns (uint256 maxRequestId, uint256 assetsUsed)
    {
        uint256 precision_ = _precision;
        uint128 nextRequestId_ = _queue.nextRequestId;
        uint128 lastRequestId_ = _queue.lastRequestId;

        uint256 remainingShares = totalQueuedShares;

        while (maxAssets > 0 && remainingShares > 0 && nextRequestId_ <= lastRequestId_) {
            WithdrawalRequest memory request_ = _queue.requests[nextRequestId_];
            uint256 requestAssets = request_.shares.mulDiv(currentPrice, precision_);

            if (requestAssets > maxAssets) {
                uint256 maxAvailable = Math.min(maxAssets, requestAssets);
                uint256 maxShares = maxAvailable.mulDiv(precision_, currentPrice);

                if (maxShares > 0) {
                    maxRequestId = nextRequestId_;
                    assetsUsed += maxShares.mulDiv(currentPrice, precision_);
                }
                break;
            }

            assetsUsed += requestAssets;
            maxAssets -= requestAssets;

            remainingShares -= request_.shares;

            maxRequestId = nextRequestId_++;
        }
    }

    /// @dev Updates the reserved liquidity, to be triggered on withdraw/redeem
    function _updateReservedLiquidity(uint256 assets) internal {
        reservedLiquidity = assets < reservedLiquidity ? reservedLiquidity - assets : 0;
    }

    /// @dev Returns the minimum of three values
    function _min3(uint256 a, uint256 b, uint256 c) internal pure returns (uint256 result) {
        assembly {
            result := a
            if lt(b, result) { result := b }
            if lt(c, result) { result := c }
        }
    }

    /// @dev Hook function to trigger state updates when a request is processed
    function _fulfillRedeemRequest(uint128 requestId, address controller, uint256 shares, uint256 price)
        internal
        virtual;
}

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

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    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 overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        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 division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds 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.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = 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^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 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^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @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;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @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 + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @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;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @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
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface 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[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC20 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: GPL-2.0-or-later
pragma solidity ^0.8.20;

import {IAccess, PermissionLevel} from "../interfaces/IAccess.sol";

import {Whitelistable} from "./Whitelistable.sol";
import {Authorizable} from "./Authorizable.sol";

/// @title AccessBase - A base contract for access control extending Whitelistable and Authorizable
/// @notice This contract is used to gate access to callable/interactable contracts
/// @dev See IAccess for detailed interface documentation
/// @custom:security-contact [email protected]
abstract contract AccessBase is IAccess, Whitelistable, Authorizable {
    /// @inheritdoc IAccess
    PermissionLevel public permissionLevel;

    /// @notice Constructor
    /// @param permissionLevel_ The permission level for the contract
    constructor(PermissionLevel permissionLevel_) {
        permissionLevel = permissionLevel_;
    }

    function _isVerified(address account, bytes calldata msgData) internal returns (bool) {
        PermissionLevel _permission = permissionLevel;

        if (_permission == PermissionLevel.KYC) return _verify(account, msgData);

        if (_permission == PermissionLevel.Whitelist) return allowed[account];

        return true;
    }

    function _areVerified(address[] memory accounts, bytes calldata msgData) internal returns (bool) {
        PermissionLevel _permission = permissionLevel;

        if (_permission == PermissionLevel.KYC) return _verifyMany(accounts, msgData);

        if (_permission == PermissionLevel.Whitelist) return allowedMany(accounts);

        return true;
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import "../constants/Errors.sol";

import {IAuthorizable, TxAuthData, VerifyManyData} from "../interfaces/IAccess.sol";

import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

/// @title Authorizable - A signature based transaction authorization mechanism
/// @notice This contract is used to authorize transactions using a signature
/// @dev See IAuthorizable for detailed interface documentation
/// @custom:security-contact [email protected]
abstract contract Authorizable is IAuthorizable, EIP712 {
    /// @notice EIP-712 typehash for TxAuthData
    bytes32 private constant _TXAUTH_TYPEHASH = keccak256(
        "TxAuthData(bytes functionCallData,address contractAddress,address account,uint256 nonce,uint256 blockExpiration)"
    );

    constructor() EIP712("Authorizable", "1") {}

    /// @notice These are used to decompose msgData
    uint256 private constant _BYTES_32_LENGTH = 32;

    /// @notice This is the length for the expected signature
    uint256 private constant _SIGNATURE_LENGTH = 65;

    /// @notice The offset for the extra data in the calldata
    uint256 private constant _EXTRA_DATA_LENGTH = _SIGNATURE_LENGTH + _BYTES_32_LENGTH;

    /// @notice The address of the off-chain signer
    address public signer;

    /// @notice Mapping to track the nonces to prevent replay attacks
    /// @dev Maps a user address to their current nonce
    mapping(address user => uint256 nonce) public nonces;

    /// @inheritdoc IAuthorizable
    function setSigner(address signer_) public virtual {
        if (signer_ == address(0)) revert ZeroAddress();
        _setSigner(signer_);
    }

    /// @inheritdoc IAuthorizable
    function getMessageHash(TxAuthData calldata txAuthData) public view returns (bytes32) {
        return _getTypedDataHash(txAuthData);
    }

    /// @dev Verify transaction authentication data
    /// @param account The account that is being verified
    /// @param msgData The message data of the transaction
    /// @return True if the transaction authentication data is valid, reverts otherwise
    function _verify(address account, bytes calldata msgData) internal returns (bool) {
        address signer_ = signer;

        if (account == address(0)) revert ZeroAddress();
        if (signer_ == address(0)) revert InvalidSigner();
        if (msgData.length < _EXTRA_DATA_LENGTH) revert InvalidMsgDataLength();

        bytes calldata argsWithSelector = msgData[:msgData.length - _EXTRA_DATA_LENGTH];

        uint256 blockExpiration =
            uint256(bytes32(msgData[msgData.length - _EXTRA_DATA_LENGTH:msgData.length - _SIGNATURE_LENGTH]));

        bytes calldata signature = msgData[msgData.length - _SIGNATURE_LENGTH:];

        if (block.number >= blockExpiration) revert InvalidBlockExpiration();

        uint256 nonce = nonces[account]++;

        TxAuthData memory txAuthData = TxAuthData({
            functionCallData: argsWithSelector,
            contractAddress: address(this),
            account: account,
            nonce: nonce,
            blockExpiration: blockExpiration
        });

        bytes32 digest = _getTypedDataHash(txAuthData);

        emit TxAuthDataVerified(block.chainid, nonce, blockExpiration, address(this), account, argsWithSelector);

        if (!SignatureChecker.isValidSignatureNow(signer_, digest, signature)) revert InvalidSignature();

        return true;
    }

    /// @dev Verify transaction authentication data for multiple accounts
    /// @param accounts Array of accounts that are being verified in the transaction
    /// @param msgData [functionCallData][blockExpiration][signature1][signature2]...[signatureN][length]
    /// @return True if all transaction authentication data is valid, false otherwise
    function _verifyMany(address[] memory accounts, bytes calldata msgData) internal returns (bool) {
        address signer_ = signer;

        if (signer_ == address(0)) revert InvalidSigner();
        if (accounts.length == 0) revert EmptyAccountsArray();
        if (msgData.length < _EXTRA_DATA_LENGTH) revert InvalidMsgDataLength();

        VerifyManyData memory data = _parseVerifyManyData(accounts, msgData);

        if (block.number >= data.blockExpiration) revert InvalidBlockExpiration();

        for (uint256 i = 0; i < accounts.length; i++) {
            if (accounts[i] == address(0)) revert ZeroAddress();

            if (!_verifySingle(signer_, accounts[i], data, i, msgData)) {
                revert InvalidSignature();
            }
        }
        return true;
    }

    /// @dev Parse msgData for _verifyMany function
    function _parseVerifyManyData(address[] memory accounts, bytes calldata msgData)
        private
        pure
        returns (VerifyManyData memory data)
    {
        data.numSignatures = uint256(bytes32(msgData[msgData.length - _BYTES_32_LENGTH:]));
        if (data.numSignatures != accounts.length) revert ArrayLengthMismatch();

        uint256 extraDataLength = _BYTES_32_LENGTH + (data.numSignatures * _SIGNATURE_LENGTH) + _BYTES_32_LENGTH;
        if (msgData.length < extraDataLength) revert InvalidMsgDataLength();

        data.argsWithSelector = msgData[:msgData.length - extraDataLength];

        uint256 blockExpirationStart = msgData.length - extraDataLength;
        data.blockExpiration = uint256(bytes32(msgData[blockExpirationStart:blockExpirationStart + _BYTES_32_LENGTH]));
        data.signaturesStart = blockExpirationStart + _BYTES_32_LENGTH;
    }

    /// @dev Verify a single signature within verifyMany
    function _verifySingle(address signer_, address account, VerifyManyData memory data, uint256 index, bytes calldata msgData)
        private
        returns (bool)
    {
        uint256 nonce = nonces[account]++;

        bytes32 digest = _getTypedDataHash(
            TxAuthData({
                functionCallData: data.argsWithSelector,
                contractAddress: address(this),
                account: account,
                nonce: nonce,
                blockExpiration: data.blockExpiration
            })
        );

        emit TxAuthDataVerified(
            block.chainid, nonce, data.blockExpiration, address(this), account, data.argsWithSelector
        );

        bytes calldata signature = msgData[
            data.signaturesStart + (index * _SIGNATURE_LENGTH):data.signaturesStart + ((index + 1) * _SIGNATURE_LENGTH)
        ];

        return SignatureChecker.isValidSignatureNow(signer_, digest, signature);
    }

    /// @dev Compute EIP-712 typed data digest for TxAuthData
    function _getTypedDataHash(TxAuthData memory txAuthData) internal view returns (bytes32) {
        bytes32 structHash = keccak256(
            abi.encode(
                _TXAUTH_TYPEHASH,
                keccak256(txAuthData.functionCallData),
                txAuthData.contractAddress,
                txAuthData.account,
                txAuthData.nonce,
                txAuthData.blockExpiration
            )
        );
        return _hashTypedDataV4(structHash);
    }

    /// @dev Sets the signer address
    function _setSigner(address signer_) internal {
        address oldSigner = signer;
        signer = signer_;
        emit SignerChanged(oldSigner, signer_);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;

import {IWhitelistable} from "../interfaces/IAccess.sol";

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

/// @title Whitelistable - A contract that allows whitelisting of accounts
/// @notice This contract is used to allow/disallow account access
/// @dev See IWhitelistable for detailed interface documentation
/// @custom:security-contact [email protected]
abstract contract Whitelistable is IWhitelistable {
    /// @notice Mapping to track the allowed status of accounts
    /// @dev Maps an account address to its allowed status
    mapping(address account => bool allowed) public allowed;

    /// @inheritdoc IWhitelistable
    function setAllowed(address[] calldata accounts, bool[] calldata statuses) public virtual {
        if (accounts.length != statuses.length) revert ArrayLengthMismatch();

        for (uint256 i = 0; i < accounts.length;) {
            allowed[accounts[i]] = statuses[i];
            emit AllowedSet(accounts[i], statuses[i]);
            unchecked {
                ++i;
            }
        }
    }

    function allowedMany(address[] memory accounts) public view returns (bool) {
        for (uint256 i = 0; i < accounts.length;) {
            if (!allowed[accounts[i]]) return false;
            unchecked {
                ++i;
            }
        }
        return true;
    }
}

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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.
     */
    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.
     */
    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.
     */
    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 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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

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

pragma solidity ^0.8.20;

import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
 * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
 * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
 * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {IERC-5267}.
     */
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _EIP712Name(),
            _EIP712Version(),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /**
     * @dev The name parameter for the EIP712 domain.
     *
     * NOTE: By default this function reads _name which is an immutable value.
     * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
     */
    // solhint-disable-next-line func-name-mixedcase
    function _EIP712Name() internal view returns (string memory) {
        return _name.toStringWithFallback(_nameFallback);
    }

    /**
     * @dev The version parameter for the EIP712 domain.
     *
     * NOTE: By default this function reads _version which is an immutable value.
     * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
     */
    // solhint-disable-next-line func-name-mixedcase
    function _EIP712Version() internal view returns (string memory) {
        return _version.toStringWithFallback(_versionFallback);
    }
}

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

pragma solidity ^0.8.20;

import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";

/**
 * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
 * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
 * Argent and Safe Wallet (previously Gnosis Safe).
 */
library SignatureChecker {
    /**
     * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
     * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
        (address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
        return
            (error == ECDSA.RecoverError.NoError && recovered == signer) ||
            isValidERC1271SignatureNow(signer, hash, signature);
    }

    /**
     * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
     * against the signer smart contract using ERC1271.
     *
     * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
     * change through time. It could return true at block N and false at block N+1 (or the opposite).
     */
    function isValidERC1271SignatureNow(
        address signer,
        bytes32 hash,
        bytes memory signature
    ) internal view returns (bool) {
        (bool success, bytes memory result) = signer.staticcall(
            abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
        );
        return (success &&
            result.length >= 32 &&
            abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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
     * {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
        }
        (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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
     */
    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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

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

pragma solidity ^0.8.20;

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

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using
     * {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

File 29 of 34 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.20;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

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

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC1271 standard signature validation method for
 * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
 */
interface IERC1271 {
    /**
     * @dev Should return whether the signature provided is valid for the provided data
     * @param hash      Hash of the data to be signed
     * @param signature Signature byte array associated with _data
     */
    function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}

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

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     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;
 *     }
 * }
 * ```
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 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) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            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) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            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) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "murky/=lib/murky/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
    "solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"contract IERC20","name":"asset_","type":"address"},{"internalType":"address","name":"strategy_","type":"address"},{"internalType":"bool","name":"sharesTransferable_","type":"bool"},{"internalType":"enum PermissionLevel","name":"permissionLevel_","type":"uint8"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint256","name":"precision_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"CancelRedeemRequestFailed","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"EmptyAccountsArray","type":"error"},{"inputs":[],"name":"ExceedsMaxRedeem","type":"error"},{"inputs":[],"name":"ExceedsRedeemLimit","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientAmount","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[],"name":"InsufficientShares","type":"error"},{"inputs":[],"name":"InvalidBlockExpiration","type":"error"},{"inputs":[],"name":"InvalidController","type":"error"},{"inputs":[],"name":"InvalidMsgDataLength","type":"error"},{"inputs":[],"name":"InvalidOperator","type":"error"},{"inputs":[],"name":"InvalidRange","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidSigner","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"NoPendingRedeemRequest","type":"error"},{"inputs":[],"name":"NoQueueRequest","type":"error"},{"inputs":[],"name":"NoRedeemRequest","type":"error"},{"inputs":[],"name":"OutOfBounds","type":"error"},{"inputs":[],"name":"PermissionLevelMismatch","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SharesNotTransferable","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"AllowedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"CancelRedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"LockAssets","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"OperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"RedeemClaimable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"RedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"ReleaseAssets","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"oldStatus","type":"bool"},{"indexed":false,"internalType":"bool","name":"newStatus","type":"bool"}],"name":"SharesTransferableSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldSigner","type":"address"},{"indexed":true,"internalType":"address","name":"newSigner","type":"address"}],"name":"SignerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"chainID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"blockExpiration","type":"uint256"},{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":true,"internalType":"address","name":"userAddress","type":"address"},{"indexed":false,"internalType":"bytes","name":"functionCallData","type":"bytes"}],"name":"TxAuthDataVerified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"MIN_AMOUNT_WEI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"allowed","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"}],"name":"allowedMany","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetShareRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"sender","type":"address"}],"name":"burnShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"name":"cancelRedeemRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"controller","type":"address"}],"name":"claimableRedeemRequest","outputs":[{"internalType":"uint256","name":"claimableShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"forceTransferShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"fulfillRedeemRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"functionCallData","type":"bytes"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"blockExpiration","type":"uint256"}],"internalType":"struct TxAuthData","name":"txAuthData","type":"tuple"}],"name":"getMessageHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"getState","outputs":[{"components":[{"internalType":"uint256","name":"maxMint","type":"uint256"},{"internalType":"uint256","name":"maxWithdraw","type":"uint256"},{"internalType":"uint256","name":"depositAssets","type":"uint256"},{"internalType":"uint256","name":"redeemShares","type":"uint256"},{"internalType":"uint256","name":"depositPrice","type":"uint256"},{"internalType":"uint256","name":"mintPrice","type":"uint256"},{"internalType":"uint256","name":"redeemPrice","type":"uint256"},{"internalType":"uint256","name":"withdrawPrice","type":"uint256"},{"internalType":"uint256","name":"pendingDepositRequest","type":"uint256"},{"internalType":"uint256","name":"pendingRedeemRequest","type":"uint256"}],"internalType":"struct VaultState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"sender","type":"address"}],"name":"lockAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mintShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"controller","type":"address"}],"name":"pendingRedeemRequest","outputs":[{"internalType":"uint256","name":"pendingShares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permissionLevel","outputs":[{"internalType":"enum PermissionLevel","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"precision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"name":"previewMaxRequestId","outputs":[{"internalType":"uint256","name":"maxRequestId","type":"uint256"},{"internalType":"uint256","name":"assetsUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"name":"previewRequiredShares","outputs":[{"internalType":"uint256","name":"processedShares","type":"uint256"},{"internalType":"uint256","name":"assetsUsed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxRequestId","type":"uint256"}],"name":"processUpToRequestId","outputs":[{"internalType":"uint256","name":"processedShares","type":"uint256"},{"internalType":"uint256","name":"assetsUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"maxShares","type":"uint256"}],"name":"processUpToShares","outputs":[{"internalType":"uint256","name":"assetsUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"queue","outputs":[{"internalType":"uint128","name":"nextRequestId","type":"uint128"},{"internalType":"uint128","name":"lastRequestId","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"releaseAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"controller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"requestRedeem","outputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reservedLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"accounts","type":"address[]"},{"internalType":"bool[]","name":"statuses","type":"bool[]"}],"name":"setAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"sharesTransferable_","type":"bool"}],"name":"setSharesTransferable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"signer_","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"share","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sharePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategy","outputs":[{"internalType":"contract IStrategyVaultHooks","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalQueuedShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transferableShares","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"withdrawalRequest","outputs":[{"components":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"controller","type":"address"}],"internalType":"struct WithdrawalRequest","name":"request","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"start","type":"uint128"},{"internalType":"uint128","name":"end","type":"uint128"}],"name":"withdrawalRequests","outputs":[{"components":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"controller","type":"address"}],"internalType":"struct WithdrawalRequest[]","name":"requests","type":"tuple[]"}],"stateMutability":"view","type":"function"}]

0x6101a0806040523461052b57614be0803803809161001d828561052f565b833981019060e08183031261052b5780516001600160a01b038116810361052b5760208201516001600160a01b0381169081900361052b5760408301519182151580930361052b576060840151600381101561052b5760808501516001600160401b03811161052b578661009291870161056d565b60a08601519096906001600160401b03811161052b5760c0916100b691880161056d565b9501519460409687516100c9898261052f565b600c815260208101906b417574686f72697a61626c6560a01b82528951936100f18b8661052f565b6001808652603160f81b602087019081525f8c905581546001600160801b0319168217909155845190946001600160401b03821161042e5760095490600182811c92168015610521575b60208310146104105781601f8493116104b3575b50602090601f831160011461044d575f92610442575b50508160011b915f199060031b1c1916176009555b8051906001600160401b03821161042e57600a5490600182811c92168015610424575b60208310146104105781601f8493116103a2575b50602090601f831160011461033c575f92610331575b50508160011b915f199060031b1c191617600a555b6101e581610656565b610120526101f2846107dd565b61014052519020918260e05251902080610100524660a05287519060208201927f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f84528983015260608201524660808201523060a082015260a0815261025960c08261052f565b5190206080523060c05260ff80196010541691161760105561027a816105b3565b9015610329575b6101805261016052801561031a5760018060a01b0319601354161760135560ff801960165416911617601655601455516142ca9081610916823960805181614068015260a05181614125015260c05181614032015260e051816140b7015261010051816140dd01526101205181611076015261014051816110a0015261016051818181611a980152612fc701526101805181611b940152f35b63d92e233d60e01b5f5260045ffd5b506012610281565b015190505f806101c7565b600a5f9081528281209350601f198516905b81811061038a5750908460019594939210610372575b505050811b01600a556101dc565b01515f1960f88460031b161c191690555f8080610364565b9293602060018192878601518155019501930161034e565b600a5f529091507fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8601f840160051c81019160208510610406575b90601f859493920160051c01905b8181106103f857506101b1565b5f81558493506001016103eb565b90915081906103dd565b634e487b7160e01b5f52602260045260245ffd5b91607f169161019d565b634e487b7160e01b5f52604160045260245ffd5b015190505f80610165565b60095f9081528281209350601f198516905b81811061049b5750908460019594939210610483575b505050811b0160095561017a565b01515f1960f88460031b161c191690555f8080610475565b9293602060018192878601518155019501930161045f565b60095f529091507f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af601f840160051c81019160208510610517575b90601f859493920160051c01905b818110610509575061014f565b5f81558493506001016104fc565b90915081906104ee565b91607f169161013b565b5f80fd5b601f909101601f19168101906001600160401b0382119082101761042e57604052565b6001600160401b03811161042e57601f01601f191660200190565b81601f8201121561052b5780519061058482610552565b92610592604051948561052f565b8284526020838301011161052b57815f9260208093018386015e8301015290565b5f8091604051602081019063313ce56760e01b8252600481526105d760248261052f565b51916001600160a01b03165afa3d1561064e573d906105f582610552565b91610603604051938461052f565b82523d5f602084013e5b80610642575b61061f575b505f905f90565b60208180518101031261052b576020015160ff8111610618579060ff6001921690565b50602081511015610613565b60609061060d565b908151602081105f146106d0575090601f815111610690576020815191015160208210610681571790565b5f198260200360031b1b161790565b604460209160405192839163305a27a960e01b83528160048401528051918291826024860152018484015e5f828201840152601f01601f19168101030190fd5b6001600160401b03811161042e57600c54600181811c911680156107d3575b602082101461041057601f81116107a0575b50602092601f821160011461073f57928192935f92610734575b50508160011b915f199060031b1c191617600c5560ff90565b015190505f8061071b565b601f19821693600c5f52805f20915f5b8681106107885750836001959610610770575b505050811b01600c5560ff90565b01515f1960f88460031b161c191690555f8080610762565b9192602060018192868501518155019401920161074f565b600c5f52601f60205f20910160051c810190601f830160051c015b8181106107c85750610701565b5f81556001016107bb565b90607f16906106ef565b908151602081105f14610808575090601f815111610690576020815191015160208210610681571790565b6001600160401b03811161042e57600d54600181811c9116801561090b575b602082101461041057601f81116108d8575b50602092601f821160011461087757928192935f9261086c575b50508160011b915f199060031b1c191617600d5560ff90565b015190505f80610853565b601f19821693600d5f52805f20915f5b8681106108c057508360019596106108a8575b505050811b01600d5560ff90565b01515f1960f88460031b161c191690555f808061089a565b91926020600181928685015181550194019201610887565b600d5f52601f60205f20910160051c810190601f830160051c015b8181106109005750610839565b5f81556001016108f3565b90607f169061082756fe6080806040526004361015610012575f80fd5b5f905f3560e01c90816301e1d1141461273b5750806301ffc9a71461262d57806306fdde031461256357806307a2d13a1461253957806308f45aff146123d7578063095ea7b31461232f5780630a28a4771461189757806317ee5d931461229357806318160ddd146122755780631bab58f51461213e5780631ef44e68146120b75780632246ced114612019578063238ac93314611ff057806323b872dd14611e8f57806324a16c6114611e1657806326535a8114611db95780632d0777d814611bd25780632e2d298414611bb8578063313ce56714611b7a57806335c1e48414611ac757806338d52e0f14611a825780633991998114611a655780633abb0604146118fd5780633d7849b7146118bb578063402d267d1461189c5780634cdad50614611897578063558a7297146117ed57806359c615d6146117ca578063623f50331461179e5780636c19e783146116d75780636e553f65146116ae57806370a0823114611675578063788cf2de146115aa5780637d41c86e146111915780637ecebe001461115857806384b0196e1461105c5780638726972914611041578063883fcc5614610e3b57806394bf804d14610e1257806395d89b4114610d2b57806399167fb414610cf25780639d81be4314610c7f5780639f30851214610c61578063a6bddd6314610af0578063a8c62e7614610ac7578063a8d5fd6514610aac578063a9059cbb146109e6578063b3d7f6b9146109bb578063b460af941461085c578063b6363cf214610804578063ba08765214610640578063bf919763146105f0578063c63d75b6146105b7578063c6e6f59214610407578063ce96cb7714610593578063d3b5dc3b14610575578063d63a8e1114610536578063d905777e14610517578063da39b3e7146104fd578063dc20c8ff146104df578063dd62ed3e1461048c578063e10d29ee1461045d578063eaed1d0714610439578063ef8b30f714610407578063f2b3c18f146103bd578063f5a23d8d146103805763fb53db6d146102ff575f80fd5b3461037d57604036600319011261037d5760043561031b61278f565b6013546001600160a01b0316330361036f5760208161035b847f48e891cea1a36b8bc4f1fb9fb7ae5508bf0edfee67aaeb354f1cfb8f62d68c8294613b53565b6040519384526001600160a01b031692a280f35b6282b42960e81b8352600483fd5b80fd5b503461037d57604036600319011261037d576020906009906040906001600160a01b036103ab61278f565b16815260128452200154604051908152f35b503461037d57604036600319011261037d576103d761278f565b6013546001600160a01b031633036103f9576103f690600435906135a9565b80f35b6282b42960e81b8252600482fd5b503461037d57602036600319011261037d576020610431610426612ce5565b6014546004356138f4565b604051908152f35b503461037d57604036600319011261037d57602061043161045861278f565b612d4c565b503461037d578060031936011261037d5760406001548151906001600160801b038116825260801c6020820152f35b503461037d57604036600319011261037d5760406104a8612779565b916104b161278f565b9260018060a01b031681526007602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461037d578060031936011261037d576020600454604051908152f35b503461037d5760206104316105113661289a565b91612d6f565b503461037d57602036600319011261037d576020610431610458612779565b503461037d57602036600319011261037d5760209060ff906040906001600160a01b03610561612779565b168152600b84522054166040519015158152f35b503461037d578060031936011261037d576020601454604051908152f35b503461037d57602036600319011261037d5760206104316105b2612779565b612d22565b503461037d57602036600319011261037d5760206104316105de6105d9612779565b612c43565b6105e6612ce5565b90601454906138f4565b503461037d57602036600319011261037d576013546001600160a01b031633036106325760206104316106216136e6565b610629612ce5565b90600435613ba6565b6282b42960e81b8152600490fd5b503461037d5761064f3661289a565b9092916001600160a01b038281169290851691908284036107b85761067436826132ee565b156107aa575b61068382613590565b61068c8161347c565b61069581612d4c565b821161079b578385526012602052604085206006810154956106c56014546106be818a886138f4565b98866138ff565b6013549093906001600160a01b0316803b15610797576107019183918b8360405180968195829463d34cf33560e01b84528d3060048601612bc8565b03925af1801561078c57610777575b505060209661072f848061073e958a809661072a82613aad565b613acf565b6107398430613a27565b613b53565b60405190848252858201527ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db60403392a4604051908152f35b610782828092612862565b61037d5780610710565b6040513d84823e3d90fd5b8280fd5b63708a336b60e11b8552600485fd5b6282b42960e81b8552600485fd5b6107f26040516107c9606082612862565b600281526040366020830137846107df82612a00565b52856107ea82612a21565b523690612fed565b61067a576282b42960e81b8552600485fd5b503461037d57604036600319011261037d576040610820612779565b9161082961278f565b9260018060a01b031681526015602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461037d5761086b3661289a565b9092916001600160a01b038281169290851691908284036109985761089036826132ee565b156107aa575b61089f82613590565b6108a88161347c565b6108b181612d22565b821161079b578385526012602052604085206007810154956108db601454976106be818a886138f4565b6013549093906001600160a01b0316803b15610797576109179183918b836040518096819582946364a0366360e01b84528d3060048601612bc8565b03925af1801561078c57610983575b505060209661094061094a93888680809661072a82613aad565b6107398730613a27565b60405190815283858201527ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db60403392a4604051908152f35b61098e828092612862565b61037d5780610926565b6109a96040516107c9606082612862565b610896576282b42960e81b8552600485fd5b503461037d57602036600319011261037d5760206104316109da612ce5565b601454906004356138ff565b503461037d57604036600319011261037d57610a00612779565b6024359060ff6016541615610a9d57610a1936826132ee565b1561036f576013546001600160a01b0316803b15610a9957836040518092630987df0360e01b8252818381610a5489893330600486016129bd565b03925af18015610a8e57610a79575b610a6e838333612eb6565b602060405160018152f35b610a84848092612862565b6107975782610a63565b6040513d86823e3d90fd5b8380fd5b636dbb04b560e01b8352600483fd5b503461037d578060031936011261037d576020604051308152f35b503461037d578060031936011261037d576013546040516001600160a01b039091168152602090f35b503461037d57602036600319011261037d57610b0a612ce5565b6004358280815492600154946001600160801b0386169560801c96600454905b83151580610c58575b80610c45575b15610c34576001600160801b0388169485875260026020526040872095610b86898660405199610b688b6127fc565b8054808c526001909101546001600160a01b031660208c0152613cf9565b92868411610bcf57505081610ba2610bb09493610ba893612cd8565b95612cb7565b945190612cb7565b966001600160801b03610bc288612e99565b9716909790939293610b2a565b9895919760409b508693919a50859750610bf5959450508082105f14610c2d5750613cf9565b9182610c0f575b5050509091505b82519182526020820152f35b610c239450610c1d92613cf9565b90612cd8565b81905f8080610bfc565b9050613cf9565b965050505091505060409250610c03565b50886001600160801b0389161115610b39565b50811515610b33565b503461037d578060031936011261037d576020600554604051908152f35b503461037d57604036600319011261037d57610c99612779565b60135460243591906001600160a01b0316330361036f576001600160a01b038116835260036020526040832054610cee929190610ce9906001600160801b03168383610ce3612ce5565b9261393d565b6134c9565b5080f35b503461037d57604036600319011261037d57610d0c61278f565b6013546001600160a01b031633036103f9576103f69060043590613a27565b503461037d578060031936011261037d576040519080600a5490610d4e82612904565b8085529160018116908115610deb5750600114610d8e575b610d8a84610d7681860382612862565b604051918291602083526020830190612755565b0390f35b600a81527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8939250905b808210610dd157509091508101602001610d7682610d66565b919260018160209254838588010152019101909291610db8565b60ff191660208087019190915292151560051b85019092019250610d769150839050610d66565b503461037d57604036600319011261037d576020610431610e3161278f565b3390600435612d6f565b503461037d57604036600319011261037d576004356001600160801b03811680910361103d576024356001600160801b0381168091036107975781158015611034575b6110255760015460801c8111611016578190036001600160801b038111611002576001600160801b0360019116016001600160801b038111611002576001600160801b0316610ecc81612883565b610ed96040519182612862565b818152601f19610ee883612883565b01845b818110610fdf575050835b6001600160801b03811683811015610f84578085016001600160801b038111610f705791610f686001926001600160801b03809516895260026020526040892060405190610f43826127fc565b8054825285808060a01b03910154166020820152610f618288612a31565b5285612a31565b500116610ef6565b634e487b7160e01b87526011600452602487fd5b8286604051918291602083016020845282518091526020604085019301915b818110610fb1575050500390f35b8251805185526020908101516001600160a01b03168186015286955060409094019390920191600101610fa3565b602090604051610fee816127fc565b5f81525f8382015282828601015201610eeb565b634e487b7160e01b83526011600452602483fd5b632d0483c560e21b8352600483fd5b63561ce9bb60e01b8352600483fd5b50808211610e7e565b5080fd5b503461037d578060031936011261037d576020610431612ce5565b503461037d578060031936011261037d576110fc9061109a7f0000000000000000000000000000000000000000000000000000000000000000613e58565b906110c47f0000000000000000000000000000000000000000000000000000000000000000613eb8565b90602061110a604051936110d88386612862565b8385525f368137604051968796600f60f81b885260e08589015260e0880190612755565b908682036040880152612755565b904660608601523060808601528260a086015284820360c08601528080855193848152019401925b82811061114157505050500390f35b835185528695509381019392810192600101611132565b503461037d57602036600319011261037d576020906040906001600160a01b03611180612779565b168152600f83522054604051908152f35b503461037d576111a03661289a565b9092916001600160a01b03808316929085169190838303611566576111c536876132ee565b156107aa575b3384141580611543575b611534576111e28661347c565b6111eb82613590565b838552600660205281604086205410801561152c575b61151d57604081869785611252985260126020526009838a2001611226868254612cd8565b905560018060a01b036013541690898451809a8195829463716f6e8d60e11b84528a3060048601612bc8565b03925af194851561151257869087966114cd575b50156113c85785948387526012602052604087206009810191825480156113b95785116113aa5760145461129a91866138f4565b916112a36136e6565b831161139b57847f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc5745069460209a887f4dd5187225a2ae5f5ea35ca7b1732180f848cc4b6f7dce34b4c5e9f384d77dec6040888e9d9c9b9860076113616113789a6113116113909e600554612cd8565b60055560018401611323868254612cd8565b80915560038501916113368d8454612cd8565b8093556113448d8254612cb7565b90556113538260145483613cf9565b6006860155601454906138ff565b91015581519081528f8790820152a35b3090612eb6565b60408051338152602081019290925290918291820190565b0390a4604051908152f35b63bb55fd2760e01b8952600489fd5b632ca2f52b60e11b8952600489fd5b634ef1d5c560e01b8a5260048afd5b92919093507f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc574506611390856113786001600160801b038960209a5086815260038b528160408220541680155f146114af5750600154600161142a8260801c612e99565b848116928580198360801b1691161782559289815260038e5260408120838619825416179055604080519161145e836127fc565b8883528f8301948c865281528f600290522090518155019060018060a01b039051166bffffffffffffffffffffffff60a01b8254161790555b6114a384600454612cd8565b60045516978897611371565b9060409082815260028d52206114c6858254612cd8565b9055611497565b9550506040853d60401161150a575b816114e960409383612862565b810103126115065760206114fc86612a8d565b950151945f611266565b8580fd5b3d91506114dc565b6040513d88823e3d90fd5b633999656760e01b8552600485fd5b508115611201565b63ccea9e6f60e01b8552600485fd5b50838552601560209081526040808720335f908152925290205460ff16156111d5565b611598604051611577606082612862565b6002815260403660208301378561158d82612a00565b52846107ea82612a21565b6111cb576282b42960e81b8552600485fd5b503461037d578060031936011261037d57600854601354604051638304ff8f60e01b8152306004820152919290602090839060249082906001600160a01b03165afa918215611668578192611630575b5050816116105750506020601454604051908152f35b60209161162261162b92601154612cb7565b601454906138f4565b610431565b9091506020813d602011611660575b8161164c60209383612862565b8101031261165c5751905f6115fa565b5f80fd5b3d915061163f565b50604051903d90823e3d90fd5b503461037d57602036600319011261037d576020906040906001600160a01b0361169d612779565b168152600683522054604051908152f35b503461037d57604036600319011261037d5760206104316116cd61278f565b3390600435612a9a565b503461037d57602036600319011261037d576116f1612779565b6013546001600160a01b031633036103f95760ff60105416600381101561178a5760010361177b576001600160a01b0316801561176c57600e80546001600160a01b0319811683179091556001600160a01b03167feeb293e1f8f3a9db91ade748726387ed1352ca78f5430c5f06fe3d1e1ad505798380a380f35b63d92e233d60e01b8252600482fd5b63fb61026d60e01b8252600482fd5b634e487b7160e01b83526021600452602483fd5b503461037d578060031936011261037d5760ff6010541660405190600381101561178a57602092508152f35b503461037d578060031936011261037d57602060ff601654166040519015158152f35b503461037d57604036600319011261037d57611807612779565b60243580151591828203610a99576001600160a01b0316923384146118885790604061185192338152601560205220845f5260205260405f209060ff801983541691151516179055565b6040519081527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa26760203392a3602060405160018152f35b63ccea9e6f60e01b8152600490fd5b6127b9565b503461037d57602036600319011261037d5760206104316105d9612779565b503461037d57602036600319011261037d576013546001600160a01b03163303610632576040610c036118ec6136e6565b6118f4612ce5565b90600435613776565b503461037d57604036600319011261037d576004356001600160401b03811161103d5761192e9036906004016128d4565b6024356001600160401b038111610a995761194d9036906004016128d4565b601354919290916001600160a01b031633036107aa5760ff601054166003811015611a5157600203611a4257818103611a3357845b81811061198d578580f35b806119dc6119a66119a160019487896136b5565b6136c5565b838060a01b036119bf6119ba85888c6136b5565b6136d2565b168952600b602052604089209060ff801983541691151516179055565b6119ea6119ba8285896136b5565b7fc5217903bd71390e046be1c1ee589f3af533c8fe5b67ee0a7dade2f8a853badd6020611a1b6119a185898b6136b5565b926040519315158452858060a01b031692a201611982565b63512509d360e11b8552600485fd5b63fb61026d60e01b8552600485fd5b634e487b7160e01b86526021600452602486fd5b503461037d578060031936011261037d5760206040516127108152f35b503461037d578060031936011261037d576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461037d57602036600319011261037d576004356001600160401b03811161103d5760a0600319823603011261103d5760405191611b0583612847565b81600401356001600160401b03811161103d578201903660238301121561037d57602061043185608486611b4136600489013560248a01612c0d565b8352611b4f602482016127a5565b85840152611b5f604482016127a5565b60408401526064810135606084015201356080820152613607565b503461037d578060031936011261037d57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b503461037d576020610431611bcc3661289a565b91612a9a565b503461037d57604036600319011261037d57611bec612779565b611bf461278f565b6001600160a01b03818116929081169190838303611d9657611c1636826132ee565b156107aa575b611c258161347c565b828552601260205260096040862001805415611d875760135460405163014f3df960e71b81523060048201526001600160a01b0384811660248301529091602091839160449183918c91165af1908115611d7c578791611d3e575b5015611d2f57611cb291610ce9879286845260036020526001600160801b036040852054169581549485925530612eb6565b5060135484906001600160a01b0316803b1561103d5781809160046040518094819363a022985f60e01b83525af1611d1a575b505060407f91f628e8c7a6f75792ac4b71145861594f59fa4439e803548a394011b6c6bc2b918151908152336020820152a380f35b81611d2491612862565b610a9957835f611ce5565b6331a852bb60e01b8652600486fd5b90506020813d602011611d74575b81611d5960209383612862565b81010312611d7057611d6a90612a8d565b5f611c80565b8680fd5b3d9150611d4c565b6040513d89823e3d90fd5b636040212f60e11b8652600486fd5b611da7604051611577606082612862565b611c1c576282b42960e81b8552600485fd5b503461037d57604036600319011261037d57600435611dd661278f565b6013546001600160a01b0316330361036f5760208161035b847f8dd4664ff532261941b00d8312d1764002e0de40af594821e7a4c08e4f0b8c7094612f76565b503461037d57602036600319011261037d5760043580151580910361103d576013546001600160a01b031633036103f95760407f304c943633ec0da73778a23e6ffc08756b2e7a7e606e52ff88cf83933d95342f916016549060ff811660ff1983161760165560ff83519216151582526020820152a180f35b503461037d57611e9e366127c2565b9160ff6016541615611fe157611eb436836132ee565b15611fd35760135484906001600160a01b0316803b1561103d57816040518092630987df0360e01b8252818381611ef18b8b8b30600486016129bd565b03925af1801561078c57611fbe575b50506001600160a01b038116808552600760209081526040808720335f90815292529020549060018201611f3b575b5050610a6e9350612eb6565b848210611fa3578015611f8f573315611f7b5785604091610a6e975260076020522060018060a01b0333165f526020528360405f20910390555f80611f2f565b634a1406b160e11b86526004869052602486fd5b63e602df0560e01b86526004869052602486fd5b6064868684637dc7a0d960e11b835233600452602452604452fd5b81611fc891612862565b610a9957835f611f00565b6282b42960e81b8452600484fd5b636dbb04b560e01b8452600484fd5b503461037d578060031936011261037d57600e546040516001600160a01b039091168152602090f35b503461037d57602036600319011261037d57600435906001600160401b03821161037d573660238301121561037d57816004013561205681612883565b906120646040519283612862565b8082526024602083019160051b8501019236841161037d5750602460209401905b8382106120a0578461209684612a45565b6040519015158152f35b8480916120ac846127a5565b815201910190612085565b503461037d57602036600319011261037d57604080916120d5612779565b6120dd6129e8565b506001600160a01b0316815260036020908152828220546001600160801b031682526002905220815190612110826127fc565b80548083526001909101546001600160a01b0390811660209384019081528451928352511691810191909152f35b503461037d57602036600319011261037d5760406101409161215e612779565b81610120845161216d8161282b565b82815282602082015282868201528260608201528260808201528260a08201528260c08201528260e082015282610100820152015260018060a01b031681526012602052206040516121be8161282b565b81549182825260018101546020830190815260028201546040840190815260038301546060850190815260048401546080860190815260058501549160a0870192835260068601549360c0880194855260078701549560e08901968752610120600960088a0154996101008c019a8b5201549901988952604051998a525160208a01525160408901525160608801525160808701525160a08601525160c08501525160e08401525161010083015251610120820152f35b503461037d578060031936011261037d576020600854604051908152f35b503461165c576122a2366127c2565b6013549293929091906001600160a01b03163381900361232157803b1561165c575f6040518092630987df0360e01b82528183816122e689898d30600486016129bd565b03925af1801561231657612300575b506103f69293612eb6565b6103f693505f61230f91612862565b5f926122f5565b6040513d5f823e3d90fd5b6282b42960e81b5f5260045ffd5b3461165c57604036600319011261165c57612348612779565b6024359033156123c4576001600160a01b03169081156123b157335f52600760205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b634a1406b160e11b5f525f60045260245ffd5b63e602df0560e01b5f525f60045260245ffd5b3461165c57602036600319011261165c576123f0612ce5565b600435905f905f925f5493600154926001600160801b0384169360801c94600454905b84151580612530575b8061251d575b1561250d576001600160801b0386165f52600260205260405f209161246d89856040519561244f876127fc565b80548088526001909101546001600160a01b03166020880152613cf9565b918683116124b3579161249e816124986124a4946124906124aa97895190612cd8565b975190612cb7565b97612cd8565b96612cb7565b95612e99565b94939290612413565b9750508793955082946124d5939250604098918082105f14610c2d5750613cf9565b91826124eb575b50505082519182526020820152f35b90610c1d916124ff84612505969597612cd8565b95613cf9565b8380806124dc565b9550505091505060409250610c03565b50866001600160801b0387161115612422565b5081151561241c565b3461165c57602036600319011261165c576020610431612557612ce5565b601454906004356138f4565b3461165c575f36600319011261165c576040515f60095461258381612904565b808452906001811690811561260957506001146125ab575b610d8a83610d7681850382612862565b60095f9081527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af939250905b8082106125ef57509091508101602001610d7661259b565b9192600181602092548385880101520191019092916125d7565b60ff191660208086019190915291151560051b84019091019150610d76905061259b565b3461165c57602036600319011261165c5760043563ffffffff60e01b811680910361165c57806020911590811561272a575b8115612719575b8115612708575b81156126f7575b81156126e6575b81156126d5575b81156126c4575b81156126b3575b81156126a2575b506040519015158152f35b63a219a02560e01b14905082612697565b6342fb809b60e11b81149150612690565b6336372b0760e01b81149150612689565b63e3bc4e6560e01b81149150612682565b63043eff2d60e51b8114915061267b565b6364ec41f760e01b81149150612674565b6305a0eefb60e31b8114915061266d565b631883ba3960e21b81149150612666565b6301ffc9a760e01b8114915061265f565b3461165c575f36600319011261165c576020906011548152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361165c57565b602435906001600160a01b038216820361165c57565b35906001600160a01b038216820361165c57565b3461165c575f80fd5b606090600319011261165c576004356001600160a01b038116810361165c57906024356001600160a01b038116810361165c579060443590565b604081019081106001600160401b0382111761281757604052565b634e487b7160e01b5f52604160045260245ffd5b61014081019081106001600160401b0382111761281757604052565b60a081019081106001600160401b0382111761281757604052565b90601f801991011681019081106001600160401b0382111761281757604052565b6001600160401b0381116128175760051b60200190565b606090600319011261165c57600435906024356001600160a01b038116810361165c57906044356001600160a01b038116810361165c5790565b9181601f8401121561165c578235916001600160401b03831161165c576020808501948460051b01011161165c57565b90600182811c92168015612932575b602083101461291e57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612913565b5f929181549161294b83612904565b80835292600181169081156129a0575060011461296757505050565b5f9081526020812093945091925b838310612986575060209250010190565b600181602092949394548385870101520191019190612975565b915050602093945060ff929192191683830152151560051b010190565b6001600160a01b03918216815291811660208301529091166040820152606081019190915260800190565b604051906129f5826127fc565b5f6020838281520152565b805115612a0d5760200190565b634e487b7160e01b5f52603260045260245ffd5b805160011015612a0d5760400190565b8051821015612a0d5760209160051b010190565b5f5b8151811015612a86576001600160a01b03612a628284612a31565b51165f52600b60205260ff60405f20541615612a8057600101612a47565b50505f90565b5050600190565b5190811515820361165c57565b90916001600160a01b03808416929082169190838303612ba557612abe36836132ee565b15612321575b612acd81613590565b612ad68261347c565b601354604051632fab172760e11b81529060209082906001600160a01b0316815f81612b08898d8a3060048601612bc8565b03925af1908115612316575f91612b6f575b5090612b60817fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d794612b5b612b54604096601454856138f4565b809a6135a9565b612f76565b8151908152856020820152a390565b9190506020823d602011612b9d575b81612b8b60209383612862565b8101031261165c579051612b60612b1a565b3d9150612b7e565b612bb6604051611577606082612862565b612ac4576282b42960e81b5f5260045ffd5b6001600160a01b039182168152602081019290925291821660408201529116606082015260800190565b6001600160401b03811161281757601f01601f191660200190565b929192612c1982612bf2565b91612c276040519384612862565b82948184528183011161165c578281602093845f960137010152565b601354604051630760168760e41b81523060048201526001600160a01b0392831660248201529160209183916044918391165afa908115612316575f91612c88575090565b90506020813d602011612caf575b81612ca360209383612862565b8101031261165c575190565b3d9150612c96565b91908203918211612cc457565b634e487b7160e01b5f52601160045260245ffd5b91908201809211612cc457565b601354604051639b9b942160e01b815230600482015290602090829060249082906001600160a01b03165afa908115612316575f91612c88575090565b60018060a01b03165f52601260205260405f20906003600183015492015415612d4757565b5f9150565b6001600160a01b03165f90815260126020526040902060030154908115612d4757565b916001600160a01b03828116929082169190838303612e7a57612d9236836132ee565b1561232157612ddd915b612da586613590565b612dae8161347c565b60208160018060a01b0360135416845f8a604051988995869485936309daab9960e11b85523060048601612bc8565b03925af180156123165786935f91612e42575b506040927fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79492612b5b612e2b612e339460145490876138ff565b998a936135a9565b8151908682526020820152a390565b9350506020833d602011612e72575b81612e5e60209383612862565b8101031261165c5791518592906040612df0565b3d9150612e51565b612e8b604051611577606082612862565b1561232157612ddd91612d9c565b6001600160801b03166001600160801b038114612cc45760010190565b6001600160a01b0316908115612f63576001600160a01b0316918215612f5057815f52600660205260405f2054818110612f3757817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92602092855f52600684520360405f2055845f526006825260405f20818154019055604051908152a3565b8263391434e360e21b5f5260045260245260445260645ffd5b63ec442f0560e01b5f525f60045260245ffd5b634b637e8f60e11b5f525f60045260245ffd5b612feb91612f8681601154612cd8565b6011556040516323b872dd60e01b60208201526001600160a01b0390921660248301523060448301526064808301919091528152612fc5608482612862565b7f0000000000000000000000000000000000000000000000000000000000000000613eef565b565b60105460ff165f60038210156132da576001821461302257600292935050146130165750600190565b61301f90612a45565b90565b5050600e546001600160a01b03169081156132cb578051156132bc576061831061329e5760405192608084018481106001600160401b03821117612817576040526060845260208401915f835260408501945f865260608101915f8352601f19840192848411612cc457602093358082528251036132ad5751604181029080820460411490151715612cc45780840190818511612cc457604001809111612cc45780851061329e576130d48186612cb7565b9085821161165c576130eb6130f39236905f612c0d565b845285612cb7565b83810190818111612cc45785821161165c573586528752845143101561328f575f5b8151811015613282576001600160a01b036131308284612a31565b511615613273576001600160a01b036131498284612a31565b5116805f52600f855260405f209081549161316383613daa565b9055613196855189516040519161317983612847565b825230898301528360408301528460608301526080820152613607565b917fe551f1469c2895fdd376c4517ff8a4cadba06443acbd1980104b6e27e2e11646895191875192604051914683528a830152604082015260806060820152806131e530946080830190612755565b0390a388516041830283810460411484151715612cc45761320591612cd8565b90895160018401808511612cc457604181029080820460411490151715612cc45761322f91612cd8565b80831161165c5787811161165c578280613250926132579550033691612c0d565b9089613f62565b1561326457600101613115565b638baa579f60e01b5f5260045ffd5b63d92e233d60e01b5f5260045ffd5b5050505050505050600190565b63b7ca31cd60e01b5f5260045ffd5b636cf4aa4f60e11b5f5260045ffd5b63512509d360e11b5f5260045ffd5b630b6e0c5f60e11b5f5260045ffd5b632057875960e21b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b5f9160ff601054169060038210156132da57600182146133325750600214613317575050600190565b6001600160a01b03168152600b602052604090205460ff1690565b600e546001600160a01b03938416945090921691905082156132735781156132cb576061811061329e5760618103818111612cc45781811161165c57818111612cc4576040198201828111612cc45780821161165c576133988284036040190183613d8e565b92811161165c578243101561328f578461346c92613472965f52600f6020527fe551f1469c2895fdd376c4517ff8a4cadba06443acbd1980104b6e27e2e1164660405f208054906133e882613daa565b90556134236040516133f981612847565b61340436865f612c0d565b8152306020820152856040820152826060820152886080820152613607565b96604051914683526020830152604082015260806060820152826080820152825f60a08301375f60a0848301015260a0813094601f80199101168101030190a360413691612c0d565b91613f62565b1561326457600190565b60018060a01b03163381141590816134a5575b5061349657565b6336abb4df60e11b5f5260045ffd5b5f90815260156020908152604080832033845290915281205460ff1615915061348f565b6001600160a01b03165f818152600360205260409020549192916001600160801b0316801561358157805f52600260205260405f20548085118015613579575b61356a5761351a8561352392612cb7565b94600454612cb7565b60045583613559575f5260026020525f60016040822082815501555f52600360205260405f206001600160801b03198154169055565b90505f5260026020528160405f2055565b633999656760e01b5f5260045ffd5b508015613509565b6333618a9160e21b5f5260045ffd5b6127101161359a57565b632ca2f52b60e11b5f5260045ffd5b6001600160a01b0316908115612f50577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020826135ea5f94600854612cd8565b6008558484526006825260408420818154019055604051908152a3565b6042908051602081519101209060018060a01b036020820151169060018060a01b036040820151169060806060820151910151916040519360208501957fb78a071cab05828812909bd43bd7019c31f7c50fbdf8657efd4932bbe287e16d875260408601526060850152608084015260a083015260c082015260c0815261368f60e082612862565b51902061369a61402f565b906040519161190160f01b8352600283015260228201522090565b9190811015612a0d5760051b0190565b35801515810361165c5790565b356001600160a01b038116810361165c5790565b601354604051638304ff8f60e01b815230600482015290602090829060249082906001600160a01b03165afa8015612316575f90613742575b61372d915060055490612cd8565b6011549080821115612a805761301f91612cb7565b506020813d60201161376e575b8161375c60209383612862565b8101031261165c5761372d905161371f565b3d915061374f565b91925f5f9460045490851580156138ec575b6138df575f54600154966001600160801b0388169760801c908181115f146138d3575096979395975b6001600160801b0381168881116138bb575f52600260205260405f206137ff8385878d604051956137e1876127fc565b80548752600101546001600160a01b03166020870190815295613db8565b999092821561387e5792613830836138369361382b8a859761383c9960018060a01b039051168b61393d565b612cd8565b98612cd8565b9a612cb7565b96156138575761384b90612e99565b965b96979395976137b1565b939796505050506001600160801b039192505b166001600160801b03196001541617600155565b509a9594939297989150509794976138a35750505050506001600160801b039061386a565b90919295946138b59098949798612e99565b9661384d565b509396505050506001600160801b039192945061386a565b905096979395976137b1565b505050925050505f905f90565b508115613788565b9061301f9291613cf9565b919061390c828285613cf9565b928215613929570961391b5790565b60018101809111612cc45790565b634e487b7160e01b5f52601260045260245ffd5b92919060018060a01b031691825f52601260205260405f2090600982019081548015613a1857841161359a5760145461397691856138f4565b9461397f6136e6565b8611613a09577f4dd5187225a2ae5f5ea35ca7b1732180f848cc4b6f7dce34b4c5e9f384d77dec9360409360076139f76001600160801b03956139c48b600554612cd8565b600555600184016139d68c8254612cd8565b80915560038501916139e9878454612cd8565b809355611344878254612cb7565b910155835196875260208701521693a3565b63bb55fd2760e01b5f5260045ffd5b634ef1d5c560e01b5f5260045ffd5b9091906001600160a01b03168015612f6357805f52600660205260405f2054838110613a93576020845f94957fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef93858752600684520360408620558060085403600855604051908152a3565b915063391434e360e21b5f5260045260245260445260645ffd5b6005549081811015613ac757613ac291612cb7565b600555565b50505f600555565b9291936001840194600386549501948086549411613b3b57839180821115613b4a57613afa91612cb7565b935b84875511613b3b5780821115613b3357613b1591612cb7565b825515613b2b575b5415613b265750565b5f9055565b5f8155613b1d565b50505f613b15565b632206bead60e21b5f5260045ffd5b50505f93613afc565b90612fc5612feb92613b6783601154612cb7565b60115560405163a9059cbb60e01b60208201526001600160a01b039091166024820152604481019290925281606481015b03601f198101835282612862565b919290925f9083158015613cef575b613ce6575f949193945493600154946001600160801b0386169560801c955b87151580613cd3575b15613cbd576001600160801b0381165f52600260205260405f20613c0b83868b89604051956137e1876127fc565b959091928315613c845791613c3881613c3e9361382b8b88613c44999860018060a01b039051168b61393d565b98612cb7565b99612cb7565b9215613c5f57613c5390612e99565b955b9596939196613bd4565b9396505050506001600160801b03919250166001600160801b03196001541617600155565b509895949150509791909497613ca957505050506001600160801b039192935061386a565b9091929593613cb790612e99565b95613c55565b9350509450506001600160801b0391925061386a565b50866001600160801b0382161115613bdd565b50509150505f90565b5060045415613bb5565b9091828202915f1984820993838086109503948086039514613d805784831115613d7157829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b63227bc15360e01b5f5260045ffd5b505080925015613929570490565b359060208110613d9c575090565b5f199060200360031b1b1690565b5f198114612cc45760010190565b939491909294602085019360018060a01b0385511615613e3f578682613ddd92613cf9565b91855192838110613e37575b50828110613e2f575b508115613e225790613e09613e1f92829783613cf9565b94519351938114936001600160a01b03166134c9565b50565b505f945084935083925050565b91505f613df2565b92505f613de9565b505f9550859450600193505050565b600411156132da57565b60ff8114613e9e5760ff811690601f8211613e8f5760405191613e7c604084612862565b6020808452838101919036833783525290565b632cd44ac360e21b5f5260045ffd5b5060405161301f81613eb181600c61293c565b0382612862565b60ff8114613edc5760ff811690601f8211613e8f5760405191613e7c604084612862565b5060405161301f81613eb181600d61293c565b5f80613f179260018060a01b03169360208151910182865af1613f10614185565b9083614236565b8051908115159182613f3f575b5050613f2d5750565b635274afe760e01b5f5260045260245ffd5b819250906020918101031261165c576020613f5a9101612a8d565b155f80613f24565b90613f6d838261414b565b50909390613f7a81613e4e565b159384614019575b508315613f90575b50505090565b5f935090613b98613fc88594936040519283916020830195630b135d3f60e11b87526024840152604060448401526064830190612755565b51915afa613fd4614185565b8161400b575b81613fe9575b505f8080613f8a565b905060208180518101031261165c5760200151630b135d3f60e11b145f613fe0565b905060208151101590613fda565b6001600160a01b0384811691161493505f613f82565b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161480614122575b1561408a577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260a0815261411c60c082612862565b51902090565b507f00000000000000000000000000000000000000000000000000000000000000004614614061565b815191906041830361417b576141749250602082015190606060408401519301515f1a906141b4565b9192909190565b50505f9160029190565b3d156141af573d9061419682612bf2565b916141a46040519384612862565b82523d5f602084013e565b606090565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161422b579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15612316575f516001600160a01b0381161561422157905f905f90565b505f906001905f90565b5050505f9160039190565b9061425a575080511561424b57805190602001fd5b630a12f52160e11b5f5260045ffd5b8151158061428b575b61426b575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561426356fea2646970667358221220e39e9ad2cb2fbd0b267a0000e0e1432ec3ada7e1a021a8bb5287c20e41c3612264736f6c634300081b0033000000000000000000000000754704bc059f8c67012fed69bc8a327a5aafb6030000000000000000000000006ef26abad081b9ca95f5f84d6115af0d4c2b54800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000c097ce7bc90715b34b9f100000000000000000000000000000000000000000000000000000000000000000000000106d696b652074657374203238313220320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086d696b6574657374000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080806040526004361015610012575f80fd5b5f905f3560e01c90816301e1d1141461273b5750806301ffc9a71461262d57806306fdde031461256357806307a2d13a1461253957806308f45aff146123d7578063095ea7b31461232f5780630a28a4771461189757806317ee5d931461229357806318160ddd146122755780631bab58f51461213e5780631ef44e68146120b75780632246ced114612019578063238ac93314611ff057806323b872dd14611e8f57806324a16c6114611e1657806326535a8114611db95780632d0777d814611bd25780632e2d298414611bb8578063313ce56714611b7a57806335c1e48414611ac757806338d52e0f14611a825780633991998114611a655780633abb0604146118fd5780633d7849b7146118bb578063402d267d1461189c5780634cdad50614611897578063558a7297146117ed57806359c615d6146117ca578063623f50331461179e5780636c19e783146116d75780636e553f65146116ae57806370a0823114611675578063788cf2de146115aa5780637d41c86e146111915780637ecebe001461115857806384b0196e1461105c5780638726972914611041578063883fcc5614610e3b57806394bf804d14610e1257806395d89b4114610d2b57806399167fb414610cf25780639d81be4314610c7f5780639f30851214610c61578063a6bddd6314610af0578063a8c62e7614610ac7578063a8d5fd6514610aac578063a9059cbb146109e6578063b3d7f6b9146109bb578063b460af941461085c578063b6363cf214610804578063ba08765214610640578063bf919763146105f0578063c63d75b6146105b7578063c6e6f59214610407578063ce96cb7714610593578063d3b5dc3b14610575578063d63a8e1114610536578063d905777e14610517578063da39b3e7146104fd578063dc20c8ff146104df578063dd62ed3e1461048c578063e10d29ee1461045d578063eaed1d0714610439578063ef8b30f714610407578063f2b3c18f146103bd578063f5a23d8d146103805763fb53db6d146102ff575f80fd5b3461037d57604036600319011261037d5760043561031b61278f565b6013546001600160a01b0316330361036f5760208161035b847f48e891cea1a36b8bc4f1fb9fb7ae5508bf0edfee67aaeb354f1cfb8f62d68c8294613b53565b6040519384526001600160a01b031692a280f35b6282b42960e81b8352600483fd5b80fd5b503461037d57604036600319011261037d576020906009906040906001600160a01b036103ab61278f565b16815260128452200154604051908152f35b503461037d57604036600319011261037d576103d761278f565b6013546001600160a01b031633036103f9576103f690600435906135a9565b80f35b6282b42960e81b8252600482fd5b503461037d57602036600319011261037d576020610431610426612ce5565b6014546004356138f4565b604051908152f35b503461037d57604036600319011261037d57602061043161045861278f565b612d4c565b503461037d578060031936011261037d5760406001548151906001600160801b038116825260801c6020820152f35b503461037d57604036600319011261037d5760406104a8612779565b916104b161278f565b9260018060a01b031681526007602052209060018060a01b03165f52602052602060405f2054604051908152f35b503461037d578060031936011261037d576020600454604051908152f35b503461037d5760206104316105113661289a565b91612d6f565b503461037d57602036600319011261037d576020610431610458612779565b503461037d57602036600319011261037d5760209060ff906040906001600160a01b03610561612779565b168152600b84522054166040519015158152f35b503461037d578060031936011261037d576020601454604051908152f35b503461037d57602036600319011261037d5760206104316105b2612779565b612d22565b503461037d57602036600319011261037d5760206104316105de6105d9612779565b612c43565b6105e6612ce5565b90601454906138f4565b503461037d57602036600319011261037d576013546001600160a01b031633036106325760206104316106216136e6565b610629612ce5565b90600435613ba6565b6282b42960e81b8152600490fd5b503461037d5761064f3661289a565b9092916001600160a01b038281169290851691908284036107b85761067436826132ee565b156107aa575b61068382613590565b61068c8161347c565b61069581612d4c565b821161079b578385526012602052604085206006810154956106c56014546106be818a886138f4565b98866138ff565b6013549093906001600160a01b0316803b15610797576107019183918b8360405180968195829463d34cf33560e01b84528d3060048601612bc8565b03925af1801561078c57610777575b505060209661072f848061073e958a809661072a82613aad565b613acf565b6107398430613a27565b613b53565b60405190848252858201527ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db60403392a4604051908152f35b610782828092612862565b61037d5780610710565b6040513d84823e3d90fd5b8280fd5b63708a336b60e11b8552600485fd5b6282b42960e81b8552600485fd5b6107f26040516107c9606082612862565b600281526040366020830137846107df82612a00565b52856107ea82612a21565b523690612fed565b61067a576282b42960e81b8552600485fd5b503461037d57604036600319011261037d576040610820612779565b9161082961278f565b9260018060a01b031681526015602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461037d5761086b3661289a565b9092916001600160a01b038281169290851691908284036109985761089036826132ee565b156107aa575b61089f82613590565b6108a88161347c565b6108b181612d22565b821161079b578385526012602052604085206007810154956108db601454976106be818a886138f4565b6013549093906001600160a01b0316803b15610797576109179183918b836040518096819582946364a0366360e01b84528d3060048601612bc8565b03925af1801561078c57610983575b505060209661094061094a93888680809661072a82613aad565b6107398730613a27565b60405190815283858201527ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db60403392a4604051908152f35b61098e828092612862565b61037d5780610926565b6109a96040516107c9606082612862565b610896576282b42960e81b8552600485fd5b503461037d57602036600319011261037d5760206104316109da612ce5565b601454906004356138ff565b503461037d57604036600319011261037d57610a00612779565b6024359060ff6016541615610a9d57610a1936826132ee565b1561036f576013546001600160a01b0316803b15610a9957836040518092630987df0360e01b8252818381610a5489893330600486016129bd565b03925af18015610a8e57610a79575b610a6e838333612eb6565b602060405160018152f35b610a84848092612862565b6107975782610a63565b6040513d86823e3d90fd5b8380fd5b636dbb04b560e01b8352600483fd5b503461037d578060031936011261037d576020604051308152f35b503461037d578060031936011261037d576013546040516001600160a01b039091168152602090f35b503461037d57602036600319011261037d57610b0a612ce5565b6004358280815492600154946001600160801b0386169560801c96600454905b83151580610c58575b80610c45575b15610c34576001600160801b0388169485875260026020526040872095610b86898660405199610b688b6127fc565b8054808c526001909101546001600160a01b031660208c0152613cf9565b92868411610bcf57505081610ba2610bb09493610ba893612cd8565b95612cb7565b945190612cb7565b966001600160801b03610bc288612e99565b9716909790939293610b2a565b9895919760409b508693919a50859750610bf5959450508082105f14610c2d5750613cf9565b9182610c0f575b5050509091505b82519182526020820152f35b610c239450610c1d92613cf9565b90612cd8565b81905f8080610bfc565b9050613cf9565b965050505091505060409250610c03565b50886001600160801b0389161115610b39565b50811515610b33565b503461037d578060031936011261037d576020600554604051908152f35b503461037d57604036600319011261037d57610c99612779565b60135460243591906001600160a01b0316330361036f576001600160a01b038116835260036020526040832054610cee929190610ce9906001600160801b03168383610ce3612ce5565b9261393d565b6134c9565b5080f35b503461037d57604036600319011261037d57610d0c61278f565b6013546001600160a01b031633036103f9576103f69060043590613a27565b503461037d578060031936011261037d576040519080600a5490610d4e82612904565b8085529160018116908115610deb5750600114610d8e575b610d8a84610d7681860382612862565b604051918291602083526020830190612755565b0390f35b600a81527fc65a7bb8d6351c1cf70c95a316cc6a92839c986682d98bc35f958f4883f9d2a8939250905b808210610dd157509091508101602001610d7682610d66565b919260018160209254838588010152019101909291610db8565b60ff191660208087019190915292151560051b85019092019250610d769150839050610d66565b503461037d57604036600319011261037d576020610431610e3161278f565b3390600435612d6f565b503461037d57604036600319011261037d576004356001600160801b03811680910361103d576024356001600160801b0381168091036107975781158015611034575b6110255760015460801c8111611016578190036001600160801b038111611002576001600160801b0360019116016001600160801b038111611002576001600160801b0316610ecc81612883565b610ed96040519182612862565b818152601f19610ee883612883565b01845b818110610fdf575050835b6001600160801b03811683811015610f84578085016001600160801b038111610f705791610f686001926001600160801b03809516895260026020526040892060405190610f43826127fc565b8054825285808060a01b03910154166020820152610f618288612a31565b5285612a31565b500116610ef6565b634e487b7160e01b87526011600452602487fd5b8286604051918291602083016020845282518091526020604085019301915b818110610fb1575050500390f35b8251805185526020908101516001600160a01b03168186015286955060409094019390920191600101610fa3565b602090604051610fee816127fc565b5f81525f8382015282828601015201610eeb565b634e487b7160e01b83526011600452602483fd5b632d0483c560e21b8352600483fd5b63561ce9bb60e01b8352600483fd5b50808211610e7e565b5080fd5b503461037d578060031936011261037d576020610431612ce5565b503461037d578060031936011261037d576110fc9061109a7f417574686f72697a61626c65000000000000000000000000000000000000000c613e58565b906110c47f3100000000000000000000000000000000000000000000000000000000000001613eb8565b90602061110a604051936110d88386612862565b8385525f368137604051968796600f60f81b885260e08589015260e0880190612755565b908682036040880152612755565b904660608601523060808601528260a086015284820360c08601528080855193848152019401925b82811061114157505050500390f35b835185528695509381019392810192600101611132565b503461037d57602036600319011261037d576020906040906001600160a01b03611180612779565b168152600f83522054604051908152f35b503461037d576111a03661289a565b9092916001600160a01b03808316929085169190838303611566576111c536876132ee565b156107aa575b3384141580611543575b611534576111e28661347c565b6111eb82613590565b838552600660205281604086205410801561152c575b61151d57604081869785611252985260126020526009838a2001611226868254612cd8565b905560018060a01b036013541690898451809a8195829463716f6e8d60e11b84528a3060048601612bc8565b03925af194851561151257869087966114cd575b50156113c85785948387526012602052604087206009810191825480156113b95785116113aa5760145461129a91866138f4565b916112a36136e6565b831161139b57847f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc5745069460209a887f4dd5187225a2ae5f5ea35ca7b1732180f848cc4b6f7dce34b4c5e9f384d77dec6040888e9d9c9b9860076113616113789a6113116113909e600554612cd8565b60055560018401611323868254612cd8565b80915560038501916113368d8454612cd8565b8093556113448d8254612cb7565b90556113538260145483613cf9565b6006860155601454906138ff565b91015581519081528f8790820152a35b3090612eb6565b60408051338152602081019290925290918291820190565b0390a4604051908152f35b63bb55fd2760e01b8952600489fd5b632ca2f52b60e11b8952600489fd5b634ef1d5c560e01b8a5260048afd5b92919093507f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc574506611390856113786001600160801b038960209a5086815260038b528160408220541680155f146114af5750600154600161142a8260801c612e99565b848116928580198360801b1691161782559289815260038e5260408120838619825416179055604080519161145e836127fc565b8883528f8301948c865281528f600290522090518155019060018060a01b039051166bffffffffffffffffffffffff60a01b8254161790555b6114a384600454612cd8565b60045516978897611371565b9060409082815260028d52206114c6858254612cd8565b9055611497565b9550506040853d60401161150a575b816114e960409383612862565b810103126115065760206114fc86612a8d565b950151945f611266565b8580fd5b3d91506114dc565b6040513d88823e3d90fd5b633999656760e01b8552600485fd5b508115611201565b63ccea9e6f60e01b8552600485fd5b50838552601560209081526040808720335f908152925290205460ff16156111d5565b611598604051611577606082612862565b6002815260403660208301378561158d82612a00565b52846107ea82612a21565b6111cb576282b42960e81b8552600485fd5b503461037d578060031936011261037d57600854601354604051638304ff8f60e01b8152306004820152919290602090839060249082906001600160a01b03165afa918215611668578192611630575b5050816116105750506020601454604051908152f35b60209161162261162b92601154612cb7565b601454906138f4565b610431565b9091506020813d602011611660575b8161164c60209383612862565b8101031261165c5751905f6115fa565b5f80fd5b3d915061163f565b50604051903d90823e3d90fd5b503461037d57602036600319011261037d576020906040906001600160a01b0361169d612779565b168152600683522054604051908152f35b503461037d57604036600319011261037d5760206104316116cd61278f565b3390600435612a9a565b503461037d57602036600319011261037d576116f1612779565b6013546001600160a01b031633036103f95760ff60105416600381101561178a5760010361177b576001600160a01b0316801561176c57600e80546001600160a01b0319811683179091556001600160a01b03167feeb293e1f8f3a9db91ade748726387ed1352ca78f5430c5f06fe3d1e1ad505798380a380f35b63d92e233d60e01b8252600482fd5b63fb61026d60e01b8252600482fd5b634e487b7160e01b83526021600452602483fd5b503461037d578060031936011261037d5760ff6010541660405190600381101561178a57602092508152f35b503461037d578060031936011261037d57602060ff601654166040519015158152f35b503461037d57604036600319011261037d57611807612779565b60243580151591828203610a99576001600160a01b0316923384146118885790604061185192338152601560205220845f5260205260405f209060ff801983541691151516179055565b6040519081527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa26760203392a3602060405160018152f35b63ccea9e6f60e01b8152600490fd5b6127b9565b503461037d57602036600319011261037d5760206104316105d9612779565b503461037d57602036600319011261037d576013546001600160a01b03163303610632576040610c036118ec6136e6565b6118f4612ce5565b90600435613776565b503461037d57604036600319011261037d576004356001600160401b03811161103d5761192e9036906004016128d4565b6024356001600160401b038111610a995761194d9036906004016128d4565b601354919290916001600160a01b031633036107aa5760ff601054166003811015611a5157600203611a4257818103611a3357845b81811061198d578580f35b806119dc6119a66119a160019487896136b5565b6136c5565b838060a01b036119bf6119ba85888c6136b5565b6136d2565b168952600b602052604089209060ff801983541691151516179055565b6119ea6119ba8285896136b5565b7fc5217903bd71390e046be1c1ee589f3af533c8fe5b67ee0a7dade2f8a853badd6020611a1b6119a185898b6136b5565b926040519315158452858060a01b031692a201611982565b63512509d360e11b8552600485fd5b63fb61026d60e01b8552600485fd5b634e487b7160e01b86526021600452602486fd5b503461037d578060031936011261037d5760206040516127108152f35b503461037d578060031936011261037d576040517f000000000000000000000000754704bc059f8c67012fed69bc8a327a5aafb6036001600160a01b03168152602090f35b503461037d57602036600319011261037d576004356001600160401b03811161103d5760a0600319823603011261103d5760405191611b0583612847565b81600401356001600160401b03811161103d578201903660238301121561037d57602061043185608486611b4136600489013560248a01612c0d565b8352611b4f602482016127a5565b85840152611b5f604482016127a5565b60408401526064810135606084015201356080820152613607565b503461037d578060031936011261037d57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000006168152f35b503461037d576020610431611bcc3661289a565b91612a9a565b503461037d57604036600319011261037d57611bec612779565b611bf461278f565b6001600160a01b03818116929081169190838303611d9657611c1636826132ee565b156107aa575b611c258161347c565b828552601260205260096040862001805415611d875760135460405163014f3df960e71b81523060048201526001600160a01b0384811660248301529091602091839160449183918c91165af1908115611d7c578791611d3e575b5015611d2f57611cb291610ce9879286845260036020526001600160801b036040852054169581549485925530612eb6565b5060135484906001600160a01b0316803b1561103d5781809160046040518094819363a022985f60e01b83525af1611d1a575b505060407f91f628e8c7a6f75792ac4b71145861594f59fa4439e803548a394011b6c6bc2b918151908152336020820152a380f35b81611d2491612862565b610a9957835f611ce5565b6331a852bb60e01b8652600486fd5b90506020813d602011611d74575b81611d5960209383612862565b81010312611d7057611d6a90612a8d565b5f611c80565b8680fd5b3d9150611d4c565b6040513d89823e3d90fd5b636040212f60e11b8652600486fd5b611da7604051611577606082612862565b611c1c576282b42960e81b8552600485fd5b503461037d57604036600319011261037d57600435611dd661278f565b6013546001600160a01b0316330361036f5760208161035b847f8dd4664ff532261941b00d8312d1764002e0de40af594821e7a4c08e4f0b8c7094612f76565b503461037d57602036600319011261037d5760043580151580910361103d576013546001600160a01b031633036103f95760407f304c943633ec0da73778a23e6ffc08756b2e7a7e606e52ff88cf83933d95342f916016549060ff811660ff1983161760165560ff83519216151582526020820152a180f35b503461037d57611e9e366127c2565b9160ff6016541615611fe157611eb436836132ee565b15611fd35760135484906001600160a01b0316803b1561103d57816040518092630987df0360e01b8252818381611ef18b8b8b30600486016129bd565b03925af1801561078c57611fbe575b50506001600160a01b038116808552600760209081526040808720335f90815292529020549060018201611f3b575b5050610a6e9350612eb6565b848210611fa3578015611f8f573315611f7b5785604091610a6e975260076020522060018060a01b0333165f526020528360405f20910390555f80611f2f565b634a1406b160e11b86526004869052602486fd5b63e602df0560e01b86526004869052602486fd5b6064868684637dc7a0d960e11b835233600452602452604452fd5b81611fc891612862565b610a9957835f611f00565b6282b42960e81b8452600484fd5b636dbb04b560e01b8452600484fd5b503461037d578060031936011261037d57600e546040516001600160a01b039091168152602090f35b503461037d57602036600319011261037d57600435906001600160401b03821161037d573660238301121561037d57816004013561205681612883565b906120646040519283612862565b8082526024602083019160051b8501019236841161037d5750602460209401905b8382106120a0578461209684612a45565b6040519015158152f35b8480916120ac846127a5565b815201910190612085565b503461037d57602036600319011261037d57604080916120d5612779565b6120dd6129e8565b506001600160a01b0316815260036020908152828220546001600160801b031682526002905220815190612110826127fc565b80548083526001909101546001600160a01b0390811660209384019081528451928352511691810191909152f35b503461037d57602036600319011261037d5760406101409161215e612779565b81610120845161216d8161282b565b82815282602082015282868201528260608201528260808201528260a08201528260c08201528260e082015282610100820152015260018060a01b031681526012602052206040516121be8161282b565b81549182825260018101546020830190815260028201546040840190815260038301546060850190815260048401546080860190815260058501549160a0870192835260068601549360c0880194855260078701549560e08901968752610120600960088a0154996101008c019a8b5201549901988952604051998a525160208a01525160408901525160608801525160808701525160a08601525160c08501525160e08401525161010083015251610120820152f35b503461037d578060031936011261037d576020600854604051908152f35b503461165c576122a2366127c2565b6013549293929091906001600160a01b03163381900361232157803b1561165c575f6040518092630987df0360e01b82528183816122e689898d30600486016129bd565b03925af1801561231657612300575b506103f69293612eb6565b6103f693505f61230f91612862565b5f926122f5565b6040513d5f823e3d90fd5b6282b42960e81b5f5260045ffd5b3461165c57604036600319011261165c57612348612779565b6024359033156123c4576001600160a01b03169081156123b157335f52600760205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b634a1406b160e11b5f525f60045260245ffd5b63e602df0560e01b5f525f60045260245ffd5b3461165c57602036600319011261165c576123f0612ce5565b600435905f905f925f5493600154926001600160801b0384169360801c94600454905b84151580612530575b8061251d575b1561250d576001600160801b0386165f52600260205260405f209161246d89856040519561244f876127fc565b80548088526001909101546001600160a01b03166020880152613cf9565b918683116124b3579161249e816124986124a4946124906124aa97895190612cd8565b975190612cb7565b97612cd8565b96612cb7565b95612e99565b94939290612413565b9750508793955082946124d5939250604098918082105f14610c2d5750613cf9565b91826124eb575b50505082519182526020820152f35b90610c1d916124ff84612505969597612cd8565b95613cf9565b8380806124dc565b9550505091505060409250610c03565b50866001600160801b0387161115612422565b5081151561241c565b3461165c57602036600319011261165c576020610431612557612ce5565b601454906004356138f4565b3461165c575f36600319011261165c576040515f60095461258381612904565b808452906001811690811561260957506001146125ab575b610d8a83610d7681850382612862565b60095f9081527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af939250905b8082106125ef57509091508101602001610d7661259b565b9192600181602092548385880101520191019092916125d7565b60ff191660208086019190915291151560051b84019091019150610d76905061259b565b3461165c57602036600319011261165c5760043563ffffffff60e01b811680910361165c57806020911590811561272a575b8115612719575b8115612708575b81156126f7575b81156126e6575b81156126d5575b81156126c4575b81156126b3575b81156126a2575b506040519015158152f35b63a219a02560e01b14905082612697565b6342fb809b60e11b81149150612690565b6336372b0760e01b81149150612689565b63e3bc4e6560e01b81149150612682565b63043eff2d60e51b8114915061267b565b6364ec41f760e01b81149150612674565b6305a0eefb60e31b8114915061266d565b631883ba3960e21b81149150612666565b6301ffc9a760e01b8114915061265f565b3461165c575f36600319011261165c576020906011548152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361165c57565b602435906001600160a01b038216820361165c57565b35906001600160a01b038216820361165c57565b3461165c575f80fd5b606090600319011261165c576004356001600160a01b038116810361165c57906024356001600160a01b038116810361165c579060443590565b604081019081106001600160401b0382111761281757604052565b634e487b7160e01b5f52604160045260245ffd5b61014081019081106001600160401b0382111761281757604052565b60a081019081106001600160401b0382111761281757604052565b90601f801991011681019081106001600160401b0382111761281757604052565b6001600160401b0381116128175760051b60200190565b606090600319011261165c57600435906024356001600160a01b038116810361165c57906044356001600160a01b038116810361165c5790565b9181601f8401121561165c578235916001600160401b03831161165c576020808501948460051b01011161165c57565b90600182811c92168015612932575b602083101461291e57565b634e487b7160e01b5f52602260045260245ffd5b91607f1691612913565b5f929181549161294b83612904565b80835292600181169081156129a0575060011461296757505050565b5f9081526020812093945091925b838310612986575060209250010190565b600181602092949394548385870101520191019190612975565b915050602093945060ff929192191683830152151560051b010190565b6001600160a01b03918216815291811660208301529091166040820152606081019190915260800190565b604051906129f5826127fc565b5f6020838281520152565b805115612a0d5760200190565b634e487b7160e01b5f52603260045260245ffd5b805160011015612a0d5760400190565b8051821015612a0d5760209160051b010190565b5f5b8151811015612a86576001600160a01b03612a628284612a31565b51165f52600b60205260ff60405f20541615612a8057600101612a47565b50505f90565b5050600190565b5190811515820361165c57565b90916001600160a01b03808416929082169190838303612ba557612abe36836132ee565b15612321575b612acd81613590565b612ad68261347c565b601354604051632fab172760e11b81529060209082906001600160a01b0316815f81612b08898d8a3060048601612bc8565b03925af1908115612316575f91612b6f575b5090612b60817fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d794612b5b612b54604096601454856138f4565b809a6135a9565b612f76565b8151908152856020820152a390565b9190506020823d602011612b9d575b81612b8b60209383612862565b8101031261165c579051612b60612b1a565b3d9150612b7e565b612bb6604051611577606082612862565b612ac4576282b42960e81b5f5260045ffd5b6001600160a01b039182168152602081019290925291821660408201529116606082015260800190565b6001600160401b03811161281757601f01601f191660200190565b929192612c1982612bf2565b91612c276040519384612862565b82948184528183011161165c578281602093845f960137010152565b601354604051630760168760e41b81523060048201526001600160a01b0392831660248201529160209183916044918391165afa908115612316575f91612c88575090565b90506020813d602011612caf575b81612ca360209383612862565b8101031261165c575190565b3d9150612c96565b91908203918211612cc457565b634e487b7160e01b5f52601160045260245ffd5b91908201809211612cc457565b601354604051639b9b942160e01b815230600482015290602090829060249082906001600160a01b03165afa908115612316575f91612c88575090565b60018060a01b03165f52601260205260405f20906003600183015492015415612d4757565b5f9150565b6001600160a01b03165f90815260126020526040902060030154908115612d4757565b916001600160a01b03828116929082169190838303612e7a57612d9236836132ee565b1561232157612ddd915b612da586613590565b612dae8161347c565b60208160018060a01b0360135416845f8a604051988995869485936309daab9960e11b85523060048601612bc8565b03925af180156123165786935f91612e42575b506040927fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d79492612b5b612e2b612e339460145490876138ff565b998a936135a9565b8151908682526020820152a390565b9350506020833d602011612e72575b81612e5e60209383612862565b8101031261165c5791518592906040612df0565b3d9150612e51565b612e8b604051611577606082612862565b1561232157612ddd91612d9c565b6001600160801b03166001600160801b038114612cc45760010190565b6001600160a01b0316908115612f63576001600160a01b0316918215612f5057815f52600660205260405f2054818110612f3757817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92602092855f52600684520360405f2055845f526006825260405f20818154019055604051908152a3565b8263391434e360e21b5f5260045260245260445260645ffd5b63ec442f0560e01b5f525f60045260245ffd5b634b637e8f60e11b5f525f60045260245ffd5b612feb91612f8681601154612cd8565b6011556040516323b872dd60e01b60208201526001600160a01b0390921660248301523060448301526064808301919091528152612fc5608482612862565b7f000000000000000000000000754704bc059f8c67012fed69bc8a327a5aafb603613eef565b565b60105460ff165f60038210156132da576001821461302257600292935050146130165750600190565b61301f90612a45565b90565b5050600e546001600160a01b03169081156132cb578051156132bc576061831061329e5760405192608084018481106001600160401b03821117612817576040526060845260208401915f835260408501945f865260608101915f8352601f19840192848411612cc457602093358082528251036132ad5751604181029080820460411490151715612cc45780840190818511612cc457604001809111612cc45780851061329e576130d48186612cb7565b9085821161165c576130eb6130f39236905f612c0d565b845285612cb7565b83810190818111612cc45785821161165c573586528752845143101561328f575f5b8151811015613282576001600160a01b036131308284612a31565b511615613273576001600160a01b036131498284612a31565b5116805f52600f855260405f209081549161316383613daa565b9055613196855189516040519161317983612847565b825230898301528360408301528460608301526080820152613607565b917fe551f1469c2895fdd376c4517ff8a4cadba06443acbd1980104b6e27e2e11646895191875192604051914683528a830152604082015260806060820152806131e530946080830190612755565b0390a388516041830283810460411484151715612cc45761320591612cd8565b90895160018401808511612cc457604181029080820460411490151715612cc45761322f91612cd8565b80831161165c5787811161165c578280613250926132579550033691612c0d565b9089613f62565b1561326457600101613115565b638baa579f60e01b5f5260045ffd5b63d92e233d60e01b5f5260045ffd5b5050505050505050600190565b63b7ca31cd60e01b5f5260045ffd5b636cf4aa4f60e11b5f5260045ffd5b63512509d360e11b5f5260045ffd5b630b6e0c5f60e11b5f5260045ffd5b632057875960e21b5f5260045ffd5b634e487b7160e01b5f52602160045260245ffd5b5f9160ff601054169060038210156132da57600182146133325750600214613317575050600190565b6001600160a01b03168152600b602052604090205460ff1690565b600e546001600160a01b03938416945090921691905082156132735781156132cb576061811061329e5760618103818111612cc45781811161165c57818111612cc4576040198201828111612cc45780821161165c576133988284036040190183613d8e565b92811161165c578243101561328f578461346c92613472965f52600f6020527fe551f1469c2895fdd376c4517ff8a4cadba06443acbd1980104b6e27e2e1164660405f208054906133e882613daa565b90556134236040516133f981612847565b61340436865f612c0d565b8152306020820152856040820152826060820152886080820152613607565b96604051914683526020830152604082015260806060820152826080820152825f60a08301375f60a0848301015260a0813094601f80199101168101030190a360413691612c0d565b91613f62565b1561326457600190565b60018060a01b03163381141590816134a5575b5061349657565b6336abb4df60e11b5f5260045ffd5b5f90815260156020908152604080832033845290915281205460ff1615915061348f565b6001600160a01b03165f818152600360205260409020549192916001600160801b0316801561358157805f52600260205260405f20548085118015613579575b61356a5761351a8561352392612cb7565b94600454612cb7565b60045583613559575f5260026020525f60016040822082815501555f52600360205260405f206001600160801b03198154169055565b90505f5260026020528160405f2055565b633999656760e01b5f5260045ffd5b508015613509565b6333618a9160e21b5f5260045ffd5b6127101161359a57565b632ca2f52b60e11b5f5260045ffd5b6001600160a01b0316908115612f50577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef6020826135ea5f94600854612cd8565b6008558484526006825260408420818154019055604051908152a3565b6042908051602081519101209060018060a01b036020820151169060018060a01b036040820151169060806060820151910151916040519360208501957fb78a071cab05828812909bd43bd7019c31f7c50fbdf8657efd4932bbe287e16d875260408601526060850152608084015260a083015260c082015260c0815261368f60e082612862565b51902061369a61402f565b906040519161190160f01b8352600283015260228201522090565b9190811015612a0d5760051b0190565b35801515810361165c5790565b356001600160a01b038116810361165c5790565b601354604051638304ff8f60e01b815230600482015290602090829060249082906001600160a01b03165afa8015612316575f90613742575b61372d915060055490612cd8565b6011549080821115612a805761301f91612cb7565b506020813d60201161376e575b8161375c60209383612862565b8101031261165c5761372d905161371f565b3d915061374f565b91925f5f9460045490851580156138ec575b6138df575f54600154966001600160801b0388169760801c908181115f146138d3575096979395975b6001600160801b0381168881116138bb575f52600260205260405f206137ff8385878d604051956137e1876127fc565b80548752600101546001600160a01b03166020870190815295613db8565b999092821561387e5792613830836138369361382b8a859761383c9960018060a01b039051168b61393d565b612cd8565b98612cd8565b9a612cb7565b96156138575761384b90612e99565b965b96979395976137b1565b939796505050506001600160801b039192505b166001600160801b03196001541617600155565b509a9594939297989150509794976138a35750505050506001600160801b039061386a565b90919295946138b59098949798612e99565b9661384d565b509396505050506001600160801b039192945061386a565b905096979395976137b1565b505050925050505f905f90565b508115613788565b9061301f9291613cf9565b919061390c828285613cf9565b928215613929570961391b5790565b60018101809111612cc45790565b634e487b7160e01b5f52601260045260245ffd5b92919060018060a01b031691825f52601260205260405f2090600982019081548015613a1857841161359a5760145461397691856138f4565b9461397f6136e6565b8611613a09577f4dd5187225a2ae5f5ea35ca7b1732180f848cc4b6f7dce34b4c5e9f384d77dec9360409360076139f76001600160801b03956139c48b600554612cd8565b600555600184016139d68c8254612cd8565b80915560038501916139e9878454612cd8565b809355611344878254612cb7565b910155835196875260208701521693a3565b63bb55fd2760e01b5f5260045ffd5b634ef1d5c560e01b5f5260045ffd5b9091906001600160a01b03168015612f6357805f52600660205260405f2054838110613a93576020845f94957fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef93858752600684520360408620558060085403600855604051908152a3565b915063391434e360e21b5f5260045260245260445260645ffd5b6005549081811015613ac757613ac291612cb7565b600555565b50505f600555565b9291936001840194600386549501948086549411613b3b57839180821115613b4a57613afa91612cb7565b935b84875511613b3b5780821115613b3357613b1591612cb7565b825515613b2b575b5415613b265750565b5f9055565b5f8155613b1d565b50505f613b15565b632206bead60e21b5f5260045ffd5b50505f93613afc565b90612fc5612feb92613b6783601154612cb7565b60115560405163a9059cbb60e01b60208201526001600160a01b039091166024820152604481019290925281606481015b03601f198101835282612862565b919290925f9083158015613cef575b613ce6575f949193945493600154946001600160801b0386169560801c955b87151580613cd3575b15613cbd576001600160801b0381165f52600260205260405f20613c0b83868b89604051956137e1876127fc565b959091928315613c845791613c3881613c3e9361382b8b88613c44999860018060a01b039051168b61393d565b98612cb7565b99612cb7565b9215613c5f57613c5390612e99565b955b9596939196613bd4565b9396505050506001600160801b03919250166001600160801b03196001541617600155565b509895949150509791909497613ca957505050506001600160801b039192935061386a565b9091929593613cb790612e99565b95613c55565b9350509450506001600160801b0391925061386a565b50866001600160801b0382161115613bdd565b50509150505f90565b5060045415613bb5565b9091828202915f1984820993838086109503948086039514613d805784831115613d7157829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b63227bc15360e01b5f5260045ffd5b505080925015613929570490565b359060208110613d9c575090565b5f199060200360031b1b1690565b5f198114612cc45760010190565b939491909294602085019360018060a01b0385511615613e3f578682613ddd92613cf9565b91855192838110613e37575b50828110613e2f575b508115613e225790613e09613e1f92829783613cf9565b94519351938114936001600160a01b03166134c9565b50565b505f945084935083925050565b91505f613df2565b92505f613de9565b505f9550859450600193505050565b600411156132da57565b60ff8114613e9e5760ff811690601f8211613e8f5760405191613e7c604084612862565b6020808452838101919036833783525290565b632cd44ac360e21b5f5260045ffd5b5060405161301f81613eb181600c61293c565b0382612862565b60ff8114613edc5760ff811690601f8211613e8f5760405191613e7c604084612862565b5060405161301f81613eb181600d61293c565b5f80613f179260018060a01b03169360208151910182865af1613f10614185565b9083614236565b8051908115159182613f3f575b5050613f2d5750565b635274afe760e01b5f5260045260245ffd5b819250906020918101031261165c576020613f5a9101612a8d565b155f80613f24565b90613f6d838261414b565b50909390613f7a81613e4e565b159384614019575b508315613f90575b50505090565b5f935090613b98613fc88594936040519283916020830195630b135d3f60e11b87526024840152604060448401526064830190612755565b51915afa613fd4614185565b8161400b575b81613fe9575b505f8080613f8a565b905060208180518101031261165c5760200151630b135d3f60e11b145f613fe0565b905060208151101590613fda565b6001600160a01b0384811691161493505f613f82565b307f0000000000000000000000003c6d09eb0e55495f7541f78fbdcdcb53cead5f936001600160a01b03161480614122575b1561408a577f8b69edcc5e86f08803fc96688930c6ef172cf079de3083b3e6a2b48d0da4bfd790565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527ff667a733ae93d1eed7f6346974ef74641f1bc18159f9cf208fa8e710699fc3a460408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815261411c60c082612862565b51902090565b507f000000000000000000000000000000000000000000000000000000000000008f4614614061565b815191906041830361417b576141749250602082015190606060408401519301515f1a906141b4565b9192909190565b50505f9160029190565b3d156141af573d9061419682612bf2565b916141a46040519384612862565b82523d5f602084013e565b606090565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161422b579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15612316575f516001600160a01b0381161561422157905f905f90565b505f906001905f90565b5050505f9160039190565b9061425a575080511561424b57805190602001fd5b630a12f52160e11b5f5260045ffd5b8151158061428b575b61426b575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561426356fea2646970667358221220e39e9ad2cb2fbd0b267a0000e0e1432ec3ada7e1a021a8bb5287c20e41c3612264736f6c634300081b0033

Deployed Bytecode Sourcemap

1428:13276:31:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;:::i;:::-;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;1428:13276:31;7648:6:32;;;7670:31;7648:6;;:::i;:::-;1428:13276:31;;;;;-1:-1:-1;;;;;1428:13276:31;;7670:31:32;1428:13276:31;;2328:83:32;-1:-1:-1;;;2386:14:32;;1428:13276:31;2386:14:32;;1428:13276:31;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;5238:45;;1428:13276;;-1:-1:-1;;;;;1428:13276:31;;:::i;:::-;;;;5238:12;1428:13276;;;5238:45;1428:13276;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;7852:6;1428:13276:31;;;7852:6:32;;:::i;:::-;1428:13276:31;;2328:83:32;-1:-1:-1;;;2386:14:32;;1428:13276:31;2386:14:32;;1428:13276:31;;;;;;;-1:-1:-1;;1428:13276:31;;;;;6799:41:32;5674:12;;:::i;:::-;6813:9;1428:13276:31;;;6799:41:32;:::i;:::-;1428:13276:31;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;5464:21;1428:13276;;:::i;:::-;5464:21;:::i;1428:13276::-;;;;;;;;;;;;;;1569:6:33;1428:13276:31;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;3952:11:4;1428:13276:31;;;3952:27:4;1428:13276:31;;;;;;-1:-1:-1;1428:13276:31;;;;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3486:14;1428:13276;;;:::i;:::-;3486:14;;:::i;1428:13276::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;:::i;:::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;:::i;:::-;;;;636:55:23;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;1963:24:32;1428:13276:31;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;6799:41:32;12471:22:31;1428:13276;;:::i;:::-;12471:22;:::i;:::-;12495:12;;:::i;:::-;1428:13276;6813:9:32;1428:13276:31;6799:41:32;;:::i;1428:13276:31:-;;;;;;;-1:-1:-1;;1428:13276:31;;;;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;1428:13276:31;9726:57;9756:12;;:::i;:::-;9770;;:::i;:::-;1428:13276;;;9726:57;:::i;2328:83:32:-;-1:-1:-1;;;2386:14:32;;1428:13276:31;;2386:14:32;1428:13276:31;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;2585:27:32;;;1428:13276:31;;2633:33:32;1428:13276:31;2633:33:32;;:::i;:::-;2632:34;2628:61;;2581:348;5714:6:31;;;:::i;:::-;5748:10;;;:::i;:::-;5782:21;;;:::i;:::-;5773:30;;5769:61;;1428:13276;;;5868:12;1428:13276;;;;;5924:17;;;1428:13276;;6503:41:32;6524:9;1428:13276:31;6503:41:32;;;;;:::i;:::-;;;;:::i;:::-;6115:8:31;1428:13276;;;;-1:-1:-1;;;;;1428:13276:31;6115:62;;;;;;1428:13276;;;;;;;;;;;;;;;;6115:62;;6141:4;;1428:13276;6115:62;;;:::i;:::-;;;;;;;;;;;2581:348:32;6213:6:31;;1428:13276;6213:6;6282;6213;;6358;6213;;;;;;;:::i;:::-;6282;:::i;:::-;6321;6141:4;;6321:6;:::i;:::-;6358;:::i;:::-;1428:13276;;;;;;;;;;6381:58;1428:13276;6390:10;6381:58;;1428:13276;;;;;;6115:62;;;;;;:::i;:::-;1428:13276;;6115:62;;;;1428:13276;;;;;;;;;6115:62;1428:13276;;;5769:61;-1:-1:-1;;;5812:18:31;;1428:13276;6770:18;5812;2628:61:32;-1:-1:-1;;;2675:14:32;;1428:13276:31;2386:14:32;2675;2581:348;2863:32;1428:13276:31;;;;;;:::i;:::-;2762:1:32;1428:13276:31;;;;;;;;2778:27:32;;;;:::i;:::-;1428:13276:31;2819:24:32;;;;:::i;:::-;1428:13276:31;;2863:32:32;;:::i;:::-;2581:348;2858:60;-1:-1:-1;;;2904:14:32;;1428:13276:31;2386:14:32;2904;1428:13276:31;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;2065:62:32;1428:13276:31;;;2065:62:32;1428:13276:31;;;;;;-1:-1:-1;1428:13276:31;;;;;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;2585:27:32;;;1428:13276:31;;2633:33:32;1428:13276:31;2633:33:32;;:::i;:::-;2632:34;2628:61;;2581:348;6670:6:31;;;:::i;:::-;6704:10;;;:::i;:::-;6738:23;;;:::i;:::-;6729:32;;6725:63;;1428:13276;;;6826:12;1428:13276;;;;;6884:19;;;1428:13276;;6799:41:32;6813:9;1428:13276:31;6799:41:32;;;;;;:::i;:::-;7081:8:31;1428:13276;;;;-1:-1:-1;;;;;1428:13276:31;7081:64;;;;;;1428:13276;;;;;;;;;;;;;;;;7081:64;;7109:4;;1428:13276;7081:64;;;:::i;:::-;;;;;;;;;;;2581:348:32;7181:6:31;;1428:13276;7181:6;7248:8;7326:6;7181;;;;;;;;;:::i;7248:8::-;7289:6;7109:4;;7289:6;:::i;7326:::-;1428:13276;;;;;;;;;;7349:58;1428:13276;7358:10;7349:58;;1428:13276;;;;;;7081:64;;;;;;:::i;:::-;1428:13276;;7081:64;;;2581:348:32;2863:32;1428:13276:31;;;;;;:::i;2863:32:32:-;2581:348;2858:60;-1:-1:-1;;;2904:14:32;;1428:13276:31;2386:14:32;2904;1428:13276:31;;;;;;;-1:-1:-1;;1428:13276:31;;;;;6503:41:32;13555:12:31;;:::i;:::-;6524:9:32;1428:13276:31;;;;6503:41:32;:::i;1428:13276:31:-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;;;;;5406:18:32;1428:13276:31;;5405:19:32;5401:55;;5471:25;1428:13276:31;5471:25:32;;:::i;:::-;5470:26;5466:53;;4781:8;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;4781:58:32;;;;;1428:13276:31;;;;;;;;4781:58:32;;4756:10;;;4781:58;4756:10;;;4809:4;1428:13276:31;4781:58:32;;;:::i;:::-;;;;;;;;;;;1428:13276:31;3754:5:4;4756:10:32;;;3754:5:4;:::i;:::-;1428:13276:31;;;;;;;4781:58:32;;;;;;:::i;:::-;1428:13276:31;;4781:58:32;;;;1428:13276:31;;;;;;;;;4781:58:32;1428:13276:31;;;5401:55:32;-1:-1:-1;;;5433:23:32;;1428:13276:31;5433:23:32;;1428:13276:31;;;;;;;;;;;;;;;;7117:4:32;1428:13276:31;;;;;;;;;;;;;;;;1872:35:32;1428:13276:31;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;10640:12;;:::i;:::-;1428:13276;;9048:20:33;9070:18;1428:13276:31;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;9310:862:33;;9317:13;;;:36;;;9310:862;9317:72;;;9310:862;9317:72;;;-1:-1:-1;;;;;1428:13276:31;;;;;;9441:15:33;1428:13276:31;;;;;;9510:48:33;1428:13276:31;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;9510:48:33;:::i;:::-;9577:25;;;;9573:412;;9999:27;;;;10081:34;9999:27;;10040:26;9999:27;;:::i;:::-;10040:26;;:::i;:::-;1428:13276:31;;10081:34:33;;:::i;:::-;10145:16;-1:-1:-1;;;;;10145:16:33;;;:::i;:::-;1428:13276:31;;9310:862:33;;;;;;;;9573:412;2641:13:19;;;;1428:13276:31;2641:13:19;;;;;;;;;;9717:45:33;2641:13:19;;;:5;;;;:13;:5;;;:13;9717:45:33;:::i;:::-;9785:13;;9781:167;;2641:13:19;9965:5:33;;;;;;9310:862;1428:13276:31;;;;;;;;;;9781:167:33;9873:56;9822:29;;9887:42;9822:29;9887:42;:::i;:::-;9873:56;;:::i;:::-;9781:167;;;;;;;2641:13:19;;;9717:45:33;:::i;9317:72::-;;;;;;;;;1428:13276:31;9317:72:33;;;;;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;;9357:32:33;;9317:72;;:36;9334:19;;;;9317:36;;1428:13276:31;;;;;;;;;;;;;;1239:32:33;1428:13276:31;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;2354:8:32;1428:13276:31;;;;;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;-1:-1:-1;;;;;1428:13276:31;;;;9452:11;1428:13276;;;;;;9520:27;;1428:13276;;9497:12;;-1:-1:-1;;;;;1428:13276:31;;;9497:12;;:::i;:::-;;;:::i;:::-;9520:27;:::i;:::-;;1428:13276;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;8006:6;1428:13276:31;;;8006:6:32;;:::i;1428:13276:31:-;;;;;;;;;;;;;;;;;2355:7:4;1428:13276:31;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;2355:7:4;1428:13276:31;;;;;-1:-1:-1;1428:13276:31;;;;;;;-1:-1:-1;1428:13276:31;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1428:13276:31;;-1:-1:-1;1428:13276:31;;-1:-1:-1;1428:13276:31;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;3486:14;1428:13276;;:::i;:::-;4040:10;1428:13276;;;3486:14;:::i;1428:13276::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;2056:10:33;;:25;;;;1428:13276:31;2052:52:33;;2124:6;1428:13276:31;;;2118:26:33;;2114:52;;1428:13276:31;;;-1:-1:-1;;;;;1428:13276:31;;;;-1:-1:-1;;;;;2124:6:33;2191:15;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;;;;-1:-1:-1;;;;;1428:13276:31;;;;:::i;:::-;;;;;;;:::i;:::-;;;;-1:-1:-1;;1428:13276:31;;;:::i;:::-;;;;;;;;;2271:13:33;;;;-1:-1:-1;;;;;1428:13276:31;;2286:7:33;;;;;;1428:13276:31;;;-1:-1:-1;;;;;1428:13276:31;;;;;2310:40:33;2124:6;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;;;;;2324:15:33;1428:13276:31;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;2310:40:33;;;;:::i;:::-;;;;:::i;:::-;;1428:13276:31;;2271:13:33;;1428:13276:31;-1:-1:-1;;;1428:13276:31;;;;;;;;2286:7:33;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;-1:-1:-1;1428:13276:31;;;;;;;;;2124:6:33;1428:13276:31;;;;;;;;;;;:::i;:::-;-1:-1:-1;1428:13276:31;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;-1:-1:-1;;;1428:13276:31;;;;;;;;2114:52:33;-1:-1:-1;;;2153:13:33;;1428:13276:31;2153:13:33;;2052:52;-1:-1:-1;;;2090:14:33;;1428:13276:31;2090:14:33;;2056:25;2070:11;;;;2056:25;;1428:13276:31;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;6099:5:15;:41;:5;:41;:::i;:::-;6554:8;:47;:8;:47;:::i;:::-;1428:13276:31;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;5590:13:15;;1428:13276:31;;;;5625:4:15;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;:::i;:::-;;;;1577:52:22;1428:13276:31;;;;;;;;;;;;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;2585:27:32;;;1428:13276:31;;2633:33:32;1428:13276:31;2633:33:32;;:::i;:::-;2632:34;2628:61;;2581:348;9851:10;9842:19;;;:53;;;2581:348;9838:108;;4320:10:31;;;:::i;:::-;4357:6;;;:::i;:::-;1428:13276;;;3390:9:4;1428:13276:31;;;;;;;10543:25:32;:40;;;;2581:348;10539:98;;1428:13276:31;;;;;4556:66;1428:13276;;4439:12;1428:13276;;4474:26;1428:13276;;;4474:26;:36;1428:13276;;;4474:36;:::i;:::-;1428:13276;;;;;;;4556:8;1428:13276;;;;;;;;;;;;;;;4556:66;;4589:4;;1428:13276;4556:66;;;:::i;:::-;;;;;;;;;;;1428:13276;;4556:66;;;2581:348:32;-1:-1:-1;4632:221:31;;;4662:30;1428:13276;;;;4439:12;1428:13276;;;;;4474:26;11288;;1428:13276;;;11329:26;;11325:56;;11395:30;-1:-1:-1;11391:63:31;;6524:9:32;1428:13276:31;6503:41:32;;;;:::i;:::-;11557:12:31;;;:::i;:::-;11548:21;;11544:57;;1428:13276;5000:63;1428:13276;;;;12051:54;1428:13276;;;;;;;11856:19;11878:75;4895:6;1428:13276;11612:27;5000:63;1428:13276;11612:27;1428:13276;11612:27;:::i;:::-;;1428:13276;11649:17;;;:27;1428:13276;;;11649:27;:::i;:::-;1428:13276;;;11686:18;;;1428:13276;11686:28;1428:13276;;;11686:28;:::i;:::-;1428:13276;;;11724:36;1428:13276;;;11724:36;:::i;:::-;1428:13276;;11791:55;1428:13276;6524:9:32;1428:13276:31;11791:55;;:::i;:::-;3390:9:4;11771:17:31;;1428:13276;6524:9:32;1428:13276:31;11878:75;;:::i;:::-;11856:19;;1428:13276;;;;;;;;;;;;12051:54;4632:221;4589:4;4895:6;;:::i;:::-;1428:13276;;;9851:10:32;1428:13276:31;;1624:1;;;1428:13276;;;;;;;;1624:1;;;;5000:63;;;;1428:13276;;;;;;11544:57;-1:-1:-1;;;11578:23:31;;1428:13276;11578:23;;11391:63;-1:-1:-1;;;11434:20:31;;1428:13276;11434:20;;11325:56;-1:-1:-1;;;11364:17:31;;1428:13276;11364:17;;4632:221;4817:25;;;;;5000:63;;4817:25;4895:6;-1:-1:-1;;;;;4817:25:31;1428:13276;4817:25;2559:17:33;1428:13276:31;;;2609:11:33;1428:13276:31;;;;;;;;2646:15:33;;2642:321;1428:13276:31;;;;2691:6:33;1428:13276:31;2691:6:33;2689:22;1428:13276:31;;;2689:22:33;:::i;:::-;1428:13276:31;;;;;;;;;;;;;;;;;;;;2609:11:33;1428:13276:31;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;2804:37:33;;;1428:13276:31;;;;;;;2775:15:33;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;2642:321:33;2973:27;1428:13276:31;;;2973:27:33;:::i;:::-;1428:13276:31;;;4632:221;;;;;2642:321:33;2872:22;1428:13276:31;2872:22:33;;1428:13276:31;;2908:15:33;1428:13276:31;;;2908:44:33;1428:13276:31;;;2908:44:33;:::i;:::-;1428:13276:31;;2642:321:33;;4556:66:31;;;;1428:13276;4556:66;;1428:13276;4556:66;;;;;;1428:13276;4556:66;;;:::i;:::-;;;1428:13276;;;;;;;;:::i;:::-;;;;4556:66;;;;1428:13276;;;;4556:66;;;-1:-1:-1;4556:66:31;;;1428:13276;;;;;;;;;10539:98:32;-1:-1:-1;;;10606:20:32;;1428:13276:31;10606:20:32;;10543:40;10572:11;;;10543:40;;9838:108;-1:-1:-1;;;9918:17:32;;1428:13276:31;3999:17:32;9918;9842:53;-1:-1:-1;1428:13276:31;;;9866:10:32;1428:13276:31;;;;;;;;9851:10:32;-1:-1:-1;1428:13276:31;;;;;;;;;;9865:30:32;9842:53;;2581:348;2863:32;1428:13276:31;;;;;;:::i;:::-;2762:1:32;1428:13276:31;;;;;;;;2778:27:32;;;;:::i;:::-;1428:13276:31;2819:24:32;;;;:::i;2863:32::-;2581:348;2858:60;-1:-1:-1;;;2904:14:32;;1428:13276:31;2386:14:32;2904;1428:13276:31;;;;;;;;;;;;;3222:12:4;1428:13276:31;6094:8:32;1428:13276:31;;;-1:-1:-1;;;6074:58:32;;6126:4;1428:13276:31;6074:58:32;;1428:13276:31;;;;;;;;6074:58:32;;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;6074:58:32;;;;;;;;;;;1428:13276:31;-1:-1:-1;;6149:11:32;;;1428:13276:31;;;6163:9:32;1428:13276:31;;;;;;;6149:99:32;1428:13276:31;;6176:24:32;6175:73;1428:13276:31;4571:12:32;1428:13276:31;6176:24:32;:::i;:::-;6209:9;1428:13276:31;6175:73:32;;:::i;:::-;6149:99;;6074:58;;;;1428:13276:31;6074:58:32;;1428:13276:31;6074:58:32;;;;;;1428:13276:31;6074:58:32;;;:::i;:::-;;;1428:13276:31;;;;;6074:58:32;;;;1428:13276:31;-1:-1:-1;1428:13276:31;;6074:58:32;;;-1:-1:-1;6074:58:32;;;1428:13276:31;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;:::i;:::-;;;;3390:9:4;1428:13276:31;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;2721:14;1428:13276;;:::i;:::-;3285:10;1428:13276;;;2721:14;:::i;1428:13276::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;1428:13276:31;8700:15:32;1428:13276:31;;;;;;;;8719:19:32;8700:38;8696:76;;-1:-1:-1;;;;;1428:13276:31;1735:21:22;;1731:47;;7321:6;1428:13276:31;;-1:-1:-1;;;;;;1428:13276:31;;;;;;;-1:-1:-1;;;;;1428:13276:31;7368:33:22;;;;1428:13276:31;;1731:47:22;-1:-1:-1;;;1765:13:22;;1428:13276:31;1765:13:22;;8696:76:32;-1:-1:-1;;;8747:25:32;;1428:13276:31;9084:25:32;8747;1428:13276:31;-1:-1:-1;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;644:38:21;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2184:30:32;1428:13276:31;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;3954:10:32;:22;;3950:77;;3954:10;1428:13276:31;4036:43:32;3954:10;;1428:13276:31;;4036:10:32;1428:13276:31;;;;-1:-1:-1;1428:13276:31;;;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;;4036:43:32;1428:13276:31;;;;;4094:43:32;1428:13276:31;3954:10:32;4094:43;;1428:13276:31;;;4154:4:32;1428:13276:31;;;3950:77:32;-1:-1:-1;;;3999:17:32;;1428:13276:31;;3999:17:32;1428:13276:31;;:::i;:::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;:::i;:::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;1428:13276:31;10040:63;10076:12;;:::i;:::-;10090;;:::i;:::-;1428:13276;;;10040:63;:::i;1428:13276::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;:::i;:::-;2354:8:32;1428:13276:31;;;;;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;1428:13276:31;9031:15:32;1428:13276:31;;;;;;;;9050:25:32;9031:44;9027:82;;837:34:23;;;833:68;;917:13;932:19;;;;;;1428:13276:31;;;917:13:23;991:11;968:34;991:11;;1428:13276:31;991:11:23;;;;:::i;:::-;;:::i;:::-;1428:13276:31;;;;;976:11:23;;;;;;:::i;:::-;;:::i;:::-;1428:13276:31;;;968:7:23;1428:13276:31;;;;;;;;;;;;;;;;;;;;968:34:23;1032:11;;;;;;:::i;:::-;1021:36;1428:13276:31;1045:11:23;;;;;;:::i;:::-;1428:13276:31;;;;;;;;;;;;;;1021:36:23;;1428:13276:31;917:13:23;;833:68;-1:-1:-1;;;880:21:23;;1428:13276:31;880:21:23;;9027:82:32;-1:-1:-1;;;9084:25:32;;1428:13276:31;9084:25:32;;1428:13276:31;-1:-1:-1;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;1780:6:32;1428:13276:31;;;;;;;;;;;;;;;;;;4452:6:32;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;1957:29:22;1428:13276:31;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;1957:29:22;:::i;1428:13276:31:-;;;;;;;;;;;;;;;;;4311:19:32;1428:13276:31;;;;;;;;;;2721:14;1428:13276;;;:::i;:::-;2721:14;;:::i;1428:13276::-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;;;:::i;:::-;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;2585:27:32;;;1428:13276:31;;2633:33:32;1428:13276:31;2633:33:32;;:::i;:::-;2632:34;2628:61;;2581:348;8302:10:31;;;:::i;:::-;1428:13276;;;8350:12;1428:13276;;8388:26;1428:13276;;;8388:26;1428:13276;;8388:31;8384:68;;8480:8;1428:13276;;;-1:-1:-1;;;8480:57:31;;8519:4;1428:13276;8480:57;;1428:13276;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;8519:4;;1428:13276;8480:57;;;;;;;;;;;2581:348:32;8551:10:31;;8547:50;;8822:34;1428:13276;8798:13;1428:13276;;;;;8628:11;1428:13276;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;8519:4;8798:13;:::i;8822:34::-;-1:-1:-1;8480:8:31;1428:13276;;;-1:-1:-1;;;;;1428:13276:31;8871:55;;;;;1428:13276;;;;;;;;;;;;;8871:55;;;;;;2581:348:32;1428:13276:31;;;8953:64;1428:13276;;;;;;9006:10;1428:13276;;;;8953:64;1428:13276;;8871:55;;;;;:::i;:::-;1428:13276;;8871:55;;;;8547:50;-1:-1:-1;;;8570:27:31;;1428:13276;8570:27;;8480:57;;;1428:13276;8480:57;;1428:13276;8480:57;;;;;;1428:13276;8480:57;;;:::i;:::-;;;1428:13276;;;;;;;:::i;:::-;8480:57;;;1428:13276;;;;8480:57;;;-1:-1:-1;8480:57:31;;;1428:13276;;;;;;;;;8384:68;-1:-1:-1;;;8428:24:31;;1428:13276;8428:24;;2581:348:32;2863:32;1428:13276:31;;;;;;:::i;2863:32:32:-;2581:348;2858:60;-1:-1:-1;;;2904:14:32;;1428:13276:31;2386:14:32;2904;1428:13276:31;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;:::i;:::-;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;1428:13276:31;7442:6:32;;;7464:26;7442:6;;:::i;1428:13276:31:-;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;;;;;;2354:8:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;2328:83;;1428:13276:31;8492:53:32;1428:13276:31;8409:18:32;1428:13276:31;;;;;;;;;;8409:18:32;1428:13276:31;;;;;;;;;;;;;;8492:53:32;1428:13276:31;;;;;;;;;;:::i;:::-;;;5406:18:32;1428:13276:31;;5405:19:32;5401:55;;5471:25;1428:13276:31;5471:25:32;;:::i;:::-;5470:26;5466:53;;5128:8;1428:13276:31;;;-1:-1:-1;;;;;1428:13276:31;5128:52:32;;;;;1428:13276:31;;;;;;;;5128:52:32;;5156:4;;;5128:52;5156:4;;;;1428:13276:31;5128:52:32;;;:::i;:::-;;;;;;;;;;;1428:13276:31;-1:-1:-1;;;;;;;1428:13276:31;;;;;3952:11:4;1428:13276:31;;;;;;;;735:10:10;-1:-1:-1;1428:13276:31;;;;;;;;;;10828:37:4;;10824:310;;1428:13276:31;5249:5:4;;;;;;:::i;10824:310::-;10885:24;;;10881:130;;10061:19;;10057:89;;735:10:10;10159:21:4;10155:90;;1428:13276:31;;;5249:5:4;1428:13276:31;;3952:11:4;1428:13276:31;;;;;;;;735:10:10;1428:13276:31;-1:-1:-1;1428:13276:31;;;;;-1:-1:-1;1428:13276:31;;;;;10824:310:4;;;;10155:90;-1:-1:-1;;;10203:31:4;;1428:13276:31;;;;;10203:31:4;;10057:89;-1:-1:-1;;;10103:32:4;;1428:13276:31;;;;;10103:32:4;;10881:130;1428:13276:31;10936:60:4;;;;;;;;735:10:10;1428:13276:31;;;;;;10936:60:4;5128:52:32;;;;;:::i;:::-;1428:13276:31;;5128:52:32;;;;5466:53;-1:-1:-1;;;5505:14:32;;1428:13276:31;2386:14:32;5505;5401:55;-1:-1:-1;;;5433:23:32;;1428:13276:31;5433:23:32;;1428:13276:31;;;;;;;;;;;;;1423:21:22;1428:13276:31;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;:::i;:::-;;;:::i;:::-;-1:-1:-1;;;;;;1428:13276:31;;;1813:11:33;1428:13276:31;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;1797:15:33;1428:13276:31;;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7268:12:32;1428:13276:31;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3222:12:4;1428:13276:31;;;;;;;;;;;;;;;:::i;:::-;2354:8:32;1428:13276:31;;;;;;;-1:-1:-1;;;;;1428:13276:31;2332:10:32;:31;;;2328:83;;8165:52;;;;;1428:13276:31;;;;;;;;8165:52:32;;8193:4;;;8165:52;8193:4;;;;1428:13276:31;8165:52:32;;;:::i;:::-;;;;;;;;;;;1428:13276:31;8247:6:32;;;;;:::i;8165:52::-;8247:6;8165:52;;1428:13276:31;8165:52:32;;;:::i;:::-;1428:13276:31;8165:52:32;;;;1428:13276:31;;;;;;;;;2328:83:32;2386:14;;;1428:13276:31;2386:14:32;1428:13276:31;;2386:14:32;1428:13276:31;;;;;;-1:-1:-1;;1428:13276:31;;;;;;:::i;:::-;;;735:10:10;;10061:19:4;10057:89;;-1:-1:-1;;;;;1428:13276:31;;10159:21:4;;10155:90;;735:10:10;1428:13276:31;;10254:11:4;1428:13276:31;;;;;;;;;;;;;;;;;;;;10333:31:4;1428:13276:31;735:10:10;10333:31:4;;1428:13276:31;;;;;;;10155:90:4;10203:31;;;1428:13276:31;10203:31:4;1428:13276:31;;;;;10203:31:4;10057:89;10103:32;;;1428:13276:31;10103:32:4;1428:13276:31;;;;;10103:32:4;1428:13276:31;;;;;;-1:-1:-1;;1428:13276:31;;;;10384:12;;:::i;:::-;1428:13276;;10350:47;1428:13276;7702:18:33;1428:13276:31;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;7940:890:33;;7947:13;;;:36;;;7940:890;7947:70;;;7940:890;7947:70;;;-1:-1:-1;;;;;1428:13276:31;;;;8069:15:33;1428:13276:31;;;;;;8137:48:33;1428:13276:31;;;;;;;;:::i;:::-;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;8137:48:33;:::i;:::-;8204:25;;;;8200:411;;1428:13276:31;8722:27:33;1428:13276:31;8673:34:33;8763:26;1428:13276:31;8625:34:33;8804:15;1428:13276:31;;;8625:34:33;;:::i;:::-;1428:13276:31;;8673:34:33;;:::i;:::-;8722:27;;:::i;:::-;8763:26;;:::i;:::-;8804:15;;:::i;:::-;7940:890;;;;;;8200:411;2641:13:19;;;;;;;;;8344:45:33;2641:13:19;;;1428:13276:31;2641:13:19;;:5;;;:13;:5;;;:13;8344:45:33;:::i;:::-;8412:13;;8408:166;;2641:13:19;8591:5:33;;;1428:13276:31;;;;;;;;;;8408:166:33;8449:28;8513:42;8449:28;;;8499:56;8449:28;;;;:::i;:::-;8513:42;;:::i;8499:56::-;8408:166;;;;;7947:70;;;;;;;;1428:13276:31;7947:70:33;;;;;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;;7987:30:33;;7947:70;;:36;7964:19;;;;7947:36;;1428:13276:31;;;;;;-1:-1:-1;;1428:13276:31;;;;;6503:41:32;5863:12;;:::i;:::-;6524:9;1428:13276:31;;;;6503:41:32;:::i;1428:13276:31:-;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;2151:5:4;1428:13276:31;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;2151:5:4;1428:13276:31;;;;;;;-1:-1:-1;1428:13276:31;;;;;;;-1:-1:-1;1428:13276:31;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1428:13276:31;;-1:-1:-1;1428:13276:31;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;;;;;;;;14113:61;1428:13276;14113:61;;:105;;;;;1428:13276;14113:166;;;;1428:13276;14113:221;;;;1428:13276;14113:287;;;;1428:13276;14113:332;;;;1428:13276;14113:400;;;;1428:13276;14113:443;;;;1428:13276;14113:519;;;;1428:13276;14113:582;;;;1428:13276;;;;;;;;;;14113:582;-1:-1:-1;;;14648:47:31;;-1:-1:-1;14113:582:31;;;:519;-1:-1:-1;;;14572:60:31;;;-1:-1:-1;14113:519:31;;:443;-1:-1:-1;;;14517:39:31;;;-1:-1:-1;14113:443:31;;:400;-1:-1:-1;;;14461:52:31;;;-1:-1:-1;14113:400:31;;:332;-1:-1:-1;;;14404:41:31;;;-1:-1:-1;14113:332:31;;:287;-1:-1:-1;;;14350:50:31;;;-1:-1:-1;14113:287:31;;:221;-1:-1:-1;;;14283:51:31;;;-1:-1:-1;14113:221:31;;:166;-1:-1:-1;;;14234:45:31;;;-1:-1:-1;14113:166:31;;:105;-1:-1:-1;;;14178:40:31;;;-1:-1:-1;14113:105:31;;1428:13276;;;;;;-1:-1:-1;;1428:13276:31;;;;;;4571:12:32;1428:13276:31;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1428:13276:31;;;;;;;;-1:-1:-1;;1428:13276:31;;;;:::o;:::-;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;:::o;:::-;;;-1:-1:-1;;;;;1428:13276:31;;;;;;:::o;:::-;;;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;:::o;:::-;;;;-1:-1:-1;1428:13276:31;;;;;-1:-1:-1;1428:13276:31;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;:::o;:::-;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;1428:13276:31;;;;;;;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;-1:-1:-1;1428:13276:31;;;;;;;:::o;:::-;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;2828:1:32;1428:13276:31;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;1139:277:23:-;1428:13276:31;1229:13:23;1428:13276:31;;1244:19:23;;;;;-1:-1:-1;;;;;1293:11:23;;;;:::i;:::-;1428:13276:31;;;;1285:7:23;1428:13276:31;;;;;;;;1284:21:23;1280:39;;1428:13276:31;;1229:13:23;;1280:39;1307:12;;1428:13276:31;1307:12:23;:::o;1244:19::-;;;1428:13276:31;1139:277:23;:::o;1428:13276:31:-;;;;;;;;;;:::o;2510:436:32:-;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;2510:436:32;2585:27;;;1428:13276:31;;2633:33:32;2657:8;2633:33;;:::i;:::-;2632:34;2628:61;;2581:348;2767:6:31;;;:::i;:::-;2801:10;;;:::i;:::-;2839:8;1428:13276;;;-1:-1:-1;;;2839:63:31;;1428:13276;2839:63;;1428:13276;;-1:-1:-1;;;;;1428:13276:31;;;;2839:63;2866:4;;;;2839:63;;;;:::i;:::-;;;;;;;;;;1428:13276;2839:63;;;2581:348:32;1428:13276:31;;3038:6;1428:13276;3061:45;1428:13276;3000:6;6799:41:32;1428:13276:31;;6813:9:32;1428:13276:31;6799:41:32;;:::i;:::-;3000:6:31;;;:::i;:::-;3038;:::i;:::-;1428:13276;;;;;;2839:63;1428:13276;;;3061:45;2510:436:32;:::o;2839:63:31:-;;;;;;;;;;;;;;1428:13276;2839:63;;;:::i;:::-;;;1428:13276;;;;;;3038:6;2839:63;;;;;-1:-1:-1;2839:63:31;;2581:348:32;2863:32;1428:13276:31;;;;;;:::i;2863:32:32:-;2581:348;2858:60;2386:14;;;1428:13276:31;2904:14:32;;1428:13276:31;2904:14:32;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;;;1428:13276:31;;;;;;-1:-1:-1;;1428:13276:31;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;1428:13276:31;;;;;;:::o;12147:164::-;12258:8;1428:13276;;;-1:-1:-1;;;12258:46:31;;12286:4;12258:46;;;1428:13276;-1:-1:-1;;;;;1428:13276:31;;;;;;;;12258:46;;1428:13276;;;;;;;12258:46;;;;;;;-1:-1:-1;12258:46:31;;;12246:58;12147:164;:::o;12258:46::-;;;;;;;;;;;;;1428:13276;12258:46;;;:::i;:::-;;;1428:13276;;;;;12147:164;:::o;12258:46::-;;;-1:-1:-1;12258:46:31;;1428:13276;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::o;6891:110:32:-;6960:8;1428:13276:31;;;-1:-1:-1;;;6960:34:32;;6988:4;6960:34;;;1428:13276:31;;;;;;6960:34:32;;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;6960:34:32;;;;;;;-1:-1:-1;6960:34:32;;;6953:41;6891:110;:::o;12840:244:31:-;1428:13276;;;;;;-1:-1:-1;1428:13276:31;12967:12;1428:13276;;;-1:-1:-1;1428:13276:31;13013:17;13044:18;13013:17;;;1428:13276;13044:18;;1428:13276;13044:23;13040:37;;12840:244::o;13040:37::-;-1:-1:-1;;;13069:8:31:o;12571:234::-;-1:-1:-1;;;;;1428:13276:31;-1:-1:-1;1428:13276:31;;;12696:12;1428:13276;;;;;12742:18;;1428:13276;;12774:14;;12770:28;;12571:234::o;2510:436:32:-;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;2510:436:32;2585:27;;;1428:13276:31;;2633:33:32;2657:8;2633:33;;:::i;:::-;2632:34;2628:61;;3604:60:31;2581:348:32;;3532:6:31;;;:::i;:::-;3566:10;;;:::i;:::-;3604:60;1428:13276;;;;;;3604:8;1428:13276;;;;;;;;;;;;;;;;;3604:60;;3628:4;3604:60;;;;:::i;:::-;;;;;;;;;;;1428:13276;3604:60;;;2581:348:32;1428:13276:31;;;3822:45;1428:13276;;3761:6;6503:41:32;3799:6:31;1428:13276;6524:9:32;1428:13276:31;6503:41:32;;;:::i;:::-;3761:6:31;;;;:::i;3799:::-;1428:13276;;;;;;3604:60;1428:13276;;;3822:45;2510:436:32;:::o;3604:60:31:-;;;;;;;;;;;;;;1428:13276;3604:60;;;:::i;:::-;;;1428:13276;;;;;;;;;;3604:60;;;;;-1:-1:-1;3604:60:31;;2581:348:32;2863:32;1428:13276:31;;;;;;:::i;2863:32:32:-;2862:33;2858:60;;3604::31;2581:348:32;;;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;;;;;;;:::o;5656:300:4:-;-1:-1:-1;;;;;1428:13276:31;;5739:18:4;;5735:86;;-1:-1:-1;;;;;1428:13276:31;;5834:16:4;;5830:86;;1428:13276:31;5755:1:4;1428:13276:31;6570:9:4;1428:13276:31;;;5755:1:4;1428:13276:31;;6603:19:4;;;6599:115;;1428:13276:31;7346:25:4;1428:13276:31;;;;5755:1:4;1428:13276:31;6570:9:4;1428:13276:31;;;;5755:1:4;1428:13276:31;;;5755:1:4;1428:13276:31;7284:9:4;1428:13276:31;;;5755:1:4;1428:13276:31;;;;;;;;;;;;7346:25:4;5656:300::o;6599:115::-;6649:50;;;;5755:1;6649:50;;1428:13276:31;;;;;;5755:1:4;6649:50;5830:86;5873:32;;;5755:1;5873:32;5755:1;5873:32;1428:13276:31;;5755:1:4;5873:32;5735:86;5780:30;;;5755:1;5780:30;5755:1;5780:30;1428:13276:31;;5755:1:4;5780:30;9238:170:32;1829:53:8;9238:170:32;9315:22;1428:13276:31;9315:22:32;1428:13276:31;9315:22:32;:::i;:::-;;1428:13276:31;;;-1:-1:-1;;;1829:53:8;;;;-1:-1:-1;;;;;1428:13276:31;;;1829:53:8;;;1428:13276:31;9387:4:32;1428:13276:31;;;;;;;;;;;;1829:53:8;;;;1428:13276:31;1829:53:8;:::i;:::-;9347:6:32;1829:53:8;:::i;:::-;9238:170:32:o;1229:354:21:-;1366:15;1428:13276:31;;;;;;;;;;1411:19:21;1396:34;;1392:77;;1499:25;1428:13276:31;;;1484:40:21;;1480:74;;1565:11;1411:19;1229:354;:::o;1480:74::-;1533:21;;;:::i;:::-;1526:28;:::o;1392:77::-;-1:-1:-1;;4057:6:22;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;;4078:21:22;;4074:49;;1428:13276:31;;4137:20:22;4133:53;;1428:13276:31;4200:35:22;;4196:70;;1428:13276:31;;;;;;;;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5044:37:22;5040:71;;1428:13276:31;1211:2:22;1428:13276:31;;;;;;1211:2:22;1428:13276:31;;;;;;;;;;;;;;;;;;;;;;;5240:32:22;;;5236:67;;5347:32;;;;:::i;:::-;1428:13276:31;;;;;;;5422:32:22;1428:13276:31;;;-1:-1:-1;1428:13276:31;:::i;:::-;5314:66:22;;5422:32;;:::i;:::-;1428:13276:31;;;;;;;;;;;;;;;;;;;;;4360:12:22;:36;;4356:73;;-1:-1:-1;4481:3:22;1428:13276:31;;4460:19:22;;;;;-1:-1:-1;;;;;4504:11:22;;;;:::i;:::-;1428:13276:31;;4504:25:22;4500:51;;-1:-1:-1;;;;;4594:11:22;;;;:::i;:::-;1428:13276:31;;;-1:-1:-1;1428:13276:31;5909:6:22;1428:13276:31;;;-1:-1:-1;1428:13276:31;;;;5909:17:22;;;;:::i;:::-;1428:13276:31;;5954:291:22;6032:21;;1428:13276:31;;;;;;;;:::i;:::-;;;6096:4:22;5985:250;;;1428:13276:31;5985:250:22;1428:13276:31;5985:250:22;;1428:13276:31;5985:250:22;1428:13276:31;5985:250:22;;1428:13276:31;;5985:250:22;;1428:13276:31;5954:291:22;:::i;:::-;1428:13276:31;6261:131:22;1428:13276:31;;6361:21:22;;;1428:13276:31;;;6293:13:22;;1428:13276:31;;;;;;;;;;;;;;;6096:4:22;1428:13276:31;6096:4:22;1428:13276:31;;;;;;:::i;:::-;6261:131:22;;;1428:13276:31;;1211:2:22;1428:13276:31;;;;;1211:2:22;1428:13276:31;;;;;;;6451:50:22;;;:::i;:::-;1428:13276:31;;;1411:19:21;1428:13276:31;;;;;;;1211:2:22;1428:13276:31;;;;;;1211:2:22;1428:13276:31;;;;;;;6502:56:22;;;:::i;:::-;1428:13276:31;;;;;;;;;;;;;;6586:64:22;1428:13276:31;;;;;;:::i;:::-;6586:64:22;;;:::i;:::-;4570:54;4566:118;;1411:19:21;1428:13276:31;4445:13:22;;4566:118;4651:18;;;-1:-1:-1;4651:18:22;;-1:-1:-1;4651:18:22;4500:51;1765:13;;;-1:-1:-1;4538:13:22;;-1:-1:-1;4538:13:22;4460:19;;;;;;;;;1411::21;1432:37;:::o;4356:73:22:-;4405:24;;;-1:-1:-1;4405:24:22;;-1:-1:-1;4405:24:22;5236:67;4244:22;;;-1:-1:-1;5281:22:22;;-1:-1:-1;5281:22:22;5040:71;880:21:23;;;-1:-1:-1;5090:21:22;;-1:-1:-1;5090:21:22;4133:53;4166:20;;;1428:13276:31;4166:20:22;;1428:13276:31;4166:20:22;4074:49;4108:15;;;1428:13276:31;4108:15:22;;1428:13276:31;4108:15:22;1428:13276:31;;;;;;;;;;;;890:333:21;1428:13276:31;970:4:21;1428:13276:31;1016:15:21;1428:13276:31;;;;;;;;;1061:19:21;1046:34;;1042:72;;1428:13276:31;1144:25:21;1129:40;1125:69;;1205:11;;1061:19;890:333;:::o;1125:69::-;-1:-1:-1;;;;;1428:13276:31;;;1178:7:21;1428:13276:31;;;;;;;;;1171:23:21:o;1042:72::-;2366:6:22;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;;;;-1:-1:-1;1428:13276:31;;;;;-1:-1:-1;2387:21:22;;2383:47;;2444:21;;2440:49;;1428:13276:31;2503:35:22;;2499:70;;1428:13276:31;;;;;;;;;;;;;;;;;;-1:-1:-1;;1428:13276:31;;;;;;;;;;;;2716:88:22;1428:13276:31;;;-1:-1:-1;;1428:13276:31;;2716:88:22;:::i;:::-;1428:13276:31;;;;;2902:12:22;;:31;;2898:68;;1428:13276:31;;;3456:64:22;1428:13276:31;-1:-1:-1;1428:13276:31;2993:6:22;1428:13276:31;;3341:99:22;1428:13276:31;-1:-1:-1;1428:13276:31;;;2993:17:22;;;;:::i;:::-;1428:13276:31;;3296:29:22;1428:13276:31;;;;;:::i;:::-;;;;-1:-1:-1;1428:13276:31;:::i;:::-;;;3150:4:22;1428:13276:31;3052:216:22;;1428:13276:31;3052:216:22;1428:13276:31;3052:216:22;;1428:13276:31;3052:216:22;;;;1428:13276:31;3052:216:22;;;;1428:13276:31;3296:29:22;:::i;:::-;1428:13276:31;;;3360:13:22;;1428:13276:31;;;;;;;;;;3052:216:22;;1428:13276:31;;;;3052:216:22;1428:13276:31;;;;-1:-1:-1;1428:13276:31;;;;-1:-1:-1;1428:13276:31;;;;;;;3150:4:22;;1428:13276:31;;;;;;;;;3341:99:22;;;;1428:13276:31;;;;:::i;:::-;3456:64:22;;:::i;:::-;3455:65;3451:96;;1061:19:21;1082:32;:::o;10101:196:32:-;1428:13276:31;;;;;;10189:10:32;10175:24;;;:63;;;;10101:196;10171:120;;;10101:196::o;10171:120::-;10261:19;;;;;;;;10175:63;-1:-1:-1;1428:13276:31;;;10204:10:32;1428:13276:31;;;;;;;;10189:10:32;1428:13276:31;;;;;;;;;;10203:35:32;;-1:-1:-1;10175:63:32;;3080:625:33;-1:-1:-1;;;;;1428:13276:31;-1:-1:-1;1428:13276:31;;;3206:11:33;1428:13276:31;;;;;;3080:625:33;;;-1:-1:-1;;;;;1428:13276:31;3243:14:33;;3239:43;;1428:13276:31;-1:-1:-1;1428:13276:31;3317:15:33;1428:13276:31;;;-1:-1:-1;1428:13276:31;;3364:22:33;;;:44;;;;3080:625;3360:77;;3466:22;;3498:27;3466:22;;:::i;:::-;1428:13276:31;3498:27:33;1428:13276:31;3498:27:33;:::i;:::-;;1428:13276:31;3540:20:33;;;-1:-1:-1;1428:13276:31;3317:15:33;1428:13276:31;;-1:-1:-1;1428:13276:31;;;;;;;;;-1:-1:-1;1428:13276:31;3206:11:33;1428:13276:31;;;-1:-1:-1;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;;;;;;3080:625:33:o;3536:163::-;1428:13276:31;;-1:-1:-1;1428:13276:31;3317:15:33;1428:13276:31;;;;-1:-1:-1;1428:13276:31;;3080:625:33:o;3360:77::-;10606:20:32;;;-1:-1:-1;3417:20:33;;-1:-1:-1;3417:20:33;3364:44;3390:18;;;3364:44;;3239:43;3266:16;;;-1:-1:-1;3266:16:33;;-1:-1:-1;3266:16:33;10718:128:32;1780:6;-1:-1:-1;10783:56:32;;10718:128::o;10783:56::-;11434:20:31;;;10819::32;;;;;7721:208:4;-1:-1:-1;;;;;1428:13276:31;;7791:21:4;;7787:91;;7346:25;1428:13276:31;;6496:21:4;7810:1;1428:13276:31;6496:21:4;1428:13276:31;6496:21:4;:::i;:::-;;1428:13276:31;;;;7284:9:4;1428:13276:31;;;;;;;;;;;;;;;;7346:25:4;7721:208::o;6725:477:22:-;3515:233:16;6725:477:22;6940:27;;1428:13276:31;;;;;6930:38:22;1428:13276:31;;;;;;;6986:26:22;;1428:13276:31;;;;;;;;7030:18:22;;;1428:13276:31;;7066:16:22;7100:26;7066:16;;;1428:13276:31;7100:26:22;;1428:13276:31;;7030:18:22;1428:13276:31;6868:272:22;1428:13276:31;6868:272:22;;1428:13276:31;803:139:22;1428:13276:31;;7030:18:22;803:139;;1428:13276:31;7066:16:22;803:139;;1428:13276:31;7100:26:22;803:139;;1428:13276:31;803:139:22;;;1428:13276:31;803:139:22;;;1428:13276:31;803:139:22;6868:272;;;;;;:::i;:::-;1428:13276:31;6845:305:22;;5053:20:15;;:::i;:::-;3515:233:16;7030:18:22;3515:233:16;;-1:-1:-1;;;3515:233:16;;;;;;;;;;;6725:477:22;:::o;1428:13276:31:-;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;-1:-1:-1;;;;;1428:13276:31;;;;;;;:::o;10764:237::-;10853:8;1428:13276;;;-1:-1:-1;;;10853:37:31;;10884:4;10853:37;;;1428:13276;;;;;;10853:37;;1428:13276;;-1:-1:-1;;;;;1428:13276:31;10853:37;;;;;;-1:-1:-1;10853:37:31;;;10764:237;10853:57;1428:13276;;10893:17;1428:13276;10853:57;;:::i;:::-;4571:12:32;1428:13276:31;;10927:30;;;;;;10960;;;:::i;10853:37::-;;1428:13276;10853:37;;1428:13276;10853:37;;;;;;1428:13276;10853:37;;;:::i;:::-;;;1428:13276;;;;10853:57;1428:13276;;10853:37;;;;;-1:-1:-1;10853:37:31;;5252:1357:33;;;1428:13276:31;;;5460:17:33;1428:13276:31;5491:17:33;;;:36;;;;5252:1357;5487:55;;1428:13276:31;;5618:6:33;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;;;;;5732:59:33;:28;;;:59;:28;;;:59;;;;;;;-1:-1:-1;;;;;1428:13276:31;;5809:35:33;;;;;1428:13276:31;;5896:15:33;1428:13276:31;;;;;6010:74:33;1428:13276:31;;;;;;;;;;:::i;:::-;;;;;5618:6:33;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;;;;;;;;6010:74:33;:::i;:::-;6103:12;;;;;6099:175;;1428:13276:31;6383:26:33;1428:13276:31;6423:21:33;1428:13276:31;6355:12:33;1428:13276:31;;;6458:20:33;1428:13276:31;;;;;;;;;6355:12:33;;:::i;:::-;6383:26;:::i;:::-;6423:21;;:::i;:::-;6458:20;;:::i;:::-;6497:11;;6493:22;;6530:15;;;:::i;:::-;5802:754;;;;;;;;;6493:22;6510:5;;;;;;;-1:-1:-1;;;;;6510:5:33;;;5802:754;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;5618:6:33;1428:13276:31;;;5618:6:33;1428:13276:31;5252:1357:33:o;6099:175::-;6135:102;;;;;;;;;;;;;;;;6254:5;;;;;-1:-1:-1;;;;;6254:5:33;;;6135:102;6173:15;;;;;;;;;;;;:::i;:::-;6210:8;;;5809:35;;;;;;;;-1:-1:-1;;;;;5809:35:33;;;;;;5732:59;;;;;;;;;;5487:55;5529:13;;;;;;;1428:13276:31;5529:13:33;1428:13276:31;5529:13:33;:::o;5491:36::-;5512:15;;;5491:36;;8051:302:19;;8188:25;8051:302;;8188:25;:::i;8051:302::-;;;8188:25;;;;;:::i;:::-;1428:13276:31;8257:25:19;;;;;8223:101;;8051:302;:::o;8223:101::-;13569:18:31;1428:13276;;;;;;;8051:302:19;:::o;8257:25::-;1428:13276:31;;;-1:-1:-1;1428:13276:31;;;;;-1:-1:-1;1428:13276:31;11046:1066;;;;1428:13276;;;;;;;;-1:-1:-1;1428:13276:31;11222:12;1428:13276;;;-1:-1:-1;1428:13276:31;11288:26;;;;1428:13276;;;11329:26;;11325:56;;11395:30;-1:-1:-1;11391:63:31;;6524:9:32;1428:13276:31;6503:41:32;;;;:::i;:::-;11557:12:31;;;:::i;:::-;11548:21;;11544:57;;12051:54;1428:13276;;;11856:19;11878:75;-1:-1:-1;;;;;1428:13276:31;11612:27;1428:13276;11612:27;1428:13276;11612:27;:::i;:::-;;1428:13276;11649:17;;;:27;1428:13276;;;11649:27;:::i;:::-;1428:13276;;;11686:18;;;1428:13276;11686:28;1428:13276;;;11686:28;:::i;:::-;1428:13276;;;11724:36;1428:13276;;;11724:36;:::i;11878:75::-;11856:19;;1428:13276;;;;;;;;;;;12051:54;;11046:1066::o;11544:57::-;11578:23;;;-1:-1:-1;11578:23:31;;-1:-1:-1;11578:23:31;11325:56;11364:17;;;-1:-1:-1;11364:17:31;;-1:-1:-1;11364:17:31;8247:206:4;;;;-1:-1:-1;;;;;1428:13276:31;8317:21:4;;8313:89;;1428:13276:31;8336:1:4;1428:13276:31;6570:9:4;1428:13276:31;;;8336:1:4;1428:13276:31;;6603:19:4;;;6599:115;;1428:13276:31;;8336:1:4;1428:13276:31;;7346:25:4;1428:13276:31;;;;6570:9:4;1428:13276:31;;;;;;;;7073:21:4;1428:13276:31;;7073:21:4;1428:13276:31;;;;;;7346:25:4;8247:206::o;6599:115::-;6649:50;;;;;8336:1;6649:50;;1428:13276:31;;;;;;8336:1:4;6649:50;10264:155:33;10362:17;1428:13276:31;;10353:26:33;;;;;;10382;;;:::i;:::-;10362:17;1428:13276:31;10264:155:33:o;10353:59::-;;;-1:-1:-1;10362:17:33;1428:13276:31;10264:155:33:o;7420:705:31:-;;;;7629:17;;;1428:13276;7680:18;1428:13276;;7680:18;;1428:13276;;;;7713:21;;7709:54;;7793:53;;:23;;;;;;7819;;;:::i;:::-;7793:53;;1428:13276;;;7861:22;7857:55;;7943:24;;;;;;7970;;;:::i;:::-;1428:13276;;8012:22;8008:50;;7943:55;1428:13276;8072:23;8068:50;;7420:705;:::o;8068:50::-;-1:-1:-1;1428:13276:31;;7420:705::o;8008:50::-;-1:-1:-1;1428:13276:31;;8008:50;;7943:55;;;-1:-1:-1;7943:55:31;;7857;7743:20;;;-1:-1:-1;7892:20:31;;-1:-1:-1;7892:20:31;7793:53;;;-1:-1:-1;7793:53:31;;;9485:156:32;;1412:43:8;;9485:156:32;9565:22;1428:13276:31;9565:22:32;1428:13276:31;9565:22:32;:::i;:::-;;1428:13276:31;;;-1:-1:-1;;;1412:43:8;;;;-1:-1:-1;;;;;1428:13276:31;;;1412:43:8;;;1428:13276:31;1624:1;;;1428:13276;;;;;1624:1;;;1412:43:8;;1428:13276:31;;1412:43:8;;;;;;:::i;4003:1184:33:-;;;;;1428:13276:31;4163:14:33;;;:40;;;;4003:1184;4159:54;;1428:13276:31;;;;;;;4289:6:33;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;;;;;4374:760:33;;4381:13;;;:47;;;4374:760;4381:47;;;-1:-1:-1;;;;;1428:13276:31;;;;4480:15:33;1428:13276:31;;;;;4595:73:33;1428:13276:31;;;;;;;;;;:::i;4595:73:33:-;4687:12;;;;;;4683:175;;1428:13276:31;4967:21:33;1428:13276:31;5002:20:33;1428:13276:31;4939:12:33;1428:13276:31;;5036:20:33;1428:13276:31;;;;;;;;;;4939:12:33;;:::i;4967:21::-;5002:20;;:::i;:::-;5036;;:::i;:::-;5075:11;;5071:22;;5108:15;;;:::i;:::-;4374:760;;;;;;;;;5071:22;5088:5;;;;;;-1:-1:-1;;;;;5088:5:33;;;1428:13276:31;-1:-1:-1;;;;;1428:13276:31;4289:6:33;1428:13276:31;;;4289:6:33;1428:13276:31;4003:1184:33:o;4683:175::-;4719:102;;;;;;;;;;;;;;4838:5;;;;-1:-1:-1;;;;;4838:5:33;;;;;;4719:102;4757:15;;;;;;;;:::i;:::-;4794:8;;;4381:47;;;;;;;-1:-1:-1;;;;;4381:47:33;;;;;;1428:13276:31;;-1:-1:-1;;;;;1428:13276:31;;4398:30:33;;4381:47;;4159:54;4205:8;;;;;1428:13276:31;4205:8:33;:::o;4163:40::-;1428:13276:31;4181:17:33;1428:13276:31;4181:22:33;4163:40;;3803:4116:19;;;1428:13276:31;;;;-1:-1:-1;;3803:4116:19;;4383:131;;;;;;;;;;;;4595:10;;4591:368;;5065:20;;;;5061:88;;5435:300;;;1428:13276:31;;;5954:31:19;;5999:371;;;6813:1;1428:13276:31;6794:1:19;1428:13276:31;6793:21:19;1428:13276:31;;;6813:1:19;1428:13276:31;;;;;6813:1:19;1428:13276:31;;;;;6813:1:19;1428:13276:31;;;;;6813:1:19;1428:13276:31;;;;;6813:1:19;1428:13276:31;;;;;6813:1:19;1428:13276:31;;5999:371:19;;;;1428:13276:31;5999:371:19;;;5435:300;;;;;;1428:13276:31;5435:300:19;;5999:371;6436:21;1428:13276:31;3803:4116:19;:::o;5061:88::-;5112:22;;;1428:13276:31;5112:22:19;;1428:13276:31;5112:22:19;4591:368;1428:13276:31;;;;;;;;;4918:26:19;:::o;1428:13276:31:-;;;;;;;;;;:::o;:::-;10848:17:4;;1428:13276:31;;;;;;;;:::o;:::-;-1:-1:-1;;1428:13276:31;;;;;;;:::o;6687:778:33:-;;;;;;;6964:18;;;1428:13276:31;;;;;;;;;6964:32:33;6960:57;;7051:41;;;;;:::i;:::-;1428:13276:31;;;7128:46:33;10571:134;;;;;6687:778;10571:134;;;;;;6687:778;7189:20;;;7185:46;;7242:33;7298:47;7414:44;7242:33;;7298:47;;;:::i;:::-;1428:13276:31;;;;7369:33:33;;;;-1:-1:-1;;;;;1428:13276:31;7414:44:33;:::i;:::-;;6687:778::o;7185:46::-;-1:-1:-1;;;;;;;;;;;7211:20:33:o;10571:134::-;;-1:-1:-1;10571:134:33;;;;;-1:-1:-1;10571:134:33;;;6960:57;-1:-1:-1;;;;;;;7012:4:33;;-1:-1:-1;;;6998:19:33:o;1428:13276:31:-;;-1:-1:-1;1428:13276:31;;;:::o;3385:267:11:-;1390:66;3508:46;;1390:66;;;2652:40;;2706:11;2715:2;2706:11;;2702:69;;1428:13276:31;;;;;;;:::i;:::-;2311:2:11;1428:13276:31;;;;;;;;;;;2367:90:11;;;3570:22;:::o;2702:69::-;2740:20;;;-1:-1:-1;2740:20:11;;-1:-1:-1;2740:20:11;3504:142;1428:13276:31;;;1390:66:11;;;;6126:13:15;1390:66:11;:::i;:::-;;;;:::i;3385:267::-;1390:66;3508:46;;1390:66;;;2652:40;;2706:11;2715:2;2706:11;;2702:69;;1428:13276:31;;;;;;;:::i;3504:142:11:-;1428:13276:31;;;1390:66:11;;;;6584:16:15;1390:66:11;:::i;4059:629:8:-;2847:1:9;4059:629:8;3510:55:9;4059:629:8;1428:13276:31;;;;;;3462:31:9;;;;;;;;;;;;:::i;:::-;3510:55;;;:::i;:::-;1428:13276:31;;4551:22:8;;;;:57;;;;4059:629;4547:135;;;;4059:629;:::o;4547:135::-;4631:40;;;2847:1:9;4631:40:8;;1428:13276:31;;2847:1:9;4631:40:8;4551:57;4578:30;;;;3462:31:9;4578:30:8;;;1428:13276:31;;;;3462:31:9;1428:13276:31;4578:30:8;;1428:13276:31;:::i;:::-;4577:31:8;4551:57;;;;1039:368:17;;1211:33;;;;:::i;:::-;-1:-1:-1;1428:13276:31;;;;;;:::i;:::-;1274:35:17;:58;;;;1039:368;1273:127;;;;;1039:368;1254:146;;;1039:368;:::o;1273:127::-;1283:26;1428:13276:31;;;;2041:60:17;1428:13276:31;;;;;2041:60:17;;;;;;;;;;;;;;;1428:13276:31;;;;;;;;;;;:::i;2041:60:17:-;2010:101;;;;;;:::i;:::-;2129:42;;;1273:127;2129:134;;;1273:127;;;;;;;2129:134;1428:13276:31;;2041:60:17;1428:13276:31;;;2187:29:17;;1428:13276:31;;;;2041:60:17;2187:29;1428:13276:31;-1:-1:-1;;;2187:76:17;2129:134;;;:42;1428:13276:31;;2041:60:17;1428:13276:31;;2152:19:17;;2129:42;;;1274:58;-1:-1:-1;;;;;1428:13276:31;;;;;1313:19:17;;-1:-1:-1;1274:58:17;;;3845:262:15;3929:4;3938:11;-1:-1:-1;;;;;1428:13276:31;3921:28:15;;:63;;3845:262;3917:184;;;4007:22;4000:29;:::o;3917:184::-;1428:13276:31;;4204:80:15;;;1428:13276:31;2079:95:15;1428:13276:31;;4226:11:15;1428:13276:31;2079:95:15;;1428:13276:31;4239:14:15;2079:95;;;1428:13276:31;4255:13:15;2079:95;;;1428:13276:31;3929:4:15;2079:95;;;1428:13276:31;2079:95:15;4204:80;;;;;;:::i;:::-;1428:13276:31;4194:91:15;;4060:30;:::o;3921:63::-;3970:14;;3953:13;:31;3921:63;;2129:766:14;1428:13276:31;;;2129:766:14;2276:2;2256:22;;2276:2;;2739:25;2539:180;;;;;;;;;;;;;;;-1:-1:-1;2539:180:14;2739:25;;:::i;:::-;2732:32;;;;;:::o;2252:637::-;2795:83;;2811:1;2795:83;2815:35;2795:83;;:::o;1428:13276:31:-;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;1428:13276:31;;;;:::o;:::-;;;:::o;5140:1530:14:-;;;6199:66;6186:79;;6182:164;;1428:13276:31;;;;;;-1:-1:-1;1428:13276:31;;;;;;;;;;;;;;;;;;;6457:24:14;;;;;;;;;-1:-1:-1;6457:24:14;-1:-1:-1;;;;;1428:13276:31;;6495:20:14;6491:113;;6614:49;-1:-1:-1;6614:49:14;-1:-1:-1;5140:1530:14;:::o;6491:113::-;6531:62;-1:-1:-1;6531:62:14;6457:24;6531:62;-1:-1:-1;6531:62:14;:::o;6182:164::-;6281:54;;;6297:1;6281:54;6301:30;6281:54;;:::o;4625:582:9:-;;4797:8;;-1:-1:-1;1428:13276:31;;5874:21:9;:17;;6046:142;;;;;;5870:383;6225:17;;;5894:1;6225:17;;5894:1;6225:17;4793:408;1428:13276:31;;5045:22:9;:49;;;4793:408;5041:119;;5173:17;;:::o;5041:119::-;-1:-1:-1;;;5066:1:9;5121:24;;;-1:-1:-1;;;;;1428:13276:31;;;;5121:24:9;1428:13276:31;;;5121:24:9;5045:49;5071:18;;;:23;5045:49;

Swarm Source

ipfs://e39e9ad2cb2fbd0b267a0000e0e1432ec3ada7e1a021a8bb5287c20e41c36122

Block Transaction Gas Used Reward
view all blocks produced

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
[ Download: CSV Export  ]

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