ETH Price: $2,227.86 (-5.42%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Request Deposit243731732026-02-03 1:52:5915 hrs ago1770083579IN
Credit Coop: ccUSDC Token
0 ETH0.000006650.10972587
Request Deposit243552942026-01-31 13:56:113 days ago1769867771IN
Credit Coop: ccUSDC Token
0 ETH0.000199012.0840056
Request Deposit243343642026-01-28 15:53:476 days ago1769615627IN
Credit Coop: ccUSDC Token
0 ETH0.000035970.31947548
Request Deposit242871232026-01-22 1:43:3512 days ago1769046215IN
Credit Coop: ccUSDC Token
0 ETH0.000004920.06282327
Request Deposit242870512026-01-22 1:28:5912 days ago1769045339IN
Credit Coop: ccUSDC Token
0 ETH0.000003370.0430416
Request Deposit242870342026-01-22 1:25:3512 days ago1769045135IN
Credit Coop: ccUSDC Token
0 ETH0.000005240.04655807
Deposit242705492026-01-19 18:15:3514 days ago1768846535IN
Credit Coop: ccUSDC Token
0 ETH0.000004840.07285106
Request Deposit242704222026-01-19 17:49:5914 days ago1768844999IN
Credit Coop: ccUSDC Token
0 ETH0.000014710.11345458
Deposit240818632025-12-24 10:24:4741 days ago1766571887IN
Credit Coop: ccUSDC Token
0 ETH0.000002320.03503365
Request Deposit240469322025-12-19 13:26:4746 days ago1766150807IN
Credit Coop: ccUSDC Token
0 ETH0.000009340.07203372
Deposit240204332025-12-15 20:37:3549 days ago1765831055IN
Credit Coop: ccUSDC Token
0 ETH0.00000230.04371195
Request Deposit240162292025-12-15 6:31:5950 days ago1765780319IN
Credit Coop: ccUSDC Token
0 ETH0.000005560.04288463
Deposit239294132025-12-03 0:40:3562 days ago1764722435IN
Credit Coop: ccUSDC Token
0 ETH0.000054971.04144256
Request Deposit239292882025-12-03 0:15:3562 days ago1764720935IN
Credit Coop: ccUSDC Token
0 ETH0.000129181.03434703
Deposit238782882025-11-25 20:50:1169 days ago1764103811IN
Credit Coop: ccUSDC Token
0 ETH0.000056531.07128724
Request Deposit238779982025-11-25 19:51:4769 days ago1764100307IN
Credit Coop: ccUSDC Token
0 ETH0.000010360.0830166
Deposit235841272025-10-15 15:57:23111 days ago1760543843IN
Credit Coop: ccUSDC Token
0 ETH0.000103851.96749337
Request Deposit235839432025-10-15 15:20:11111 days ago1760541611IN
Credit Coop: ccUSDC Token
0 ETH0.000186871.49622038
Approve234939052025-10-03 1:15:23123 days ago1759454123IN
Credit Coop: ccUSDC Token
0 ETH0.000004020.16498909
Approve234938722025-10-03 1:08:47123 days ago1759453727IN
Credit Coop: ccUSDC Token
0 ETH0.00000510.17265637
Approve234938692025-10-03 1:08:11123 days ago1759453691IN
Credit Coop: ccUSDC Token
0 ETH0.000009160.19790058
Approve234938622025-10-03 1:06:47123 days ago1759453607IN
Credit Coop: ccUSDC Token
0 ETH0.000004320.17735585
Approve234938502025-10-03 1:04:23123 days ago1759453463IN
Credit Coop: ccUSDC Token
0 ETH0.000009590.2056608
Transfer234938302025-10-03 1:00:23123 days ago1759453223IN
Credit Coop: ccUSDC Token
0 ETH0.000039010.75609912
Deposit234224202025-09-23 1:23:23133 days ago1758590603IN
Credit Coop: ccUSDC Token
0 ETH0.000010960.16497527
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x61010060226414342025-06-05 22:37:35242 days ago1749163055  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
LendingVault

Compiler Version
v0.8.25+commit.b61c2a91

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
File 1 of 49 : LendingVault.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

// Interfaces
import {ILendingVault} from "contracts/interfaces/ILendingVault.sol";
import {ILiquidStrategy} from "contracts/interfaces/ILiquidStrategy.sol";
import {IERC7540, IERC7540Deposit, IERC7540Redeem, IERC7540Operator} from "contracts/interfaces/IERC7540.sol";
import {IERC7575, IERC165} from "contracts/interfaces/IERC7575.sol";
import {ILineOfCredit} from "Line-Of-Credit-v2/interfaces/ILineOfCredit.sol";

// Openzeppelin imports
import {Math} from "openzeppelin/utils/math/Math.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {ERC20} from "openzeppelin/token/ERC20/ERC20.sol";
import {ERC4626} from "openzeppelin/token/ERC20/extensions/ERC4626.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {ReentrancyGuard} from "openzeppelin/utils/ReentrancyGuard.sol";
import {Pausable} from "openzeppelin/utils/Pausable.sol";
import {AccessControl} from "openzeppelin/access/AccessControl.sol";

// Vaults
import {CreditStrategy} from "contracts/strategies/credit/CreditStrategy.sol";

/**
 * @title  - Lending Vault
 * @author - Credit Cooperative
 * @notice - The LendingVault inherits the ERC4626 and ERC7540 Tokenized Vault standards to enable pooling of capital for lending to Credit Coop Line-Of-Credit-v2 contracts.
 * @dev    - Should only be deployed using the LendingVaultFactory contract.
 */
contract LendingVault is ILendingVault, IERC7540, ERC4626, Pausable, AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;
    using Math for uint256;

    // Pausable + Access Control
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

    // ERC-7575
    address public share = address(this);

    // ERC-7540 mappings
    mapping(uint256 => mapping(address => uint256)) public pendingDepositRequest;
    mapping(uint256 => mapping(address => uint256)) public claimableDepositRequest;
    mapping(uint256 => mapping(address => uint256)) public claimableDepositRequestShares;
    mapping(uint256 => mapping(address => uint256)) public pendingRedeemRequest;
    mapping(uint256 => mapping(address => uint256)) public claimableRedeemRequest;
    mapping(uint256 => mapping(address => uint256)) public claimableRedeemRequestAssets;
    mapping(address controller => mapping(address operator => bool)) public isOperator;

    // ERC-7540 total pending deposit requests (in assets) that are illiquid and cannot be spent by vault
    uint256 public totalPendingDeposits;

    // ERC-7540 total claimable redeem requests (in assets) that are illiquid and cannot be spent by vault
    uint256 public totalClaimableRedeemAssets;

    // ERC-7540 deposit and redemption requests are non-transferrable and processed by the manager
    uint256 constant REQUEST_ID = 0;

    address private deployerFactory;

    ILiquidStrategy public liquidStrategy;
    address public creditStrategy;
    uint256 public immutable baseUnit;

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    address public immutable underlyingAsset;

    constructor(
        address asset_,
        string memory name_,
        string memory symbol_,
        address liquidStrategy_,
        address factory_
    ) ERC4626(IERC20(asset_)) ERC20(name_, symbol_) {
        if (asset_ == address(0) || liquidStrategy_ == address(0) || factory_ == address(0)) {
            revert CannotBeZeroAddress();
        }
        underlyingAsset = asset_;
        baseUnit = 10 ** decimals();

        liquidStrategy = ILiquidStrategy(liquidStrategy_);
        emit LiquidStrategySet(liquidStrategy_);

        deployerFactory = factory_;
    }

    /**
     * @notice - Sets the CreditStrategy contract for the LendingVault.
     * @dev    - Can only be called once by `deployerFactory`.
     * @param _creditStrategy - Address of the CreditStrategy contract.
     */
    function setCreditStrategy(address _creditStrategy, address[] memory pausers) external {
        if (msg.sender != deployerFactory) {
            revert OnlyDeployerFactory();
        }
        if (creditStrategy != address(0)) {
            revert CreditStrategyAlreadySet();
        }
        creditStrategy = _creditStrategy;
        emit CreditStrategySet(_creditStrategy);

        // Create the admin and pauser roles
        _setPausers(_creditStrategy, pausers);

    }

    /**
     * @notice - Setup the admin and pausers for the LendingVault contract.
     * @param _creditStrategy - Address of the CreditStrategy contract.
     * @param pausers         - Array of addresses to set as pausers.
     */
    function _setPausers(address _creditStrategy, address[] memory pausers) internal {
        // `manager` of the CreditStrategy contract is set as the admin and pauser of the LendingVault
        _grantRole(DEFAULT_ADMIN_ROLE, CreditStrategy(_creditStrategy).manager());
        _grantRole(PAUSER_ROLE, CreditStrategy(_creditStrategy).manager());
        _setRoleAdmin(PAUSER_ROLE, DEFAULT_ADMIN_ROLE);

        // setup additional pausers
        uint256 len = pausers.length;
        for (uint256 i = 0; i < len; i++) {
            _grantRole(PAUSER_ROLE, pausers[i]);
        }
    }

    /*//////////////////////////////////////////////////////////////
                             ACCESS CONTROL
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Asserts that available, liquid assets in the LendingVault are greater than or equal to the amount specified.
     * @param amount - Amount to compare against the liquid assets in vault.
     */
    function _assertVaultHasSufficientAvailableAssets(uint256 amount) internal view {
        uint256 availableAssets = IERC20(underlyingAsset).balanceOf(address(this)) - totalPendingDeposits - totalClaimableRedeemAssets;
        if (availableAssets < amount) {
            revert InsufficientAvailableAssets();
        }
    }

    /**
     * @dev Modifier to restrict functions to pausers.
     */
    function _onlyPauser()  internal view {
        if (!hasRole(PAUSER_ROLE, msg.sender)) {
            revert OnlyPauser();
        }
    }

    /**
     * @notice - Checks `msg.sender` is `owner` or one of its operators.
     */
    function _onlyOwnerOrOperator(address owner) internal view {
        if (msg.sender != owner
            && !isOperator[owner][msg.sender] // user approved operators
        ) {
            revert OnlyOwnerOrOperator();
        }
    }

    /**
     * @notice - Checks `msg.sender` is `manager` of the Credit Strategy contract.
     */
    function _onlyVaultManager() internal view {
        if (msg.sender != CreditStrategy(creditStrategy).manager()) {
            revert OnlyVaultManager();
        }
    }

    /**
     * @notice - Checks `msg.sender` is `manager` or `operator` of the Credit Strategy contract.
     */
    function _onlyOperator() internal view {
        if (!CreditStrategy(creditStrategy).isOperator(msg.sender)) {
            revert OnlyOperator();
        }
    }

    /**
     * @notice - Checks `msg.sender` is `creditStrategy`.
     */
    function _onlyCreditStrategy() internal view {
        if (msg.sender != address(creditStrategy)) {
            revert OnlyCreditStrategy();
        }
    }

    /**
     * @notice - Checks `target` is not zero address.
     */
    function _cannotBeZeroAddress(address target) internal pure {
        if (target == address(0)) {
            revert CannotBeZeroAddress();
        }
    }

    /**
     * @notice - Checks `newStrategy` has the same underlying asset as the LendingVault.
     */
    function _onlyNativeAsset(address newStrategy) internal view {
        if (address(underlyingAsset) != address(ILiquidStrategy(newStrategy).asset())) {
            revert OnlyNativeAsset();
        }
    }

    /**
     * @notice - Checks `msg.sender` is whitelisted to call LendingVault functions.
     */
    function _isWhitelisted() internal view {
        if (
            CreditStrategy(creditStrategy).whitelistEnabled()
                && !CreditStrategy(creditStrategy).whitelistedAddresses(msg.sender)
        ) {
            revert AddressNotWhitelisted();
        }
    }

    /**
     * @notice - Checks `amounts` and `addresses` are of same length.
     */
    function _arrayLengthsMustMatch(uint256[] memory amounts, address[] memory addresses) internal pure {
        if (amounts.length != addresses.length) {
            revert ArrayLengthsMustMatch();
        }
    }

    /**
     * @notice - Checks `assets` do not exceed the pending deposit request of the `owner`.
     */
    function _cannotExceedDepositRequest(address owner, uint256 assets) internal view {
        if (assets > pendingDepositRequest[REQUEST_ID][owner]) {
            revert ERC7540ExceededDepositRequest(owner, assets, pendingDepositRequest[REQUEST_ID][owner]);
        }
    }

    /**
     * @notice - Checks `shares` do not exceed the pending redeem request of the `owner`.
     */
    function _cannotExceedRedeemRequest(address owner, uint256 shares) internal view {
        if (shares > pendingRedeemRequest[REQUEST_ID][owner]) {
            revert ERC7540ExceededRedeemRequest(owner, shares, pendingRedeemRequest[REQUEST_ID][owner]);
        }
    }

    /**
     * @notice - Checks `shares` are greater than zero.
     */
    function _sharesCannotBeZero(uint256 shares) internal pure {
        if (shares == 0) {
            revert ERC4626ZeroShares();
        }
    }

    /**
     * @notice - Checks `assets` are greater than zero.
     */
    function _assetsCannotBeZero(uint256 assets) internal pure {
        if (assets == 0) {
            revert ERC4626ZeroAssets();
        }
    }

    /*//////////////////////////////////////////////////////////////
                        STRATEGY MANAGEMENT
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice   - Deposit assets into the Liquid Strategy or Credit Strategy contract
     * @dev      - Only callable by the `manager` of the Credit Strategy contract.
     * @dev      - Only callable if LendingVault has sufficient liquid assets.
     * @param amount         - Amount of assets to deposit
     * @param strategyType   - Type of strategy to deposit into. 0 for Liquid Strategy, 1 for Credit Strategy
     */
    function depositIntoStrategy(uint256 amount, uint256 strategyType) external {
        _onlyVaultManager();
        _assertVaultHasSufficientAvailableAssets(amount);
        if (strategyType > 0) {
            IERC20(underlyingAsset).forceApprove(address(creditStrategy), amount);
            CreditStrategy(creditStrategy).deposit(amount);
            IERC20(underlyingAsset).forceApprove(address(creditStrategy), 0);
        } else {
            IERC20(underlyingAsset).forceApprove(address(liquidStrategy), amount);
            liquidStrategy.deposit(amount);
            IERC20(underlyingAsset).forceApprove(address(liquidStrategy), 0);
        }
   }

    /**
     * @notice  - Migrate the Liquid Strategy contract to a new Liquid Strategy contract. Transfers all assets from the old strategy to the new liquid Strategy.
     * @dev     - Only callable by the `manager` of the Credit Strategy contract.
     * @dev     - Cannot migrate to a zero address.
     * @dev     - Cannot migrate to a strategy that does not hold the same underlying asset as the LendingVault.
     * @param _newLiquidStrategy - Address of the new strategy contract
     */
    function migrateLiquidStrategy(address _newLiquidStrategy) external {
        _onlyVaultManager();
        _onlyNativeAsset(_newLiquidStrategy);

        uint256 balanceBefore = IERC20(underlyingAsset).balanceOf(address(this));

        // Withdraw all assets from the old liquid strategy
        uint256 liquidStrategyBalance = IERC20(underlyingAsset).balanceOf(address(liquidStrategy));
        liquidStrategy.withdraw(liquidStrategyBalance);
        uint256 withdrawn = IERC20(underlyingAsset).balanceOf(address(this)) - balanceBefore;

        // Set the existing strategy's allowance to 0
        IERC20(underlyingAsset).forceApprove(address(liquidStrategy), 0);

        // Set the new liquid strategy
        liquidStrategy = ILiquidStrategy(_newLiquidStrategy);

        // Approve the new liquid strategy contract for the underlying asset
        IERC20(underlyingAsset).forceApprove(_newLiquidStrategy, withdrawn);

        // Deposit all assets into the new liquid strategy
        liquidStrategy.deposit(withdrawn);

        // Set approval to new liquid strategy
        IERC20(underlyingAsset).forceApprove(_newLiquidStrategy, 0);

        emit StrategyContractTransfered(_newLiquidStrategy);
    }

    /*//////////////////////////////////////////////////////////////
                        VAULT MANAGER INTERACTONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Withdraws assets from the CreditStrategy contract
     * @dev     - Only callable by the `manager` of the Credit Strategy contract.
     * @param amount - Amount of assets to withdraw from the Credit or Liquid Strategy contract
     * @param strategyType - Type of strategy to withdraw from. 0 for Liquid Strategy, 1 for Credit Strategy
     */
    function withdrawFromStrategy(uint256 amount, uint256 strategyType) external {
        _onlyVaultManager();
        if (strategyType > 0) {
            CreditStrategy(creditStrategy).withdraw(amount);
        } else {
            liquidStrategy.withdraw(amount);
        }
    }

    /**
     * @notice - Callback to mint shares for the management fee and performance fees.
     * @dev    - Only callable by the Credit Strategy contract.
     * @param receiver - Address to receive the minted shares
     * @param shares   - Amount of shares to mint
     */
    function mintFeeShares(address receiver, uint256 shares) public {
        _onlyCreditStrategy();
        _mint(receiver, shares);
    }

    /**
     * @notice  - Transfers ERC20 tokens from the LendingVault contract to arbitrary `to` destination. Can be used to recover tokens not in the underlying asset or in emergency situations.
     * @dev     - Only callable by the `manager` of the Credit Strategy contract.
     * @param token     - Address of the ERC20 token to recover.
     * @param to        - Address to send the recovered tokens to.
     * @param amount    - Amount of tokens to recover.
     */
    function recoverTokens(address token, address to, uint256 amount) external {
        _onlyVaultManager();
        IERC20(token).safeTransfer(to, amount);
        emit TokensRecovered(msg.sender, to, token, amount);
    }

    /*//////////////////////////////////////////////////////////////
                        PREVIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    // ERC7540 overrides ERC4626 specification
    function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
        revert();
    }

    // ERC7540 overrides ERC4626 specification
    function previewMint(uint256 shares) public view virtual override returns (uint256) {
        revert();
    }

    // ERC7540 overrides ERC4626 specification
    function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
        revert();
    }

    // ERC7540 overrides ERC4626 specification
    function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
        revert();
    }

    /*//////////////////////////////////////////////////////////////
                        PAUSABLE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice - Pauses request functions and the ability to deposit or withdraw assets.
     * @dev    - Only callable by pausers.
     */
    function pause() public {
        _onlyPauser();
        CreditStrategy(creditStrategy).accrueFees();
        _pause();
    }

    /**
     * @notice - Unpauses request functions and the ability to deposit or withdraw assets.
     * @dev    - Only callable by pausers.
     */
    function unpause() public {
        _onlyPauser();
        CreditStrategy(creditStrategy).setLastManagementFeeTimestamp();
        _unpause();
    }

    /*//////////////////////////////////////////////////////////////
                SUPPORTS ERC-165, ERC-7575, ERC-7540
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice - Returns true if the contract supports the ERC-7540, ERC7575, or ERC165 contract interfaces.
     * @param interfaceId - interface ID to check for support.
     * @return            - True if the contract supports the ERC-7540 interface.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControl) returns (bool) {
        return interfaceId == type(IERC7540Deposit).interfaceId // asynchronous deposit vault
            || interfaceId == type(IERC7540Redeem).interfaceId // asynchronous redemption vault
            || interfaceId == type(IERC7540Operator).interfaceId // operator management
            || interfaceId == type(IERC165).interfaceId || interfaceId == type(IERC7575).interfaceId
            || super.supportsInterface(interfaceId); // access control
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice - The total amount of underlying tokens the vault holds or has claim to.
     * @return - Total amount of underlying tokens the vault holds, has deployed to credit lines, and interest owed or repaid on those credit lines.
     *
     */
    function totalAssets() public view override returns (uint256) {
        return totalLiquidAssets() + CreditStrategy(creditStrategy).totalActiveCredit();
    }

    /**
     * @notice - The total amount of underlying tokens the vault currently holds.
     * @return - The total amount of underlying tokens the vault holds.
     */
    function totalLiquidAssets() public view virtual returns (uint256) {
        uint256 liquidStrategyAssets = address(liquidStrategy) == address(0) ? 0 : liquidStrategy.totalAssets();
        uint256 creditStrategyAssets = IERC20(underlyingAsset).balanceOf(creditStrategy);
        return IERC20(underlyingAsset).balanceOf(address(this)) - totalPendingDeposits - totalClaimableRedeemAssets + liquidStrategyAssets
            + creditStrategyAssets;
    }

    /**
     * @notice  - Amount of underlying tokens a user's vault shares is redeemable for.
     * @param user - Address to get the underlying balance of.
     * @return     - User's vault balance in underlying tokens.
     */
    function balanceOfUnderlying(address user) public view returns (uint256) {
        return _convertToAssets(balanceOf(user), Math.Rounding.Floor);
    }

    /**
     * @notice  - The value of a single vault share expressed in terms of underlying tokens the vault holds, has deployed to credit lines, and interest owed or repaid on those credit lines.
     * @return  - The value of a single vault share expressed in terms of underlying tokens.
     */
    function sharePrice() public view returns (uint256) {
        // Get the total supply of shares.
        if (totalSupply() != 0) {
            // Calculate the exchange rate by dividing the total holdings by the share supply.
            return totalAssets().mulDiv(baseUnit, totalSupply());
        }
        // If there are no shares in circulation, return an exchange rate of 1:1.
        return baseUnit;
    }


    /*//////////////////////////////////////////////////////////////
                        Request LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Transfers assets from msg.sender into the Vault and submits a Request for asynchronous deposit. Because REQUEST_ID == 0, all requests are aggregated for a single owner.
     * @dev     - If whitelist is enabled, only whitelisted addresses can deposit.
     * @dev     - Cannot request to deposit 0 `assets`.
     * @dev     - Only the `owner` or an `operator` can request to deposit.
     * @param assets     - Amount of the underlying token to deposit.
     * @param controller - Address to receive newly minted shares.
     * @param owner      - Address of the owner of the assets.
     * @return requestId - Unique identifier for the request.
     */
    function requestDeposit(uint256 assets, address controller, address owner)
        external
        nonReentrant
        whenNotPaused
        returns (uint256 requestId)
    {
        _isWhitelisted();
        _assetsCannotBeZero(assets);
        _onlyOwnerOrOperator(owner);

        IERC20(underlyingAsset).safeTransferFrom(owner, address(this), assets); // asset here is the Vault underlying asset

        pendingDepositRequest[REQUEST_ID][controller] += assets;
        totalPendingDeposits += assets;

        emit DepositRequest(controller, owner, REQUEST_ID, msg.sender, assets);
        return REQUEST_ID;
    }

    /**
     * @notice  - Assumes control of shares from owner and submits a Request for asynchronous redeem.  Because REQUEST_ID == 0, all requests are aggregated for a single owner.
     * @dev     - Cannot request to redeem 0 `shares`.
     * @dev     - Only the `owner` or an `operator` can request to deposit.
     * @param shares     - Amount of shares to redeem.
     * @param controller - Address to receive the underlying tokens.
     * @param owner      - Address of the owner of the shares.
     * @return requestId - Unique identifier for the request.
     */
    function requestRedeem(uint256 shares, address controller, address owner)
        external
        nonReentrant
        whenNotPaused
        returns (uint256 requestId)
    {
        _isWhitelisted();
        _sharesCannotBeZero(shares);
        _onlyOwnerOrOperator(owner);

        // transfer shares to the Vault
        _transfer(owner, address(this), shares);

        pendingRedeemRequest[REQUEST_ID][controller] += shares;

        emit RedeemRequest(controller, owner, REQUEST_ID, msg.sender, shares);
        return REQUEST_ID;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Deposit a specific amount of underlying tokens and receive vault shares.
     * @dev     - If whitelist is enabled, only whitelisted addresses can deposit.
     * @dev     - Only the `owner` or an `operator` can deposit.
     * @param assets        - Amount of the underlying token to deposit.
     * @param receiver      - Address to receive shares.
     * @param controller    - Owner of the deposit Request, who can manage any actions related to the Request, including claiming the shares.
     * @return shares       - Amount of shares to transfer to receiver.
     */
    function deposit(uint256 assets, address receiver, address controller)
        external
        nonReentrant
        whenNotPaused
        returns (uint256 shares)
    {
        shares = _baseDeposit(assets, controller);

        // transfer shares from vault to receiver
        _transfer(address(this), receiver, shares);

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

    /**
     * @notice  - Deposit a specific amount of underlying tokens and receive vault shares.
     * @dev     - If whitelist is enabled, only whitelisted addresses can deposit.
     * @dev     - Only the `owner` or an `operator` can deposit.
     * @param assets   - Amount of the underlying token to deposit.
     * @param receiver - Address to receive shares.
     * @return shares  - Amount of shares to transfer to receiver.
     */
    function deposit(uint256 assets, address receiver)
        public
        override
        nonReentrant
        whenNotPaused
        returns (uint256 shares)
    {
        shares = _baseDeposit(assets, receiver);

        // transfer shares from vault to receiver
        _transfer(address(this), receiver, shares);

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

    /**
     * @notice  - Deposit a specific amount of underlying tokens and receive vault shares.
     * @dev     - If whitelist is enabled, only whitelisted addresses can deposit.
     * @dev     - Only the `owner` or an `operator` can deposit.
     * @param assets        - Amount of the underlying token to deposit.
     * @param controller    - Owner of the deposit Request, who can manage any actions related to the Request, including claiming the shares.
     * @return shares       - Amount of shares.
     */
    function _baseDeposit(uint256 assets, address controller) internal returns (uint256 shares) {
        _isWhitelisted();
        _onlyOwnerOrOperator(controller);
        _assetsCannotBeZero(assets);

        uint256 maxAssets = maxDeposit(controller);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(controller, assets, maxAssets);
        }

        // determine pro-rata shares
        uint256 maxShares = maxMint(controller);
        shares = maxShares.mulDiv(assets, maxAssets);
        _sharesCannotBeZero(shares);

        // update vault accounting
        claimableDepositRequest[REQUEST_ID][controller] -= assets;
        claimableDepositRequestShares[REQUEST_ID][controller] -= shares;
    }

    /**
     * @notice  - Receive a specific amount of vault shares in exchange for underlying tokens.
     * @dev     - If whitelist is enabled, only whitelisted addresses can deposit.
     * @dev     - Only the `owner` or an `operator` can deposit.
     * @param shares        - Amount of the shares to mint.
     * @param receiver      - Address to receive newly minted shares.
     * @param controller    - Owner of the deposit Request, who can manage any actions related to the Request, including claiming the assets.
     * @return assets       - Amount of underlying tokens deposited to mint shares.
     */
    function mint(uint256 shares, address receiver, address controller)
        external
        nonReentrant
        whenNotPaused
        returns (uint256 assets)
    {
        assets = _baseMint(shares, controller);

        // transfer shares from vault to receiver
        _transfer(address(this), receiver, shares);

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

    /**
     * @notice  - Receive a specific amount of vault shares in exchange for underlying tokens.
     * @dev     - If whitelist is enabled, only whitelisted addresses can deposit.
     * @dev     - Only the `owner` or an `operator` can deposit.
     * @param shares   - Amount of the shares to mint.
     * @param receiver - Address to receive newly minted shares.
     * @return assets  - Amount of underlying tokens deposited to mint shares.
     */
    function mint(uint256 shares, address receiver)
        public
        override
        nonReentrant
        whenNotPaused
        returns (uint256 assets)
    {
        assets = _baseMint(shares, receiver);

        // transfer shares from vault to receiver
        _transfer(address(this), receiver, shares);

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

    /**
     * @notice  - Mint a specific amount of vault shares in exchange for underlying tokens.
     * @dev     - If whitelist is enabled, only whitelisted addresses can deposit.
     * @dev     - Only the `owner` or an `operator` can deposit.
     * @param shares        - Amount of the shares to mint.
     * @param controller    - Owner of the deposit Request, who can manage any actions related to the Request, including claiming the assets.
     * @return assets       - Amount of underlying tokens deposited to mint shares.
     */
    function _baseMint(uint256 shares, address controller) internal returns (uint256 assets) {
        _isWhitelisted();
        _onlyOwnerOrOperator(controller);
        _sharesCannotBeZero(shares);

        uint256 maxShares = maxMint(controller);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(controller, shares, maxShares);
        }

        // determine pro-rata assets
        uint256 maxAssets = maxDeposit(controller);
        assets = maxAssets.mulDiv(shares, maxShares, Math.Rounding.Ceil);
        _assetsCannotBeZero(assets);

        // update vault accounting
        claimableDepositRequest[REQUEST_ID][controller] -= assets;
        claimableDepositRequestShares[REQUEST_ID][controller] -= shares;
    }

    /**
     * @notice  - Withdraw a specific amount of underlying tokens from owner's balance.
     * @dev     - Will revert if msg.sender does not have sufficient allowance from owner.
     * @dev     - Only the `owner` or an `operator` can withdraw.
     * @param assets        - Amount of underlying tokens to withdraw by burning shares.
     * @param receiver      - Address to receive underlying tokens after burning shares.
     * @param controller    - Address where underlying tokens are withdrawn from.
     * @return shares       - Amount of shares burned to withdraw underlying tokens.
     */
    function withdraw(uint256 assets, address receiver, address controller)
        public
        override
        nonReentrant
        whenNotPaused
        returns (uint256 shares)
    {
        _isWhitelisted();
        _onlyOwnerOrOperator(controller);
        _assetsCannotBeZero(assets);

        uint256 maxAssets = maxWithdraw(controller);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxWithdraw(controller, assets, maxAssets);
        }

        // determine pro-rata shares
        uint256 maxShares = maxRedeem(controller);
        shares = maxShares.mulDiv(assets, maxAssets, Math.Rounding.Ceil);
        _sharesCannotBeZero(shares);

        // transfer assets to receiver
        _withdraw(controller, receiver, assets, shares);
    }

    /**
     * @notice  - Withdraws a specific amount of underlying tokens from owner's balance.
     * @dev     - Revert if there are not enough liquid assets to withdraw. See _assertVaultHasSufficientAvailableAssets() function.
     * @dev     - Called by withdraw() and redeem() functions.
     * @param controller    - Address where shares are burned.
     * @param receiver - Address to receive underlying tokens after burning shares.
     * @param assets   - Amount of underlying tokens to withdraw by burning shares.
     * @param shares   - Amount of shares to burn.
     */
    function _withdraw(address controller, address receiver, uint256 assets, uint256 shares) internal {

        // update vault accounting
        claimableRedeemRequest[REQUEST_ID][controller] -= shares;
        claimableRedeemRequestAssets[REQUEST_ID][controller] -= assets;
        totalClaimableRedeemAssets -= assets;

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

        IERC20(underlyingAsset).safeTransfer(receiver, assets);
    }

    /**
     * @notice  - Redeem a specific amount of shares for underlying tokens from owner.
     * @dev     - Reverts if msg.sender does not have sufficient allowance from owner.
     * @dev     - Only the `owner` or an `operator` can redeem.
     * @param shares        - Amount of shares to redeem for underlying tokens.
     * @param receiver      - Address to receive underlying tokens after burning shares.
     * @param controller    - Address where shares are burned to withdraw underlying tokens.
     * @return assets       - Amount of underlying tokens withdrawn.
     */
    function redeem(uint256 shares, address receiver, address controller)
        public
        override
        nonReentrant
        whenNotPaused
        returns (uint256 assets)
    {
        _isWhitelisted();
        _onlyOwnerOrOperator(controller);
        _sharesCannotBeZero(shares);

        uint256 maxShares = maxRedeem(controller);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxRedeem(controller, shares, maxShares);
        }

        // determine pro-rata assets
        uint256 maxAssets = maxWithdraw(controller);
        assets = maxAssets.mulDiv(shares, maxShares);
        _assetsCannotBeZero(assets);

        // transfer assets to receiver
        _withdraw(controller, receiver, assets, shares);
    }


    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - The maximum amount of underlying tokens the receiver can deposit into the vault. Per EIP-7540, Increases and decreases in sync with claimableDepositRequest.
     * @param receiver - Address to check the maximum deposit amount for.
     * @return         - Returns the maximum amount of underlying tokens a user can deposit into the vault.
     *
     */
    function maxDeposit(address receiver) public view override returns (uint256) {
        return claimableDepositRequest[REQUEST_ID][receiver];
    }

    /**
     * @notice  - The maximum amount of vault shares the receiver can mint with underlying tokens.
     * @param receiver - Address to check the maximum mint amount for.
     * @return         - Returns the maximum amount of vault shares the receiver can mint with underlying tokens.
     */
    function maxMint(address receiver) public view override returns (uint256) {
        return claimableDepositRequestShares[REQUEST_ID][receiver];
    }

    /**
     * @notice  - The maximum amount of assets the owner can withdraw from the vault. Increases and decreases in sync with claimableRedeemRequestAssets.
     * @param owner - Address to check the maximum withdraw amount for.
     * @return      - Returns the maximum amount of assets the owner can withdraw from the vault.
     */
    function maxWithdraw(address owner) public view override returns (uint256) {
        return claimableRedeemRequestAssets[REQUEST_ID][owner];
    }

    /**
     * @notice  - The maximum amount of vault shares the owner can redeem. Per EIP-7540, increases and decreases in sync with claimableRedeemRequest.
     * @param owner - Address to check the maximum redeem amount for.
     * @return      - Returns the maximum amount of shares the owner can redeem from the vault.
     */
    function maxRedeem(address owner) public view override returns (uint256) {
        return claimableRedeemRequest[REQUEST_ID][owner];
    }

    /*///////////////////////////////////////////////////////////////
                        ERC-7540 Functions
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Adds or remove operator approval for msg.sender.
     * @param operator - Address of the operator to add or remove.
     * @param approved - True to add operator, false to remove operator.
     * @return         - True if the operator was successfully added or removed.
     */
    function setOperator(address operator, bool approved) external returns (bool) {
        isOperator[msg.sender][operator] = approved;
        emit OperatorSet(msg.sender, operator, approved);
        return true;
    }

    /*///////////////////////////////////////////////////////////////
                    PROCESS AND CANCEL REQUESTS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Processes a deposit request by reducing the pending deposit request and increasing the claimable deposit request.
     * @dev     - Only callable by the `manager` or `operator` of the Credit Strategy contract.
     * @param controller    - Address of the owner of the assets.
     * @param assets        - Amount of the deposit request to move from PENDING to CLAIMABLE.
     * @param minShares     - The minimum amount of shares that can be minted.
     * @param maxShares     - The maximum amount of shares that can be minted.
     * @return              - True if the deposit request was successfully processed.
     */
    function processDepositRequest(address controller, uint256 assets, uint256 minShares, uint256 maxShares) external nonReentrant returns (bool) {
        _onlyOperator();

        CreditStrategy(creditStrategy).accrueFees(); // accrue fees before depositing assets to mint shares
        return _processDepositRequest(controller, assets, minShares, maxShares);
    }

    /**
     * @notice  - Processes multiple deposit requests by reducing the pending deposit request and increasing the claimable deposit request.
     * @dev     - Only callable by the `manager` or `operator` of the Credit Strategy contract.
     * @param controllers   - Addresses of the owners of the assets.
     * @param assets        - The amounts of the deposit requests to move from PENDING to CLAIMABLE.
     * @param minShares     - The minimum amounts of shares that can be minted.
     * @param maxShares     - The maximum amounts of shares that can be minted.
     * @return              - True if the deposit request was successfully processed.
     */
    function batchProcessDepositRequest(address[] memory controllers, uint256[] memory assets, uint256[] memory minShares, uint256[] memory maxShares)
        external
        nonReentrant
        returns (bool)
    {
        _onlyOperator();
        _arrayLengthsMustMatch(assets, controllers);

        CreditStrategy(creditStrategy).accrueFees(); // accrue fees before minting shares
        uint256 len = controllers.length;
        for (uint256 x = 0; x < len; x++) {
            _processDepositRequest(controllers[x], assets[x], minShares[x], maxShares[x]);
        }

        return true;
    }

    // See processDepositRequest()
    function _processDepositRequest(address controller, uint256 assets, uint256 minShares, uint256 maxShares) internal returns (bool) {
        _cannotExceedDepositRequest(controller, assets);

        pendingDepositRequest[REQUEST_ID][controller] -= assets;
        claimableDepositRequest[REQUEST_ID][controller] += assets;

        // check that the request is within the min and max limits
        uint256 sharesToDeposit = _convertToShares(assets, Math.Rounding.Floor);
        if (sharesToDeposit < minShares || sharesToDeposit > maxShares) {
            revert DepositRequestOutsideLimits(controller, sharesToDeposit, minShares, maxShares);
        }

        claimableDepositRequestShares[REQUEST_ID][controller] += sharesToDeposit;
        totalPendingDeposits -= assets;

        emit ProcessDepositRequest(controller, REQUEST_ID, sharesToDeposit, assets);

        // mint shares to the Lending Vault
        _mint(address(this), sharesToDeposit);

        return true;
    }

    /**
     * @notice  - Cancels a deposit request and sends tokens to receiver.
     * @dev     - Only callable by `controller` or its operators.
     * @param assets        - Amount of the deposit request to cancel.
     * @param receiver      - Address to receive assets from cancellation of Deposit Request.
     * @param controller    - Address of the owner of the Deposit Request.
     * @return              - True if the deposit request was successfully cancelled.
     */
    function cancelDepositRequest(uint256 assets, address receiver, address controller)
        external
        nonReentrant
        returns (bool)
    {
        _onlyOwnerOrOperator(controller);
        return _cancelDepositRequest(assets, receiver, controller);
    }

    // See cancelDepositRequest()
    function _cancelDepositRequest(uint256 assets, address receiver, address controller) internal returns (bool) {
        _cannotExceedDepositRequest(controller, assets);

        // remove assets from pending deposit request
        pendingDepositRequest[REQUEST_ID][controller] -= assets;
        totalPendingDeposits -= assets;

        // transfer funds back to owner
        IERC20(underlyingAsset).safeTransfer(receiver, assets); // asset here is the Vault underlying asset

        emit CancelDepositRequest(controller, receiver, REQUEST_ID, assets);
        return true;
    }

    /**
     * @notice  - Processes a redeem request by reducing the pending redeem request and increasing the claimable redeem request.
     * @dev     - Only callable by the `manager` or `operator` of the Credit Strategy contract.
     * @param controller    - Address of the owner of the shares.
     * @param shares        - The amount of the redeem request to move from PENDING to CLAIMABLE.
     * @param minAssets     - The minimum amount of assets that can be withdrawn.
     * @param maxAssets     - The maximum amount of assets that can be withdrawn.
     * @return              - True if the redeem request was successfully processed.
     */
    function processRedeemRequest(address controller, uint256 shares, uint256 minAssets, uint256 maxAssets) external nonReentrant returns (bool) {
        _onlyOperator();

        CreditStrategy(creditStrategy).accrueFees(); // accrue fees before redeeming shares
        return _processRedeemRequest(controller, shares, minAssets, maxAssets);
    }

    /**
     * @notice  - Processes multiple redeem requests by reducing the pending redeem request and increasing the claimable redeem request.
     * @dev     - Only callable by the `manager` or `operator` of the Credit Strategy contract.
     * @dev     - The vault manager or operator must ensure that the vault has sufficient liquidity to process the redemption requests.
     * @param controllers   - A list of addresses of the owners of the shares.
     * @param shares        - A list of amounts of the redeem request to move from PENDING to CLAIMABLE.
     * @param minAssets     - A list of minimum amounts of assets that can be withdrawn.
     * @param maxAssets     - A list of maximum amounts of assets that can be withdrawn.
     * @return              - True if the redeem request was successfully processed.
     */
    function batchProcessRedeemRequest(address[] memory controllers, uint256[] memory shares, uint256[] memory minAssets, uint256[] memory maxAssets)
        external
        nonReentrant
        returns (bool)
    {
        _onlyOperator();
        _arrayLengthsMustMatch(shares, controllers);
        _arrayLengthsMustMatch(minAssets, controllers);
        _arrayLengthsMustMatch(maxAssets, controllers);

        CreditStrategy(creditStrategy).accrueFees(); // accrue fees before redeeming shares
        uint256 len = controllers.length;
        for (uint256 x = 0; x < len; x++) {
            _processRedeemRequest(controllers[x], shares[x], minAssets[x], maxAssets[x]);
        }
        return true;
    }

    // See processRedeemRequest()
    function _processRedeemRequest(address controller, uint256 shares, uint256 minAssets, uint256 maxAssets) internal returns (bool) {
        _cannotExceedRedeemRequest(controller, shares);

        pendingRedeemRequest[REQUEST_ID][controller] -= shares;
        claimableRedeemRequest[REQUEST_ID][controller] += shares;

        // check if the vault has sufficient liquidity to process the redemption request
        uint256 assetsToWithdraw = _convertToAssets(shares, Math.Rounding.Floor);
        _assertVaultHasSufficientAvailableAssets(assetsToWithdraw);

        // check that the request is within the min and max limits
        if (assetsToWithdraw < minAssets || assetsToWithdraw > maxAssets) {
            revert RedeemRequestOutsideLimits(controller, assetsToWithdraw, minAssets, maxAssets);
        }

        // aggregate all claimable redeem requests
        claimableRedeemRequestAssets[REQUEST_ID][controller] += assetsToWithdraw;
        totalClaimableRedeemAssets += assetsToWithdraw;

        emit ProcessRedeemRequest(controller, REQUEST_ID, shares, assetsToWithdraw);

        // Note: LendingVault owns the controller's shares after requestRedeem
        _burn(address(this), shares);

        return true;
    }

    /**
     * @notice  - Cancels a redeem request.
     * @dev     - Only callable by `controller` or its operators.
     * @param shares        - Amount of the redeem request to cancel.
     * @param receiver      - Address of the owner of the shares.
     * @param controller    - Address of the owner of the shares.
     * @return              - True if the redeem request was successfully cancelled.
     */
    function cancelRedeemRequest(uint256 shares, address receiver, address controller)
        external
        nonReentrant
        returns (bool)
    {
        _onlyOwnerOrOperator(controller);
        return _cancelRedeemRequest(shares, receiver, controller);
    }

    // See processRedeemRequest()
    function _cancelRedeemRequest(uint256 shares, address receiver, address controller) internal returns (bool) {
        _cannotExceedRedeemRequest(controller, shares);

        pendingRedeemRequest[REQUEST_ID][controller] -= shares;

        // transfer shares to receiver
        _transfer(address(this), receiver, shares);

        emit CancelRedeemRequest(controller, receiver, REQUEST_ID, shares);
        return true;
    }

}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import {ILiquidStrategy} from "contracts/interfaces/ILiquidStrategy.sol";

interface ILendingVault {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event StrategyContractTransfered(address indexed newStrategy);

    event ProcessDepositRequest(address indexed controller, uint256 requestId, uint256 shares, uint256 assets);
    event ProcessRedeemRequest(address indexed controller, uint256 requestId, uint256 shares, uint256 assets);
    event CancelDepositRequest(address indexed controller, address indexed receiver, uint256 requestId, uint256 assets);
    event CancelRedeemRequest(address indexed controller, address indexed receiver, uint256 requestId, uint256 shares);

    event CreditStrategySet(address indexed creditStrategy);
    event LiquidStrategySet(address indexed liquidStrategy);

    event TokensRecovered(address indexed sender, address indexed receiver, address indexed token, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                                FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function mintFeeShares(address receiver, uint256 shares) external;

    function sharePrice() external view returns (uint256);

    function totalLiquidAssets() external view returns (uint256);

    function liquidStrategy() external view returns (ILiquidStrategy);

    function depositIntoStrategy(uint256 amount, uint256 strategyType) external;
    function withdrawFromStrategy(uint256 amount, uint256 strategyType) external;

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

    error InsufficientAvailableAssets();
    error DepositRequestOutsideLimits(address controller, uint256 sharesToDeposit, uint256 minShares, uint256 maxShares);
    error RedeemRequestOutsideLimits(address controller, uint256 assetsToWithdraw, uint256 minAssets, uint256 maxAssets);

    error OnlyPauser();
    error OnlyVaultManager();
    error OnlyOperator();
    error OnlyOwnerOrOperator();
    error OnlyCreditStrategy();
    error CreditStrategyAlreadySet();
    error OnlyDeployerFactory();
    error AddressNotWhitelisted();
    error OnlyNativeAsset();

    error CannotBeZeroAddress();
    error ArrayLengthsMustMatch();

    /*//////////////////////////////////////////////////////////////
                        ERC-4626 ERRORS
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Attempted to mint zero shares.
     */
    error ERC4626ZeroShares();

    /**
     * @dev Attempted to mint zero assets.
     */
    error ERC4626ZeroAssets();

    /**
     * @dev Attempted to deposit more assets than the pending deposit request for `receiver`.
     */
    error ERC7540ExceededDepositRequest(address receiver, uint256 assets, uint256 requestedDepositAssets);

    /**
     * @dev Attempted to redeem more assets than the pending redeem request for `receiver`.
     */
    error ERC7540ExceededRedeemRequest(address receiver, uint256 assets, uint256 requestedRedeemAssets);
}

pragma solidity 0.8.25;

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

interface ILiquidStrategy {
    /*//////////////////////////////////////////////////////////////
                                FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function asset() external view returns (IERC20);
    function totalAssets() external view returns (uint256);
    function deposit(uint256 _amount) external;
    function withdraw(uint256 _amount) external;
    function migrateFrom4626(address _currentVault, address _newVault, uint256 _minDepositAmount, uint256 _minSharesOut) external;
    function reallocateFunds(address[] memory targets, uint256[] memory amounts, bool[] memory isDeposit, uint256[] memory minAmountOuts) external;

    function setLendingVault(address) external;
    function setOracle(address) external;

    // Ethena
    function setEthenaTokens(address _sUSDe, address _USDe) external;
    function enableEthena(bool _enabled) external;

    // CoWSwap
    function approveTokenToCowSwap(address token, uint256 amount) external;
    function signCowSwapOrder(bytes calldata orderUid) external;
    function cancelCowSwapOrder(bytes calldata orderUid) external;

    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event LendingVaultSet(address indexed sender, address indexed lendingVault);
    event OracleSet(address indexed sender, address indexed oracle);

    event VaultDeposit(address indexed vault, address indexed token, uint256 assets, uint256 shares);
    event VaultWithdraw(address indexed vault, address indexed token, uint256 assets, uint256 shares);
    event StrategyExchange(address indexed from, address indexed to, uint256 amountFrom, uint256 amountOut);

    event VaultAdded(address indexed vault, address indexed asset);
    event VaultRemoved(address indexed vault, address indexed asset);

    event StrategyDeposit(address indexed sender, address token, uint256 indexed amount);
    event StrategyWithdraw(address indexed sender, address token, uint256 indexed amount);

    event TokensRecovered(address indexed sender, address indexed receiver, address indexed token, uint256 amount);

    // CowSwap events
    event OrderPlaced(bytes orderUid);
    event OrderCancelled(bytes orderUid);
    event TokensApproved(address token, uint256 amount);

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

    error OnlyLendingVault();
    error ArrayLengthsMustMatch();
    error LendingVaultAlreadySet();
    error VaultNotSupported();
    error VaultNotApproved();
    error InvalidVaultAddress();
    error CannotBeZeroAddress();
    error SlippageThresholdExceeded(address target, bool isDeposit, uint256 amount, uint256 minAmount);
}

pragma solidity 0.8.25;

import {IERC7575} from "contracts/interfaces/IERC7575.sol";

interface IERC7540Operator {
    /**
     * @dev The `controller` has set the `approved` status to an `operator`.
     *
     * - MUST be logged when the operator status is set.
     * - MAY be logged when the operator status is set to the same status it was before the current call.
     */
    event OperatorSet(address indexed controller, address indexed operator, bool approved);

    /**
     * @dev Grants or revokes permissions for `operator` to manage Requests on behalf of the `msg.sender`.
     *
     * - MUST set the operator status to the `approved` value.
     * - MUST log the `OperatorSet` event.
     * - MUST return True.
     */
    function setOperator(address operator, bool approved) external returns (bool);

    /**
     * @dev Returns `true` if the `operator` is approved as an operator for an `controller`.
     */
    function isOperator(address controller, address operator) external returns (bool status);
}

interface IERC7540Deposit {
    /**
     * @dev `owner` has locked `assets` in the Vault to Request a deposit with request ID `requestId`. `controller` controls this Request. `sender` is the caller of the `requestDeposit` which may not be equal to the `owner`.
     *
     * - MUST be emitted when a deposit Request is submitted using the requestDeposit method.
     */
    event DepositRequest(
        address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets
    );

    /**
     * @dev Transfers `assets` from `owner` into the Vault and submits a Request for asynchronous `deposit`.
     *
     * - MUST support ERC-20 `approve` / `transferFrom` on `asset` as a deposit Request flow.
     * - `owner` MUST equal `msg.sender` unless the `owner` has approved the `msg.sender` as an operator.
     * - MUST revert if all of `assets` cannot be requested for `deposit`/`mint`
     * - NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying `asset` token.
     * - MUST emit the `RequestDeposit` event.
     */
    function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId);

    /**
     * @dev The amount of requested `assets` in Pending state for the `controller` with the given `requestId` to `deposit` or `mint`.
     *
     * - MUST NOT include any `assets` in Claimable state for deposit or mint.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function pendingDepositRequest(uint256 requestId, address controller) external view returns (uint256 assets);

    /**
     * @dev The amount of requested `assets` in Claimable state for the `controller` with the given `requestId` to `deposit` or `mint`.
     *
     * - MUST NOT include any `assets` in Pending state for `deposit` or `mint`.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function claimableDepositRequest(uint256 requestId, address controller) external view returns (uint256 assets);

    /**
     * @dev Mints shares Vault shares to `receiver` by claiming the Request of the `controller`.
     *
     * - MUST revert unless `msg.sender` is either equal to `controller` or an operator approved by `controller`.
     * - MUST emit the `Deposit` event.
     * - 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).
     */
    function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares);

    /**
     * @dev Mints exactly shares Vault shares to `receiver` by claiming the Request of the `controller`.
     *
     * - MUST revert unless `msg.sender` is either equal to `controller` or an operator approved by `controller`.
     * - MUST emit the Deposit event.
     * - 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).
     */
    function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets);
}

interface IERC7540Redeem is IERC7540Operator {
    /**
     * @dev `sender` has locked `shares`, owned by `owner`, in the Vault to Request a redemption. `controller` controls this Request, but is not necessarily the `owner`.
     *
     * - MUST be emitted when a redemption Request is submitted using the `requestRedeem` method.
     */
    event RedeemRequest(
        address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 shares
    );

    /**
     * @dev Assumes control of `shares` from `owner` and submits a Request for asynchronous `redeem`.
     *
     * - MUST remove `shares` from the custody of `owner` upon `requestRedeem` and burned by the time the request is Claimed.
     *   where msg.sender has ERC-20 approval over the shares of owner.
     * - MUST revert if all of shares cannot be requested for `redeem` / `withdraw`
     * - MUST emit the `RequestRedeem` event.
     *
     */
    function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId);

    /**
     * @dev The amount of requested `shares` in Pending state for the `controller` with the given `requestId` to `redeem` or `withdraw`.
     *
     * - MUST NOT include any `shares` in Claimable state for `redeem` or `withdraw`.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function pendingRedeemRequest(uint256 requestId, address controller) external view returns (uint256 shares);

    /**
     * @dev The amount of requested `shares` in Claimable state for the `controller` with the given `requestId` to `redeem` or `withdraw`.
     *
     * - MUST NOT include any `shares` in Pending state for `redeem` or `withdraw`.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input.
     */
    function claimableRedeemRequest(uint256 requestId, address controller) external view returns (uint256 shares);
}

/**
 * @title  IERC7540
 * @dev    Interface of the ERC7540 "Asynchronous Tokenized Vault Standard", as defined in
 *         https://eips.ethereum.org/EIPS/eip-7540
 */
interface IERC7540 is IERC7540Operator, IERC7540Deposit, IERC7540Redeem {}

pragma solidity 0.8.25;

import {IERC165} from "openzeppelin/interfaces/IERC165.sol";

/**
 * @title  IERC7540
 * @dev    Interface of the ERC7575 "Multi-Asset ERC-4626 Vaults", as defined in
 *         https://eips.ethereum.org/EIPS/eip-7575
 */
interface IERC7575 {
    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 address of the share token
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function share() external view returns (address shareTokenAddress);

    /**
     * @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 controller 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 controller
     *   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 controller) 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 controller
     *   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 controller) external returns (uint256 assets);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

import {LineLib} from "../utils/LineLib.sol";
import {ICreditPositionToken} from "../interfaces/ICreditPositionToken.sol";
import {IInterestRateCredit} from "../interfaces/IInterestRateCredit.sol";

interface ILineOfCredit is IInterestRateCredit {
    // Constructor Params
    struct LineParams {
        address lineFactory;
        address tokenContract;
        address oracle;
        address admin;
        address borrower;
        bool recoveryEnabled;
        Fees fees;
    }

    // Lender data
    struct Credit {
        //  all denominated in token, not USD
        uint256 deposit; // The total liquidity provided by a Lender in a given token on a Line of Credit
        uint256 principal; // The amount of a Lender's Deposit on a Line of Credit that has actually been drawn down by the Borrower (in Tokens)
        uint256 interestAccrued; // Interest due by a Borrower but not yet repaid to the Line of Credit contract
        uint256 interestRepaid; // Interest repaid by a Borrower to the Line of Credit contract but not yet withdrawn by a Lender
        uint8 decimals; // Decimals of Credit Token for calcs
        address token; // The token being lent out (Credit Token)
        uint256 tokenId; // The person to repay
        bool isOpen; // Status of position
        bool isRestricted; // Whether the position can be traded
        uint16 earlyWithdrawalFee; // early withdrawal fee paid by lender in BPS
        uint256 deadline; // The timestamp at which the position will be liquidatable
    }

    // in BPS 4 decimals  fee = 50 loan amount = 10000 * (50/100)
    struct Fees {
        uint16 originationFee;
        uint16 swapFee;
        uint16 servicingFee;
    }

    enum FeeTypes {
        ORIGINATION,
        SWAP,
        SERVICING
    }

    // General Events
    event UpdateStatus(uint256 indexed status); // store as normal uint so it can be indexed in subgraph

    event DeployLine(
        address indexed lineFactory, address indexed oracle, address admin, address borrower, bool isRecoverable
    );

    event UpdateBorrower(address indexed borrower, address indexed newBorrower);
    event UpdateAdmin(address indexed oldAdmin, address indexed newAdmin);
    event UpdateServicer(address indexed oldServicer, address indexed newServicer);

    event SortedIntoQ(uint256 indexed id, uint256 indexed newIdx, uint256 indexed oldIdx, uint256 oldId);

    // MutualConsent borrower/lender events

    event AddCredit(uint256 indexed tokenId, uint256 indexed deposit);

    event SetRates(uint256 indexed id, uint128 indexed dRate, uint128 indexed fRate);

    event IncreaseCredit(uint256 indexed id, uint256 indexed deposit);

    event RefinanceCredit(uint256 indexed id, uint256 indexed amount, uint128 dRate, uint128 fRate, uint256 deadline);

    event SetDeadline(uint256 indexed id, uint256 indexed deadline);

    event SetFee(uint16 feeBps, FeeTypes indexed feeType);

    event TransferOriginationFee(uint256 indexed fee, address indexed to);

    event EarlyWithdrawalFee(uint256 indexed fee, address indexed lender, address indexed to);

    // Lender Events

    // Emits data re Lender removes funds (principal) - there is no corresponding function, just withdraw()
    event WithdrawDeposit(uint256 indexed id, uint256 indexed amount);

    // Emits data re Lender withdraws interest - there is no corresponding function, just withdraw()
    event WithdrawProfit(uint256 indexed id, uint256 indexed amount);

    // Emitted when any credit line is closed by the line's borrower or the position's lender
    event CloseCreditPosition(uint256 indexed id);

    // After accrueInterest runs, emits the amount of interest added to a Borrower's outstanding balance of interest due
    // but not yet repaid to the Line of Credit contract
    event InterestAccrued(uint256 indexed id, uint256 indexed amount);

    // Borrower Events

    // receive full line or drawdown on credit
    event Borrow(uint256 indexed id, uint256 indexed amount, address indexed to);

    // Emits that a Borrower has repaid an amount of interest Results in an increase in interestRepaid, i.e. interest not yet withdrawn by a Lender). There is no corresponding function
    event RepayInterest(uint256 indexed id, uint256 indexed amount);

    // Emits that a Borrower has repaid an amount of principal - there is no corresponding function
    event RepayPrincipal(uint256 indexed id, uint256 indexed amount);

    // Emits that a Borrower has paid servicing fee to protocol treasury
    event PayServicingFee(uint256 indexed id, uint256 indexed amount);

    event Default(uint256 indexed id);

    event ServicerApproved(address indexed servicer, bool indexed approved);

    event OtcSwapServicerUpdated(address indexed servicer);

    // Access Errors
    error NotActive();
    error NotBorrowing();
    error CallerAccessDenied();

    // Callback Errors
    error LenderIsNotCreditStrategy();

    // Tokens
    error NoTokenPrice();

    // Line
    error BadModule(address module);
    error NoLiquidity();
    error PositionExists();
    error CloseFailedWithPrincipal();
    error NotInsolvent(address module);
    error NotLiquidatable();
    error AlreadyInitialized();
    error PositionIsClosed();
    error RepayAmountExceedsDebt(uint256 totalAvailable);
    error CantStepQ();
    error EthSupportDisabled();
    error BorrowFailed();
    error RecoveryNotEnabled();
    error MaximumFeeExceeded();
    error InvalidAddress();

    error NoQueue();
    error NonceMismatch();
    error BadStatus();
    error InvalidDeadline();
    error EthNotSupported();

    // Fully public functions

    function init() external;

    // MutualConsent functions

    function setFees(Fees memory _fees) external;

    function addCredit(
        uint256 desiredNonce,
        uint128 drate,
        uint128 frate,
        uint256 amount,
        address token,
        address lender,
        bool isRestricted,
        uint16 earlyWithdrawalFee,
        uint256 deadline
    ) external returns (uint256);

    function refinanceCredit(
        uint256 desiredNonce,
        uint256 tokenId,
        uint256 amount,
        uint128 drate,
        uint128 frate,
        uint256 deadline
    ) external;

    // Borrower functions
    function updateBorrower(address newBorrower) external;
    function borrow(uint256 id, uint256 amount, address to) external;
    function depositAndRepay(uint256 amount) external;
    function depositAndClose() external;
    function close(uint256 id) external;
    function claimEarlyWithdrawalFees(uint256 tokenId) external;

    // Lender functions
    function withdraw(uint256 tokenId, uint256 amount) external;

    // Admin functions
    function updateAdmin(address newAdmin) external;
    function setServicer(address servicer, bool approved) external;

    // Recovery functions
    function recoverTokens(address token, address to) external;

    // State management functions
    function declareInsolvent() external;
    function accrueInterest() external;
    function healthcheck() external returns (LineLib.STATUS);
    function stepQ() external;
    function updateOutstandingDebt() external returns (uint256, uint256);

    // State getters
    function status() external view returns (LineLib.STATUS);
    function borrower() external view returns (address);
    function admin() external view returns (address);
    function isServicer(address) external view returns (bool);
    function protocolTreasury() external view returns (address);
    function counts() external view returns (uint256, uint256);
    function getCreditPosition(uint256 tokenId) external view returns (Credit memory);
    function nextInQ() external view returns (uint256, uint256, address, uint256, uint256, uint256, uint128, uint128);
    function available(uint256 id) external view returns (uint256, uint256);
    function tokenContract() external view returns (ICreditPositionToken);
    function interestAccrued(uint256 id) external view returns (uint256);
    function getFees() external view returns (Fees memory);
    function ids(uint256 idx) external returns (uint256);
    function recoveryEnabled() external view returns (bool);
    function nonce() external view returns (uint256);
    function getLineFactory() external view returns (address);
    function otcSwapServicer() external view returns (address);
    function setOtcSwapServicer(address servicer) external;
}

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

pragma solidity ^0.8.20;

import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

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

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

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

    /**
     * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
     *
     * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
     * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
     * one branch when needed, making this function more expensive.
     */
    function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
        unchecked {
            // branchless ternary works because:
            // b ^ (a ^ b) == a
            // b ^ 0 == b
            return b ^ ((a ^ b) * SafeCast.toUint(condition));
        }
    }

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }

        // The following calculation ensures accurate ceiling division without overflow.
        // Since a is non-zero, (a - 1) / b will not overflow.
        // The largest possible result occurs when (a - 1) / b is type(uint256).max,
        // but the largest value we can obtain is type(uint256).max - 1, which happens
        // when a = type(uint256).max and b = 1.
        unchecked {
            return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
        }
    }

    /**
     * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     *
     * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
            // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2²⁵⁶ + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
            if (denominator <= prod1) {
                Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

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

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

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
            inverse *= 2 - denominator * inverse; // inverse mod 2³²
            inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
            inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
            inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶

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

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

    /**
     * @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
     *
     * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
     * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
     *
     * If the input value is not inversible, 0 is returned.
     *
     * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
     * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
     */
    function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
        unchecked {
            if (n == 0) return 0;

            // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
            // Used to compute integers x and y such that: ax + ny = gcd(a, n).
            // When the gcd is 1, then the inverse of a modulo n exists and it's x.
            // ax + ny = 1
            // ax = 1 + (-y)n
            // ax ≡ 1 (mod n) # x is the inverse of a modulo n

            // If the remainder is 0 the gcd is n right away.
            uint256 remainder = a % n;
            uint256 gcd = n;

            // Therefore the initial coefficients are:
            // ax + ny = gcd(a, n) = n
            // 0a + 1n = n
            int256 x = 0;
            int256 y = 1;

            while (remainder != 0) {
                uint256 quotient = gcd / remainder;

                (gcd, remainder) = (
                    // The old remainder is the next gcd to try.
                    remainder,
                    // Compute the next remainder.
                    // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
                    // where gcd is at most n (capped to type(uint256).max)
                    gcd - remainder * quotient
                );

                (x, y) = (
                    // Increment the coefficient of a.
                    y,
                    // Decrement the coefficient of n.
                    // Can overflow, but the result is casted to uint256 so that the
                    // next value of y is "wrapped around" to a value between 0 and n - 1.
                    x - y * int256(quotient)
                );
            }

            if (gcd != 1) return 0; // No inverse exists.
            return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
        }
    }

    /**
     * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
     *
     * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
     * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
     * `a**(p-2)` is the modular multiplicative inverse of a in Fp.
     *
     * NOTE: this function does NOT check that `p` is a prime greater than `2`.
     */
    function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
        unchecked {
            return Math.modExp(a, p - 2, p);
        }
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
     *
     * Requirements:
     * - modulus can't be zero
     * - underlying staticcall to precompile must succeed
     *
     * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
     * sure the chain you're using it on supports the precompiled contract for modular exponentiation
     * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
     * the underlying function will succeed given the lack of a revert, but the result may be incorrectly
     * interpreted as 0.
     */
    function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
        (bool success, uint256 result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
     * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
     * to operate modulo 0 or if the underlying precompile reverted.
     *
     * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
     * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
     * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
     * of a revert, but the result may be incorrectly interpreted as 0.
     */
    function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
        if (m == 0) return (false, 0);
        assembly ("memory-safe") {
            let ptr := mload(0x40)
            // | Offset    | Content    | Content (Hex)                                                      |
            // |-----------|------------|--------------------------------------------------------------------|
            // | 0x00:0x1f | size of b  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x20:0x3f | size of e  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x40:0x5f | size of m  | 0x0000000000000000000000000000000000000000000000000000000000000020 |
            // | 0x60:0x7f | value of b | 0x<.............................................................b> |
            // | 0x80:0x9f | value of e | 0x<.............................................................e> |
            // | 0xa0:0xbf | value of m | 0x<.............................................................m> |
            mstore(ptr, 0x20)
            mstore(add(ptr, 0x20), 0x20)
            mstore(add(ptr, 0x40), 0x20)
            mstore(add(ptr, 0x60), b)
            mstore(add(ptr, 0x80), e)
            mstore(add(ptr, 0xa0), m)

            // Given the result < m, it's guaranteed to fit in 32 bytes,
            // so we can use the memory scratch space located at offset 0.
            success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
            result := mload(0x00)
        }
    }

    /**
     * @dev Variant of {modExp} that supports inputs of arbitrary length.
     */
    function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
        (bool success, bytes memory result) = tryModExp(b, e, m);
        if (!success) {
            Panic.panic(Panic.DIVISION_BY_ZERO);
        }
        return result;
    }

    /**
     * @dev Variant of {tryModExp} that supports inputs of arbitrary length.
     */
    function tryModExp(
        bytes memory b,
        bytes memory e,
        bytes memory m
    ) internal view returns (bool success, bytes memory result) {
        if (_zeroBytes(m)) return (false, new bytes(0));

        uint256 mLen = m.length;

        // Encode call args in result and move the free memory pointer
        result = abi.encodePacked(b.length, e.length, mLen, b, e, m);

        assembly ("memory-safe") {
            let dataPtr := add(result, 0x20)
            // Write result on top of args to avoid allocating extra memory.
            success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
            // Overwrite the length.
            // result.length > returndatasize() is guaranteed because returndatasize() == m.length
            mstore(result, mLen)
            // Set the memory pointer after the returned data.
            mstore(0x40, add(dataPtr, mLen))
        }
    }

    /**
     * @dev Returns whether the provided byte array is zero.
     */
    function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
        for (uint256 i = 0; i < byteArray.length; ++i) {
            if (byteArray[i] != 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * This method is based on Newton's method for computing square roots; the algorithm is restricted to only
     * using integer operations.
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        unchecked {
            // Take care of easy edge cases when a == 0 or a == 1
            if (a <= 1) {
                return a;
            }

            // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
            // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
            // the current value as `ε_n = | x_n - sqrt(a) |`.
            //
            // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
            // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
            // bigger than any uint256.
            //
            // By noticing that
            // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
            // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
            // to the msb function.
            uint256 aa = a;
            uint256 xn = 1;

            if (aa >= (1 << 128)) {
                aa >>= 128;
                xn <<= 64;
            }
            if (aa >= (1 << 64)) {
                aa >>= 64;
                xn <<= 32;
            }
            if (aa >= (1 << 32)) {
                aa >>= 32;
                xn <<= 16;
            }
            if (aa >= (1 << 16)) {
                aa >>= 16;
                xn <<= 8;
            }
            if (aa >= (1 << 8)) {
                aa >>= 8;
                xn <<= 4;
            }
            if (aa >= (1 << 4)) {
                aa >>= 4;
                xn <<= 2;
            }
            if (aa >= (1 << 2)) {
                xn <<= 1;
            }

            // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
            //
            // We can refine our estimation by noticing that the middle of that interval minimizes the error.
            // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
            // This is going to be our x_0 (and ε_0)
            xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)

            // From here, Newton's method give us:
            // x_{n+1} = (x_n + a / x_n) / 2
            //
            // One should note that:
            // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
            //              = ((x_n² + a) / (2 * x_n))² - a
            //              = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
            //              = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
            //              = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
            //              = (x_n² - a)² / (2 * x_n)²
            //              = ((x_n² - a) / (2 * x_n))²
            //              ≥ 0
            // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
            //
            // This gives us the proof of quadratic convergence of the sequence:
            // ε_{n+1} = | x_{n+1} - sqrt(a) |
            //         = | (x_n + a / x_n) / 2 - sqrt(a) |
            //         = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
            //         = | (x_n - sqrt(a))² / (2 * x_n) |
            //         = | ε_n² / (2 * x_n) |
            //         = ε_n² / | (2 * x_n) |
            //
            // For the first iteration, we have a special case where x_0 is known:
            // ε_1 = ε_0² / | (2 * x_0) |
            //     ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
            //     ≤ 2**(2*e-4) / (3 * 2**(e-1))
            //     ≤ 2**(e-3) / 3
            //     ≤ 2**(e-3-log2(3))
            //     ≤ 2**(e-4.5)
            //
            // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
            // ε_{n+1} = ε_n² / | (2 * x_n) |
            //         ≤ (2**(e-k))² / (2 * 2**(e-1))
            //         ≤ 2**(2*e-2*k) / 2**e
            //         ≤ 2**(e-2*k)
            xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5)  -- special case, see above
            xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9)    -- general case with k = 4.5
            xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18)   -- general case with k = 9
            xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36)   -- general case with k = 18
            xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72)   -- general case with k = 36
            xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144)  -- general case with k = 72

            // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
            // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
            // sqrt(a) or sqrt(a) + 1.
            return xn - SafeCast.toUint(xn > a / xn);
        }
    }

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

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 x) internal pure returns (uint256 r) {
        // If value has upper 128 bits set, log2 result is at least 128
        r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
        // If upper 64 bits of 128-bit half set, add 64 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
        // If upper 32 bits of 64-bit half set, add 32 to result
        r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
        // If upper 16 bits of 32-bit half set, add 16 to result
        r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
        // If upper 8 bits of 16-bit half set, add 8 to result
        r |= SafeCast.toUint((x >> r) > 0xff) << 3;
        // If upper 4 bits of 8-bit half set, add 4 to result
        r |= SafeCast.toUint((x >> r) > 0xf) << 2;

        // Shifts value right by the current result and use it as an index into this lookup table:
        //
        // | x (4 bits) |  index  | table[index] = MSB position |
        // |------------|---------|-----------------------------|
        // |    0000    |    0    |        table[0] = 0         |
        // |    0001    |    1    |        table[1] = 0         |
        // |    0010    |    2    |        table[2] = 1         |
        // |    0011    |    3    |        table[3] = 1         |
        // |    0100    |    4    |        table[4] = 2         |
        // |    0101    |    5    |        table[5] = 2         |
        // |    0110    |    6    |        table[6] = 2         |
        // |    0111    |    7    |        table[7] = 2         |
        // |    1000    |    8    |        table[8] = 3         |
        // |    1001    |    9    |        table[9] = 3         |
        // |    1010    |   10    |        table[10] = 3        |
        // |    1011    |   11    |        table[11] = 3        |
        // |    1100    |   12    |        table[12] = 3        |
        // |    1101    |   13    |        table[13] = 3        |
        // |    1110    |   14    |        table[14] = 3        |
        // |    1111    |   15    |        table[15] = 3        |
        //
        // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
        assembly ("memory-safe") {
            r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
        }
    }

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

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

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

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        uint256 isGt;
        unchecked {
            isGt = SafeCast.toUint(value > (1 << 128) - 1);
            value >>= isGt * 128;
            result += isGt * 16;

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

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

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

            result += SafeCast.toUint(value > (1 << 8) - 1);
        }
        return result;
    }

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

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

pragma solidity ^0.8.20;

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

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

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

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

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

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

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

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

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

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

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

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

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

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * 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}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

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

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

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

        emit Transfer(from, to, value);
    }

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC20, IERC20Metadata, ERC20} from "../ERC20.sol";
import {SafeERC20} from "../utils/SafeERC20.sol";
import {IERC4626} from "../../../interfaces/IERC4626.sol";
import {Math} from "../../../utils/math/Math.sol";

/**
 * @dev Implementation of the ERC-4626 "Tokenized Vault Standard" as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 *
 * This extension allows the minting and burning of "shares" (represented using the ERC-20 inheritance) in exchange for
 * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
 * the ERC-20 standard. Any additional extensions included along it would affect the "shares" token represented by this
 * contract and not the "assets" token which is an independent contract.
 *
 * [CAUTION]
 * ====
 * In empty (or nearly empty) ERC-4626 vaults, deposits are at high risk of being stolen through frontrunning
 * with a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
 * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
 * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
 * similarly be affected by slippage. Users can protect against this attack as well as unexpected slippage in general by
 * verifying the amount received is as expected, using a wrapper that performs these checks such as
 * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
 *
 * Since v4.9, this implementation introduces configurable virtual assets and shares to help developers mitigate that risk.
 * The `_decimalsOffset()` corresponds to an offset in the decimal representation between the underlying asset's decimals
 * and the vault decimals. This offset also determines the rate of virtual shares to virtual assets in the vault, which
 * itself determines the initial exchange rate. While not fully preventing the attack, analysis shows that the default
 * offset (0) makes it non-profitable even if an attacker is able to capture value from multiple user deposits, as a result
 * of the value being captured by the virtual shares (out of the attacker's donation) matching the attacker's expected gains.
 * With a larger offset, the attack becomes orders of magnitude more expensive than it is profitable. More details about the
 * underlying math can be found xref:erc4626.adoc#inflation-attack[here].
 *
 * The drawback of this approach is that the virtual shares do capture (a very small) part of the value being accrued
 * to the vault. Also, if the vault experiences losses, the users try to exit the vault, the virtual shares and assets
 * will cause the first user to exit to experience reduced losses in detriment to the last users that will experience
 * bigger losses. Developers willing to revert back to the pre-v4.9 behavior just need to override the
 * `_convertToShares` and `_convertToAssets` functions.
 *
 * To learn more, check out our xref:ROOT:erc4626.adoc[ERC-4626 guide].
 * ====
 */
abstract contract ERC4626 is ERC20, IERC4626 {
    using Math for uint256;

    IERC20 private immutable _asset;
    uint8 private immutable _underlyingDecimals;

    /**
     * @dev Attempted to deposit more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max);

    /**
     * @dev Attempted to mint more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max);

    /**
     * @dev Attempted to withdraw more assets than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max);

    /**
     * @dev Attempted to redeem more shares than the max amount for `receiver`.
     */
    error ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max);

    /**
     * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC-20 or ERC-777).
     */
    constructor(IERC20 asset_) {
        (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
        _underlyingDecimals = success ? assetDecimals : 18;
        _asset = asset_;
    }

    /**
     * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
     */
    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);
    }

    /**
     * @dev Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This
     * "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the
     * asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
     *
     * See {IERC20Metadata-decimals}.
     */
    function decimals() public view virtual override(IERC20Metadata, ERC20) returns (uint8) {
        return _underlyingDecimals + _decimalsOffset();
    }

    /** @dev See {IERC4626-asset}. */
    function asset() public view virtual returns (address) {
        return address(_asset);
    }

    /** @dev See {IERC4626-totalAssets}. */
    function totalAssets() public view virtual returns (uint256) {
        return _asset.balanceOf(address(this));
    }

    /** @dev See {IERC4626-convertToShares}. */
    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-convertToAssets}. */
    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxDeposit}. */
    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxMint}. */
    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /** @dev See {IERC4626-maxWithdraw}. */
    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-maxRedeem}. */
    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf(owner);
    }

    /** @dev See {IERC4626-previewDeposit}. */
    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-previewMint}. */
    function previewMint(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewWithdraw}. */
    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        return _convertToShares(assets, Math.Rounding.Ceil);
    }

    /** @dev See {IERC4626-previewRedeem}. */
    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return _convertToAssets(shares, Math.Rounding.Floor);
    }

    /** @dev See {IERC4626-deposit}. */
    function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
        uint256 maxAssets = maxDeposit(receiver);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxDeposit(receiver, assets, maxAssets);
        }

        uint256 shares = previewDeposit(assets);
        _deposit(_msgSender(), receiver, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-mint}. */
    function mint(uint256 shares, address receiver) public virtual returns (uint256) {
        uint256 maxShares = maxMint(receiver);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxMint(receiver, shares, maxShares);
        }

        uint256 assets = previewMint(shares);
        _deposit(_msgSender(), receiver, assets, shares);

        return assets;
    }

    /** @dev See {IERC4626-withdraw}. */
    function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxAssets = maxWithdraw(owner);
        if (assets > maxAssets) {
            revert ERC4626ExceededMaxWithdraw(owner, assets, maxAssets);
        }

        uint256 shares = previewWithdraw(assets);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return shares;
    }

    /** @dev See {IERC4626-redeem}. */
    function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {
        uint256 maxShares = maxRedeem(owner);
        if (shares > maxShares) {
            revert ERC4626ExceededMaxRedeem(owner, shares, maxShares);
        }

        uint256 assets = previewRedeem(shares);
        _withdraw(_msgSender(), receiver, owner, assets, shares);

        return assets;
    }

    /**
     * @dev Internal conversion function (from assets to shares) with support for rounding direction.
     */
    function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256) {
        return assets.mulDiv(totalSupply() + 10 ** _decimalsOffset(), totalAssets() + 1, rounding);
    }

    /**
     * @dev Internal conversion function (from shares to assets) with support for rounding direction.
     */
    function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
        return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** _decimalsOffset(), rounding);
    }

    /**
     * @dev Deposit/mint common workflow.
     */
    function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual {
        // If _asset is ERC-777, `transferFrom` can trigger a reentrancy BEFORE the transfer happens through the
        // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
        // assets are transferred and before the shares are minted, which is a valid state.
        // slither-disable-next-line reentrancy-no-eth
        SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
        _mint(receiver, shares);

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

    /**
     * @dev Withdraw/redeem common workflow.
     */
    function _withdraw(
        address caller,
        address receiver,
        address owner,
        uint256 assets,
        uint256 shares
    ) internal virtual {
        if (caller != owner) {
            _spendAllowance(owner, caller, shares);
        }

        // If _asset is ERC-777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
        // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
        // calls the vault, which is assumed not malicious.
        //
        // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
        // shares are burned and after the assets are transferred, which is a valid state.
        _burn(owner, shares);
        SafeERC20.safeTransfer(_asset, receiver, assets);

        emit Withdraw(caller, receiver, owner, assets, shares);
    }

    function _decimalsOffset() internal view virtual returns (uint8) {
        return 0;
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

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

    uint256 private _status;

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

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

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

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

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

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

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

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

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

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

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

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

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControl is Context, IAccessControl, ERC165 {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    mapping(bytes32 role => RoleData) private _roles;

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        return _roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        bytes32 previousAdminRole = getRoleAdmin(role);
        _roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        if (!hasRole(role, account)) {
            _roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        if (hasRole(role, account)) {
            _roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

// Openzeppelin imports
import {Math} from "openzeppelin/utils/math/Math.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {ERC20} from "openzeppelin/token/ERC20/ERC20.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "openzeppelin/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC721} from "openzeppelin/interfaces/IERC721.sol";
import {IERC165} from "openzeppelin/interfaces/IERC165.sol";

// Line-Of-Credit-v2
import {LineLib} from "Line-Of-Credit-v2/utils/LineLib.sol";
import {MutualConsent} from "Line-Of-Credit-v2/utils/MutualConsent.sol";
import {ILineOfCredit} from "Line-Of-Credit-v2/interfaces/ILineOfCredit.sol";
import {ISpigotedLine} from "Line-Of-Credit-v2/interfaces/ISpigotedLine.sol";
import {ISecuredLine} from "Line-Of-Credit-v2/interfaces/ISecuredLine.sol";

// Vaults
import {FeeManager} from "contracts/strategies/credit/FeeManager.sol";
import {ERC721Manager} from "contracts/strategies/credit/ERC721Manager.sol";
import {ILendingVault} from "contracts/interfaces/ILendingVault.sol";
import {ICreditStrategy} from "contracts/interfaces/ICreditStrategy.sol";
import {ILiquidStrategy} from "contracts/interfaces/ILiquidStrategy.sol";

/**
 * @title  - CreditStrategy
 * @author - Credit Cooperative
 * @notice - Interacts with Line-Of-Credit-v2 contracts on behalf of depositors to LendingVault.
 * @notice - Responsible for proposing, accepting, and impairing credit positions on behalf of the LendingVault.
 * @dev    - All external and public state changing functions, with the exception of swapCreditPositionForTokens, can only be called by the vault manager or vault operator roles.
 */
contract CreditStrategy is ERC721Manager, FeeManager, ICreditStrategy {
    using SafeERC20 for ERC20;

    // LineOfCredit mappings for tracking credit positions
    mapping(address => bool) private isLine;
    mapping(uint256 => address) public creditTokenIdToLine;
    uint256[] public tokenIds; // token ids of all open credit positions.

    address public immutable asset;

    /**
     * @notice  - How to deploy a Credit Coop Vault Manager
     * @dev     - A CreditStrategy determines parameters for the LendingVault including the underlying asset of the vault, the manager and operator addresses, the protocol fee split, the performance fee, and whether to enable a whitelist or not. Then the Vault manager deploys the contract using the constuctor below.
     * @dev     - If the manager enables a whitelist, then the manager will need to whitelist addresses that can deposit assets into the vault. Once funds have been deposited into the vault, the manager will be able to deploy funds into lines of credit.
     * @param _asset            - Native asset address of the vault.
     * @param _manager          - Address that manages the vault.
     * @param _protocolTreasury - Address that accrues fees from the vault.
     * @param _protocolSplit    - Percentage of fees that accrue to the protocol treasury.
     * @param _fees             - Performance and management fees that the vault will charge.
     * @param _whitelistEnabled - Whether the vault whitelist is enabled or not.
     * @param _lendingVault     - Address of the LendingVault.
     */
    constructor(
        address _asset,
        address _manager,
        address _protocolTreasury,
        uint8 _protocolSplit,
        Fees memory _fees,
        bool _whitelistEnabled,
        address _lendingVault
    ) {
        if (_manager == address(0) || _lendingVault == address(0)) {
            revert ILendingVault.CannotBeZeroAddress();
        }

        asset = _asset;
        baseUnit = 10 ** IERC20Metadata(address(_asset)).decimals();
        highWaterMarkSharePrice = baseUnit; // set initial highWaterMarkSharePrice to baseUnit
        emit SetHighWaterMarkSharePrice(highWaterMarkSharePrice);

        manager = _manager;
        isOperator[_manager] = true;
        whitelistEnabled = _whitelistEnabled;
        emit SetWhitelistStatus(_whitelistEnabled);

        protocolTreasury = _protocolTreasury;
        lendingVault = _lendingVault;

        // assert and set protocolSplit and vault fees
        _assertMaxProtocolSplit(_protocolSplit);
        protocolSplit = _protocolSplit;
        emit UpdateProtocolSplit(protocolSplit);

        // assert and set vault fees
        _assertMaxFee(_fees.performance, FeeTypes.PERFORMANCE);
        _assertMaxFee(_fees.management, FeeTypes.MANAGEMENT);
        fees = _fees;

        lastManagementFeeTimestamp = block.timestamp;
    }

    /*///////////////////////////////////////////////////////////////
                        FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Returns the principal and interest owed across all credit positions owned by the CreditStrategy.
     * @return totalActiveCredit - Total principal and interest owed, including both interest accrued and interest repaid, across all credit positions.
     */
    function totalActiveCredit() public view returns (uint256 totalActiveCredit) {

        // Iterate through tokenIds array to get principal and interest owed for all credit positions
        uint256 len = tokenIds.length;
        for (uint256 i = 0; i < len; i++) {
            uint256 tokenId = tokenIds[i];
            address line = creditTokenIdToLine[tokenId];
            (uint256 deposit, uint256 interest) = getPositionActiveCredit(line, tokenId);

            // add credit deposit and interest to total
            totalActiveCredit += (deposit + interest);
        }
    }

    /**
     * @notice  - Indicates the amount of active credit (deposit and interest) for a credit position.
     * @param line      - Address of the line of credit.
     * @param tokenId   - Token id of the credit position.
     * @return deposit - Total amount of deposit owed for the credit position.
     * @return interest - Total amount of interest owed for the credit position.
     */
    function getPositionActiveCredit(address line, uint256 tokenId) public view returns (uint256 deposit, uint256 interest) {
        address tokenContract = address(ILineOfCredit(line).tokenContract());
        bool isOwned = ownerOfTokenId(tokenContract, tokenId) == address(this);
        uint8 status = uint8(ILineOfCredit(line).status());

        if (
            line != address(0) // valid line
            && isOwned // credit position owned by credit strategy
            && status != uint8(LineLib.STATUS.INSOLVENT) // line is not INSOLVENT
            && status != uint8(LineLib.STATUS.ABORTED) // line is not ABORTED
        ) {
            ILineOfCredit.Credit memory credit = ILineOfCredit(line).getCreditPosition(tokenId);
            uint256 interestAccrued = ILineOfCredit(line).interestAccrued(tokenId);

            // only add accrued interest for open credit positions
            if (credit.isOpen) {
                interest += interestAccrued;
            }

            // add deposit to principal owed
            deposit = credit.deposit;

            // add interest repaid to interest owed
            interest += credit.interestRepaid;
        }
    }

    /**
     * @notice - Checks only the `buyToken` is equivalent to CreditStrategy `asset`.
     */
    function _onlyVaultAssetSupported(address buyToken) internal view {
        if (buyToken != address(asset)) {
            revert AssetNotSupported();
        }
    }

    /**
     * @notice - Checks only callable by `lendingVault` or `manager`.
     */
    function _onlyLendingVaultOrManager() internal view {
        if (msg.sender != lendingVault && msg.sender != manager) {
            revert OnlyLendingVaultOrManager();
        }
    }

   /**
     * @notice  - Returns the number of active credit positions owned by the CreditStrategy.
     * @return  - Count of credit positions.
     */
    function numCreditPositions() external view returns (uint256) {
        return tokenIds.length;
    }

    /**
     * @notice  - Approves a line of credit to spend a certain amount of the vault's assets.
     * @dev     - Only callable by the manager.
     * @param line      - Address of the line of credit to approve.
     * @param amount    - Amount of the vault's assets to approve the line of credit to spend.
     */
    function approve(address line, uint256 amount) external {
        _onlyVaultManager();
        ERC20(asset).forceApprove(line, amount);
    }

    /**
     * @notice  - Deposits assets into the CreditStrategy contract.
     * @dev     - Only callable by `lendingVault` or `manager`.
     * @param _amount - Amount to deposit (denominated in asset of the CreditStrategy contract).
     */
    function deposit(uint256 _amount) external {
        _onlyLendingVaultOrManager();
        ERC20(asset).safeTransferFrom(msg.sender, address(this), _amount);
        emit ManagerDeposit(msg.sender, _amount);
    }

    /**
     * @notice  - Withdraws assets from CreditStrategy contract and transfers them to the LendingVault.
     * @dev     - Only callable by `lendingVault` or `manager`.
     * @param _amount - Amount to withdraw (denominated in asset of the Strategy).
     */
    function withdraw(uint256 _amount) external {
        _onlyLendingVaultOrManager();
        ERC20(asset).safeTransfer(lendingVault, _amount);
        emit ManagerWithdraw(msg.sender, _amount);
    }

    /*//////////////////////////////////////////////////////////////
                SUPPORTS CreditStrategy.addCreditPosition
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice - Returns true if the contract supports the CreditStrategy.addCreditPosition function.
     * @param interfaceId - interface ID to check for support.
     * @return            - True if the contract supports the CreditStrategy.addCreditPosition function.
     */
    function supportsInterface(bytes4 interfaceId)
        public
        pure
        returns (bool)
    {
        // Return true if the provided interfaceId matches addCreditPosition
        return interfaceId == ICreditStrategy.addCreditPosition.selector
            || interfaceId == type(IERC165).interfaceId;
    }


    /**
     * @notice  - Proposes, or accepts a credit position to a line of credit with the addCredit() function in LineOfCredit. Usually, the vault will be proposing a credit position to a line of credit, but the vault can also accept a credit position proposed by a borrower.
     * @dev     - Only callable by the manager. Will revert if the vault does not have sufficient liquid assets to propose the credit position. Will revert if the vault attempts to lend to a line for which it is the borrower.
     * @param line               - Address of the line of credit to propose the position to.
     * @param drate              - Drawn rate of the credit position.
     * @param frate              - Facility rate of the credit position.
     * @param amount             - Amount of assets to propose to the line of credit.
     * @param isRestricted       - Whether the credit position is restricted or not.
     * @param earlyWithdrawalFee - Early withdrawal fee of the credit position.
     * @param deadline           - Deadline of the credit position.
     * @return                   - Returns tuple (true, tokenId) if the credit position was proposed successfully, otherwise (false, 0).
     */
    function addCredit(
        address line,
        uint256 desiredNonce,
        uint128 drate,
        uint128 frate,
        uint256 amount,
        bool isRestricted,
        uint16 earlyWithdrawalFee,
        uint256 deadline
    ) external nonReentrant returns (bool, uint256 tokenId) {
        _onlyVaultManager();



        if (ILineOfCredit(line).borrower() == address(this) || ILineOfCredit(line).borrower() == address(lendingVault))
        {
            revert CannotLendToSelf();
        }

        // Add line to isLine mapping
        isLine[line] = true;

        tokenId = ILineOfCredit(line).addCredit(
            desiredNonce,
            drate,
            frate,
            amount,
            address(asset),
            address(this),
            isRestricted,
            earlyWithdrawalFee,
            deadline
        );
        return (true, tokenId);
    }

    /**
     * @notice  - Revokes vault's consent for a credit proposal (i.e. addCredit or refinanceCredit).
     * @dev     - Only callable by the `manager` or `operator`.
     * @dev     - Reverts if the function selector in the reconstructed message data is not a valid function selector for the revokeConsent() function in LineOfCredit.
     * @param line                 - Address of the line of credit to propose the position to.
     * @param tokenId              - TokenId of the credit position to revoke the proposal from.
     * @param reconstructedMsgData - The reconstructed message data of the proposal to revoke.
     */
    function revokeProposal(address line, uint256 tokenId, bytes calldata reconstructedMsgData) external nonReentrant {
        _onlyOperator();

        ISecuredLine(line).revokeConsent(tokenId, reconstructedMsgData);
    }

    /**
     * @notice  - Creates a proposal to refinance a credit position.
     * @dev     - Only callable by the manager.
     * @param line     - Address of the line of credit to make the proposal to.
     * @param tokenId  - TokenId of the credit position to refinance.
     * @param amount   - Amount of assets to add to the credit position.
     * @param dRate    - New drawn rate to set on the credit position.
     * @param fRate    - New facility rate to set on the credit position.
     * @param deadline - New deadline to propose to the credit position.
     */
    function refinanceCredit(
        address line,
        uint256 desiredNonce,
        uint256 tokenId,
        uint256 amount,
        uint128 dRate,
        uint128 fRate,
        uint256 deadline
    ) external nonReentrant returns (bool) {
        _onlyVaultManager();
        ILineOfCredit(line).refinanceCredit(desiredNonce, tokenId, amount, dRate, fRate, deadline);
        return true;
    }

    /**
     * @notice  - Repays a line of credit with the line's reserves.
     * @dev     - Only callable by the manager or operator.
     * @param line   - Address of the line of credit to repay.
     * @param amount - Amount of assets from reserves to repay the line of credit.
     */
    function useAndRepay(address line, uint256 amount) external nonReentrant {
        _onlyOperator();
        ISpigotedLine(line).useAndRepay(amount);
    }

    /**
     * @notice  - Withdraws credit from a credit position
     * @dev     - Only callable by the manager or operator.
     * @param line      - Address of the line of credit to withdraw from.
     * @param tokenId   - TokenId of the credit position to withdraw from.
     * @param amount    - Amount to withdraw from the credit position.
     *
     */
    function withdrawCredit(address line, uint256 tokenId, uint256 amount) external nonReentrant {
        _onlyOperator();
        if (amount == 0) {
            revert NoWithdrawableAssets();
        }

        (, uint256 interestRepaid) = ILineOfCredit(line).available(tokenId);
        _accrueFees();
        if (amount >= interestRepaid) {
            uint256 depositToWithdraw = amount - interestRepaid;
            emit WithdrawProfit(line, tokenId, interestRepaid);
            emit WithdrawDeposit(line, tokenId, depositToWithdraw);
        } else if (amount < interestRepaid) {
            emit WithdrawProfit(line, tokenId, amount);
            emit WithdrawDeposit(line, tokenId, 0);
        }
        ILineOfCredit(line).withdraw(tokenId, amount);
    }

    /**
     * @notice  - Updates CreditStrategy accounting for ERC-721 credit position after successful callback from a SecuredLine.
     * @dev     - Only callable by `line` in `SecuredLine.addCredit()` and `SecuredLine.refinanceCredit()`.
     * @param tokenId          - id of the credit position.
     * @return                 - True if the credit position was successfully minted to the CreditStrategy.
     */
    function addCreditPosition(uint256 tokenId) public returns (bool) {
        // Reverts if caller is not the line or the line is not in the line mapping
        if (isLine[msg.sender] != true) {
            revert CallerNotLine();
        }

        // add to tokenIds if tokenId is not already in tokenIds
        if (creditTokenIdToLine[tokenId] == address(0)) {
            tokenIds.push(tokenId);
            creditTokenIdToLine[tokenId] = msg.sender;
        }

        return true;
    }

    /**
     * @notice  - Swaps a credit position owned by the vault for ERC20 tokens.
     * @dev     - Called via mutual consent by the manager and the buyer.
     * @param line                - Address of the line of credit where the credit position exists.
     * @param creditPositionToken - Address of the credit position token.
     * @param tokenId             - Id of the credit position to swap.
     * @param buyer               - Address of the buyer.
     * @param receiver            - Address of the receiver.
     * @param buyToken            - Address of the token to buy the credit position with. Must be the same as the vault's asset.
     * @param buyTokenAmount      - Amount of buyToken to buy the credit position with.
     */
    function swapCreditPositionForTokens(
        address line,
        address creditPositionToken,
        uint256 tokenId,
        address buyer,
        address receiver,
        address buyToken,
        uint256 buyTokenAmount
    ) external mutualConsent(manager, buyer) returns (bool) {
        _onlyVaultAssetSupported(buyToken);

        // Vault transfers Credit Position Token to receiver
        IERC721(creditPositionToken).transferFrom(address(this), receiver, tokenId);

        // buyer pays CreditStrategy for Credit Position Token
        ERC20(buyToken).safeTransferFrom(buyer, address(this), buyTokenAmount);
        emit SwapCreditPositionForTokens(line, tokenId, buyer, buyToken, buyTokenAmount);

        // Update vault accounting
        _sync();

        return true;
    }

    /**
     * @notice  - Updates Vault accounting when at least one credit position is sold/transferred to a buyer in exchange for ERC20 tokens. Calculates performance and management fees and creates new shares.
     * @dev     - Only callable by the manager or operator.
     * @dev     - Should be called before processing deposit and redeem requests.
     */
    function sync() external {
        _onlyOperator();
        _sync();
    }

    function _sync() internal {

        // Iterate through all credit positions owned by the CreditStrategy to reset totalAssets()
        uint256 index = tokenIds.length;
        while (index > 0) {
            index--;
            uint256 tokenId = tokenIds[index];
            address line = creditTokenIdToLine[tokenId];
            address tokenContract = address(ILineOfCredit(line).tokenContract());
            address tokenOwner = ownerOfTokenId(tokenContract, tokenId);

            // If the token owner is not the CreditStrategy, we remove the tokenId
            if (tokenOwner != address(this)) {
                creditTokenIdToLine[tokenId] = address(0);
                _removeTokenId(index);
            }
        }

        // accrue management and performance fees for vault manager
        _accrueFees();

    }


    /**
     * @notice  - Removes a tokenId from the tokenIds array without preserving the order.
     * @param index - The index of the tokenId to remove.
     */
    function _removeTokenId(uint256 index) internal {
        tokenIds[index] = tokenIds[tokenIds.length - 1]; // Overwrite the tokenId at the index with the last tokenId
        tokenIds.pop(); // Removes the last element, reducing the array's length
    }

    /*///////////////////////////////////////////////////////////////
                        RECOVERY
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Transfers ERC20 tokens from the CreditStrategy contract to arbitrary `to` destination. Can be used to recover tokens not in the underlying asset or in emergency situations.
     * @dev     - Only callable by the manager.
     * @param token     - Address of the ERC20 token to recover.
     * @param to        - Address to send the recovered tokens to.
     * @param amount    - Amount of tokens to recover.
     */
    function recoverTokens(address token, address to, uint256 amount) external {
        _onlyVaultManager();
        ERC20(token).safeTransfer(to, amount);
        emit TokensRecovered(msg.sender, to, token, amount);
    }
}

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

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

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

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity 0.8.25;

import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {Denominations} from "chainlink/Denominations.sol";

/**
 * @title   - LineLib
 * @author  - Credit Cooperative
 * @notice  - Core logic used by LineOfCredit contract for sending and receiving tokens/ETH.
 */
library LineLib {
    using SafeERC20 for IERC20;

    error EthSentWithERC20();
    error TransferFailed();
    error SendingEthFailed();
    error RefundEthFailed();
    error BadToken();

    enum STATUS {
        UNINITIALIZED,
        ACTIVE,
        LIQUIDATABLE,
        REPAID,
        INSOLVENT,
        ABORTED
    }

    /**
     * @notice - Send ETH or ERC20 token from this contract to an external contract
     * @param token - address of token to send out. Denominations.ETH for raw ETH
     * @param receiver - address to send tokens to
     * @param amount - amount of tokens to send
     */
    function sendOutTokenOrETH(address token, address receiver, uint256 amount) internal {
        // both branches revert if call failed
        if (token != Denominations.ETH) {
            // ERC20
            IERC20(token).safeTransfer(receiver, amount);
        } else {
            // ETH
            _safeTransferFunds(receiver, amount);
        }
    }

    /**
     * @notice - Receive ETH or ERC20 token at this contract from an external contract
     * @dev    - If the sender overpays, the difference will be refunded to the sender
     * @param token - address of token to receive. Denominations.ETH for raw ETH
     * @param sender - address that is sendingtokens/ETH
     * @param amount - amount of tokens to send
     */
    function receiveTokenOrETH(address token, address sender, uint256 amount) external {
        if (token != Denominations.ETH) {
            // ERC20
            if (msg.value != 0) {
                revert EthSentWithERC20();
            }
            IERC20(token).safeTransferFrom(sender, address(this), amount);
        } else {
            // ETH
            if (msg.value != amount) {
                revert TransferFailed();
            }
        }
    }

    /**
     * @notice - Helper function to get current balance of this contract for ERC20 or ETH
     * @param token - address of token to check. Denominations.ETH for raw ETH
     */
    function getBalance(address token) internal view returns (uint256) {
        if (token == address(0)) return 0;
        return token != Denominations.ETH ? IERC20(token).balanceOf(address(this)) : address(this).balance;
    }

    /**
     * @notice  - Helper function to safely transfer Eth using native call
     * @dev     - Errors should be handled in the calling function
     * @param recipient - address of the recipient
     * @param value - value to be sent (in wei)
     */
    function _safeTransferFunds(address recipient, uint256 value) internal returns (bool success) {
        (success,) = payable(recipient).call{value: value}("");
        if (!success) revert SendingEthFailed();
    }

    /**
     * @notice  - Helper function to safely approve a contract to spend tokens in the line
     * @dev     - Errors should be handled in the calling function
     * @param token - address of the token being approved
     * @param spender - the address of the address that is being approved to spend
     * @param amount - amount being approved
     */
    function _forceApprove(address token, address spender, uint256 amount) internal {
        IERC20(token).forceApprove(spender, amount);
    }
}

// make a new interface for the lending position token

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

import {IERC721} from "openzeppelin/token/ERC721/IERC721.sol";

interface ICreditPositionToken is IERC721 {
    struct PositionInfo {
        address line;
        uint256 id;
        uint256 deposit;
        uint256 principal;
        uint256 interestAccrued;
        uint256 interestRepaid;
        uint128 dRate;
        uint128 fRate;
        uint256 deadline;
        uint256 mincratio;
    }

    error OpenProposals();
    error CallerIsNotLine();
    error NotSupportedLineFactory();
    error PositionTokenTransferRestricted();


    event SupportedLineFactorySet(address indexed sender, address indexed lineFactory, bool supported);
    event UpdateAdmin(address indexed newAdmin);

    function admin() external view returns (address);

    function mint(address lineFactory, address to, address line, bool iRestricted) external returns (uint256);
    function approveTokenTransfer(uint256 tokenId, address to) external;
    function getPositionInfo(uint256 tokenId) external view returns (PositionInfo memory);
    function getCRatio(uint256 tokenId) external returns (uint256);
    function openProposal(uint256 tokenId) external;
    function closeProposal(uint256 tokenId) external;
    function setLineFactory(address lineFactory, bool supported) external returns (bool);
    function updateAdmin(address newAdmin) external returns (bool);
}

File 20 of 49 : IInterestRateCredit.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

interface IInterestRateCredit {
    struct Rate {
        // The interest rate charged to a Borrower on borrowed / drawn down funds
        // in bps, 4 decimals
        uint128 dRate;
        // The interest rate charged to a Borrower on the remaining funds available, but not yet drawn down (rate charged on the available headroom)
        // in bps, 4 decimals
        uint128 fRate;
        // The time stamp at which accrued interest was last calculated on an ID and then added to the overall interestAccrued (interest due but not yet repaid)
        uint256 lastAccrued;
    }

    function getRates(uint256 id) external view returns (uint128, uint128);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Helper library for emitting standardized panic codes.
 *
 * ```solidity
 * contract Example {
 *      using Panic for uint256;
 *
 *      // Use any of the declared internal constants
 *      function foo() { Panic.GENERIC.panic(); }
 *
 *      // Alternatively
 *      function foo() { Panic.panic(Panic.GENERIC); }
 * }
 * ```
 *
 * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
 *
 * _Available since v5.1._
 */
// slither-disable-next-line unused-state
library Panic {
    /// @dev generic / unspecified error
    uint256 internal constant GENERIC = 0x00;
    /// @dev used by the assert() builtin
    uint256 internal constant ASSERT = 0x01;
    /// @dev arithmetic underflow or overflow
    uint256 internal constant UNDER_OVERFLOW = 0x11;
    /// @dev division or modulo by zero
    uint256 internal constant DIVISION_BY_ZERO = 0x12;
    /// @dev enum conversion error
    uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
    /// @dev invalid encoding in storage
    uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
    /// @dev empty array pop
    uint256 internal constant EMPTY_ARRAY_POP = 0x31;
    /// @dev array out of bounds access
    uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
    /// @dev resource error (too large allocation or too large array)
    uint256 internal constant RESOURCE_ERROR = 0x41;
    /// @dev calling invalid internal function
    uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;

    /// @dev Reverts with a panic code. Recommended to use with
    /// the internal constants with predefined codes.
    function panic(uint256 code) internal pure {
        assembly ("memory-safe") {
            mstore(0x00, 0x4e487b71)
            mstore(0x20, code)
            revert(0x1c, 0x24)
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }

    /**
     * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
     */
    function toUint(bool b) internal pure returns (uint256 u) {
        assembly ("memory-safe") {
            u := iszero(iszero(b))
        }
    }
}

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

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: 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.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.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 ERC-4626 "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.1.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC-165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
     * Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 30 of 49 : IERC721.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC721.sol)

pragma solidity ^0.8.20;

import {IERC721} from "../token/ERC721/IERC721.sol";

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

// forked from https://github.com/IndexCoop/index-coop-smart-contracts/blob/master/contracts/lib/MutualConsent.sol

pragma solidity 0.8.25;

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

/**
 * @title MutualConsent
 * @author Set Protocol
 *
 * The MutualConsent contract contains a modifier for handling mutual consents between two parties
 */
contract MutualConsent is IMutualConsent {
    /* ============ State Variables ============ */

    // equivalent to longest msg.data bytes, ie addCredit
    uint256 constant MAX_DATA_LENGTH_BYTES = 292;

    // equivalent to any fn with no args, ie just a fn selector
    uint256 constant MIN_DATA_LENGTH_BYTES = 4;

    // Mapping of upgradable units and if consent has been initialized by other party
    mapping(bytes32 => address) public mutualConsentProposals;
    uint128 private _nonce;
    uint128 public proposalCount;

    error Unauthorized();
    error InvalidConsent();
    error NotUserConsent();

    // causes revert when the msg.data passed in has more data (ie arguments) than the largest known fn signature
    error UnsupportedMutualConsentFunction();

    /* ============ Events ============ */

    event MutualConsentRegistered(bytes32 indexed proposalId, address indexed taker, uint256 indexed nonce, bytes msgData);
    event MutualConsentRevoked(bytes32 indexed proposalId);
    event MutualConsentAccepted(bytes32 indexed proposalId);
    event MutualConsentRevokedAll(uint256 indexed newNonce);

    /* ============ Modifiers ============ */

    /**
     * @notice - allows a function to be called if only two specific stakeholders signoff on the tx data
     *         - signers can be anyone. only two signers per contract or dynamic signers per tx.
     */
    modifier mutualConsent(address _signerOne, address _signerTwo) {
        if (_mutualConsent(_signerOne, _signerTwo)) {
            // Run whatever code needed 2/2 consent
            _;
        }
    }

    /**
     *  @notice - allows a caller to revoke a previously created consent
     *  @dev    - MAX_DATA_LENGTH_BYTES is set at 164 bytes, which is the length of the msg.data
     *          - for the addCredit function. Anything over that is not valid and might be used in
     *          - an attempt to create a hash collision
     *  @param  _reconstructedMsgData The reconstructed msg.data for the function call for which the
     *          original consent was created - comprised of the fn selector (bytes4) and abi.encoded
     *          function arguments.
     *
     */
    function revokeConsent(uint256, bytes calldata _reconstructedMsgData) public virtual {
        if (
            _reconstructedMsgData.length > MAX_DATA_LENGTH_BYTES || _reconstructedMsgData.length < MIN_DATA_LENGTH_BYTES
        ) {
            revert UnsupportedMutualConsentFunction();
        }

        bytes32 proposalIdToDelete = keccak256(abi.encodePacked(_reconstructedMsgData, msg.sender, _nonce));

        address consentor = mutualConsentProposals[proposalIdToDelete];
        if (consentor == address(0)) {
            revert InvalidConsent();
        }
        if (consentor != msg.sender) {
            revert NotUserConsent();
        } // note: cannot test, as no way to know what data (+msg.sender) would cause hash collision

        --proposalCount;
        delete mutualConsentProposals[proposalIdToDelete];

        emit MutualConsentRevoked(proposalIdToDelete);
    }

    /* ============ Internal Functions ============ */

    function _mutualConsent(address _signerOne, address _signerTwo) internal returns (bool) {
        if (msg.sender != _signerOne && msg.sender != _signerTwo) {
            revert Unauthorized();
        }

        address nonCaller = _getNonCaller(_signerOne, _signerTwo);

        // The consent hash is defined by the hash of the transaction call data and sender of msg,
        // which uniquely identifies the function, arguments, and sender.
        uint128 nonce = _nonce;
        bytes32 expectedProposalId = keccak256(abi.encodePacked(msg.data, nonCaller, nonce));

        if (mutualConsentProposals[expectedProposalId] == address(0)) {
            bytes32 newProposalId = keccak256(abi.encodePacked(msg.data, msg.sender, nonce));
            if (mutualConsentProposals[newProposalId] != address(0)) {
                return false;
            }

            ++proposalCount;
            mutualConsentProposals[newProposalId] = msg.sender; // save caller's consent for nonCaller to accept

            emit MutualConsentRegistered(newProposalId, nonCaller, nonce, msg.data);

            return false;
        }

        --proposalCount;
        delete mutualConsentProposals[expectedProposalId];

        emit MutualConsentAccepted(expectedProposalId);

        return true;
    }

    function _clearProposals() internal {
        proposalCount = 0;
        emit MutualConsentRevokedAll(++_nonce);
    }

    function _getNonCaller(address _signerOne, address _signerTwo) internal view returns (address) {
        return msg.sender == _signerOne ? _signerTwo : _signerOne;
    }
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

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

interface ISpigotedLine {
    struct ClaimAndTradeParams {
        address claimToken;
        address targetToken;
        address payable swapTarget;
        address spigot;
        uint256 unused;
        uint128 swapFee;
        address protocolTreasury;
        bytes zeroExTradeData;
    }

    /**
     * @notice - Log how many spigot tokens are used to repay debt after claimAndRepay
     *         - dont need to track value like other events because _repay already emits that
     *         - Mainly used to log debt that is paid via Spigot directly vs other sources. Without this event it's a lot harder to parse that offchain.
     */
    event SpigotPayment(address indexed token, uint256 indexed amount);

    /**
     * @notice - Log how many spigot tokens were traded for credit tokens.
     *         - Differs from SpigotPayment because we trade tokens at different times from repaying with spigot tokens
     * @dev    - Can you use to figure out price of spigot tokens offchain since we only have an oracle for credit tokens
     * @dev    - Spigot tokens may be from reserves or from Spigot tokens.
     */
    event TradeSpigotTokens(
        address indexed spigotToken,
        uint256 spigotTokenAmount,
        address indexed debtToken,
        uint256 indexed debtTokensBought
    );

    // 0 for spigot token, 1 for credit token
    event ReservesChanged(address indexed token, int256 indexed diff, uint256 tokenType);

    error ReservesOverdrawn(address token, uint256 amountAvailable);
    error LineHasActiveCreditPositions(uint256 numActiveCreditPositions);

    // Borrower functions
    function useAndRepay(uint256 amount) external;
    function claimAndRepay(address claimToken, bytes calldata zeroExTradeData) external returns (uint256);
    function claimAndTrade(address claimToken, bytes calldata zeroExTradeData)
        external
        returns (uint256 tokensBought);

    // Spigot management functions
    function sweep(address to, address token, uint256 amount) external;

    // getters
    function unused(address token) external view returns (uint256);
    function tradeable(address token) external view returns (uint256);
    function spigot() external returns (ISpigot);
    function swapTarget() external view returns (address payable);
}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

import {IEscrowedLine} from "./IEscrowedLine.sol";
import {ISpigotedLine} from "./ISpigotedLine.sol";

interface ISecuredLine is IEscrowedLine, ISpigotedLine {
    // Errors
    error DebtOwed();
    error CannotAmendLine();

    // Events
    event RecoveredEscrow(address indexed to, uint256 amount, address token);

    // Recovery Functions
    function abort() external;

    // SecuredLine management functions
    function clearProposals() external;
    function activateLine() external;
    function incrementNonce() external;
    function revokeConsent(uint256 tokenId, bytes calldata _reconstructedMsgData) external;
    function updateAllowedOTCPriceImpact(uint128 allowedPriceImpact) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import {ILendingVault} from "contracts/interfaces/ILendingVault.sol";
import {LendingVault} from "contracts/LendingVault.sol";
import {IFeeManager} from "contracts/interfaces/IFeeManager.sol";
import {VaultAdmin} from "contracts/strategies/credit/VaultAdmin.sol";

import {ReentrancyGuard} from "openzeppelin/utils/ReentrancyGuard.sol";
import {Math} from "openzeppelin/utils/math/Math.sol";
import {ERC20} from "openzeppelin/token/ERC20/ERC20.sol";
import {Pausable} from "openzeppelin/utils/Pausable.sol";

/**
 * @title  - FeeManager
 * @author - Credit Cooperative
 * @notice - Provides functions to set and calculate fees for the Vault.
 * @dev    - All external and public state changing functions are only callable by the vault manager, vault operator, or associated LendingVault.
 */
abstract contract FeeManager is VaultAdmin, ReentrancyGuard, IFeeManager {
    using Math for uint256;

    Fees public fees;
    uint256 public lastManagementFeeTimestamp;
    uint256 public highWaterMarkSharePrice;

    uint8 public protocolSplit;
    uint8 public constant MAX_SPLIT = 100;

    address public immutable lendingVault;

    /*///////////////////////////////////////////////////////////////
                        ACCESS CONTROL LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice - Checks `msg.sender` is `lendingVault`.
     */
    function _onlyLendingVault() internal view {
        if (msg.sender != lendingVault) {
            revert OnlyLendingVault();
        }
    }

    /**
     * @notice - Checks `msg.sender` is `lendingVault` or `isOperator`.
     */
    function _onlyLendingVaultOrOperator() internal view {
        if (msg.sender != lendingVault && !isOperator[msg.sender]) {
            revert OnlyLendingVaultOrOperator();
        }
    }


    /**
     * @notice  - Asserts that the protocol split is less than the maximum allowed.
     * @param split - The split to assert.
     */
    function _assertMaxProtocolSplit(uint8 split) internal pure {
        if (split > MAX_SPLIT) {
            revert ProtocolSplitExceedsMaximumAllowed();
        }
    }

    /**
     * @notice  - Asserts that the fee is less than the maximum allowed.
     * @param fee   - The fee to assert.
     */
    function _assertMaxFee(uint16 fee, FeeTypes fee_type) internal {
        if (uint256(fee) >= FEE_COEFFICIENT) {
            revert FeeExceedsMaximumAllowed();
        }
        emit SetFee(fee, uint256(fee_type));
    }


    /*///////////////////////////////////////////////////////////////
                        FEE SETTINGS & MANAGEMENT
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice  - Sets the performance fee of the vault.
     * @dev     - Only callable by `manager`.
     * @dev     - The fee cannot be greater than the maximum allowed.
     * @param fee   - The new performance fee.
     */
    function setPerformanceFee(uint16 fee) external nonReentrant {
        _onlyVaultManager();
        _assertMaxFee(fee, FeeTypes.PERFORMANCE);
        _accrueFees(); // accrue fees before changing performance fee
        fees.performance = fee;
    }

    /**
     * @notice  - Gets the performance fee of the vault.
     * @return  - The performance fee of the vault.
     */
    function performanceFee() external view returns (uint16) {
        return fees.performance;
    }

    /**
     * @notice  - Sets the management fee of the vault.
     * @dev     - Only callable by `manager`.
     * @dev     - The fee cannot be greater than the maximum allowed.
     * @param fee   - The new management fee.
     */
    function setManagementFee(uint16 fee) external nonReentrant {
        _onlyVaultManager();
        _assertMaxFee(fee, FeeTypes.MANAGEMENT);
        _accrueFees(); // accrue fees before changing management fee
        fees.management = fee;
    }

    /**
     * @notice  - Gets the management fee of the vault.
     * @return  - The management fee of the vault.
     */
    function managementFee() external view returns (uint16) {
        return fees.management;
    }

    /**
     * @notice  - Updates the protocol split of the vault.
     * @dev     - Updating the protocol split must be agreed with mutual consent between the protocol's treasury and the vault manager. The new protocol split cannot be greater than the maximum allowed.
     * @param newProtocolSplit - The new percentage of fees that accrue to the protocol treasury.
     */
    function updateProtocolSplit(uint8 newProtocolSplit) external mutualConsent(protocolTreasury, manager) {
        _assertMaxProtocolSplit(newProtocolSplit);
        _accrueFees(); // accrue fees before changing protocol split
        emit UpdateProtocolSplit(newProtocolSplit);
        protocolSplit = newProtocolSplit;
    }

    /**
        * @notice  - set the last management fee timestamp to the current block timestamp
        * @dev     - Only callable by `lendingVault`.
     */
    function setLastManagementFeeTimestamp() external nonReentrant {
        _onlyLendingVault();
        lastManagementFeeTimestamp = block.timestamp;
    }

    /**
     * Calculates pro rata management fee by multiplying the Vault's management fee percentage by the elapsed amount of time since the last management fee was accrued divided by one year in seconds, since the fee is a yearly fee.
     *
     * @return proratedManagementFee - management fee denominated in percentage of ERC20(lendingVault).totalSupply()
     */
    function _calculateProRataManagementFee() internal view returns (uint256 proratedManagementFee) {
        if (Pausable(lendingVault).paused()) {
            return 0;
        }
        uint256 timeSinceLastFee = block.timestamp - lastManagementFeeTimestamp;
        if (timeSinceLastFee > ONE_YEAR_IN_SECONDS) {
            timeSinceLastFee = ONE_YEAR_IN_SECONDS;
        }
        uint256 managementFeeInBaseUnit = baseUnit.mulDiv(fees.management, 10000);
        proratedManagementFee = timeSinceLastFee.mulDiv(managementFeeInBaseUnit, ONE_YEAR_IN_SECONDS);
    }


    /**
     * @notice  - Accrues management fees.
     * @return newVaultShares - Amount of new shares minted.
     */
    function _accrueManagementFee() internal returns (uint256 newVaultShares) {
        uint256 totalSupplyBefore = ERC20(lendingVault).totalSupply();

        uint256 inflationFeePercentage = _calculateProRataManagementFee();
        newVaultShares = _calculateManagementFeeInflation(inflationFeePercentage);

        // Mint new Vault shares to manager and protocol treasury
        uint256 newManagerShares = newVaultShares.mulDiv(100 - protocolSplit, 100);
        uint256 newProtocolShares = newVaultShares - newManagerShares;
        ILendingVault(lendingVault).mintFeeShares(manager, newManagerShares);
        ILendingVault(lendingVault).mintFeeShares(protocolTreasury, newProtocolShares);
        emit MintManagementShares(manager, newManagerShares, protocolTreasury, newProtocolShares);

        // update lastManagementFeeTimestamp
        lastManagementFeeTimestamp = block.timestamp;

        // reset highWaterMarkSharePrice to adjust for new shares minted
        if (totalSupplyBefore > 0) {
            uint256 totalSupplyAfter = ERC20(lendingVault).totalSupply();
            highWaterMarkSharePrice = highWaterMarkSharePrice.mulDiv(totalSupplyBefore, totalSupplyAfter);
            emit SetHighWaterMarkSharePrice(highWaterMarkSharePrice);
        }
    }

    /**
     * @notice  - Returns new management fees denominated in terms of the number of shares to mint for the vault manager. The calculation determines the quantity of new shares to mint such that the Vault manager owns the fee percentage of the entire supply of the Vault.
     *
     * The simplified (floating point) version of the formula utilized by the function is:
     * newManagerShares = _feePercentage * ERC20(lendingVault).totalSupply() / (1 - _feePercentage)
     *
     * @param   _feePercentage      - Pro-rated management fee levied by manager
     * @return  newManagerShares    - New Vault shares issue quantity
     */
    function _calculateManagementFeeInflation(uint256 _feePercentage)
        internal
        view
        returns (uint256 newManagerShares)
    {
        newManagerShares = _feePercentage.mulDiv(ERC20(lendingVault).totalSupply(), baseUnit - _feePercentage);
    }


    /**
     * @notice  - Calculates the performance and management fees and mints the corresponding vault shares to the manager and protocol treasury.
     * @dev     - Only callable by `manager`, `operator`, or `lendingVault`.
     */
    function accrueFees() external {
        _onlyLendingVaultOrOperator();
        _accrueFees();
    }


    /**
     * @notice  - Calculates the performance and management fees and mints the corresponding vault shares to the manager and protocol treasury.
     */
    function _accrueFees() internal {
        _accruePerformanceFee(); // NOTE: always accrue performance fees so highWaterMarkSharePrice is updated
        if (fees.management > 0) {
            _accrueManagementFee();
        }
    }


    /**
     * @notice  - Calculates the performance fee and mints the corresponding vault shares to the manager.
     */
    function _accruePerformanceFee() internal {
        uint256 newSharePrice = LendingVault(lendingVault).sharePrice();
        uint256 newTotalAssets = LendingVault(lendingVault).totalAssets();

        if (newSharePrice > highWaterMarkSharePrice) {
            uint256 highWaterMarkSharesValue = highWaterMarkSharePrice.mulDiv(ERC20(lendingVault).totalSupply(), baseUnit);
            uint256 newFeeAssets = (newTotalAssets - highWaterMarkSharesValue).mulDiv(fees.performance, FEE_COEFFICIENT);

            // Calculate new shares to mint for manager and protocol treasury
            uint256 newFeeShares = newFeeAssets.mulDiv(ERC20(lendingVault).totalSupply(), newTotalAssets - newFeeAssets);
            uint256 newManagerShares = newFeeShares.mulDiv((100 - protocolSplit), 100);
            uint256 newProtocolShares = newFeeShares - newManagerShares;

            // Mint new Vault shares to manager and protocol treasury
            ILendingVault(lendingVault).mintFeeShares(manager, newManagerShares);
            ILendingVault(lendingVault).mintFeeShares(protocolTreasury, newProtocolShares);

            emit MintPerformanceShares(manager, newManagerShares, protocolTreasury, newProtocolShares);

            // update highWaterMarkSharePrice
            highWaterMarkSharePrice = ILendingVault(lendingVault).sharePrice();
            emit SetHighWaterMarkSharePrice(highWaterMarkSharePrice);
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import {VaultAdmin} from "contracts/strategies/credit/VaultAdmin.sol";
import {IERC721} from "openzeppelin/interfaces/IERC721.sol";

/**
 * @title  - ERC721Manager
 * @author - Credit Cooperative
 * @notice - Provides functions to interact with ERC-721 credit positions from Line-Of-Credit-v2.
 * @dev    - All external and public state changing functions are only callable by the vault manager or operator roles.
 */
abstract contract ERC721Manager is VaultAdmin {
    // See IERC721.balanceOf
    function balanceOfTokenId(address creditPositionToken, address owner) external view returns (uint256 balance) {
        return IERC721(creditPositionToken).balanceOf(owner);
    }

    // See IERC721.ownerOf
    function ownerOfTokenId(address creditPositionToken, uint256 tokenId) public view returns (address owner) {
        return IERC721(creditPositionToken).ownerOf(tokenId);
    }

    // See IERC721.getApproved
    function getApprovedTokenId(address creditPositionToken, uint256 tokenId)
        external
        view
        returns (address operator)
    {
        return IERC721(creditPositionToken).getApproved(tokenId);
    }

    // See IERC721.isApprovedForAll
    function isApprovedForAllTokenId(address creditPositionToken, address owner, address operator)
        external
        view
        returns (bool)
    {
        return IERC721(creditPositionToken).isApprovedForAll(owner, operator);
    }

    /*///////////////////////////////////////////////////////////////
                    State Changing Functions
    //////////////////////////////////////////////////////////////*/

    // See IERC721.safeTransferFrom
    function safeTransferFromTokenId(
        address creditPositionToken,
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external {
        _onlyVaultManager();
        IERC721(creditPositionToken).safeTransferFrom(from, to, tokenId, data);
    }

    // See IERC721.safeTransferFrom
    function safeTransferFromTokenId(address creditPositionToken, address from, address to, uint256 tokenId) external {
        _onlyVaultManager();
        IERC721(creditPositionToken).safeTransferFrom(from, to, tokenId);
    }

    // See IERC721.transferFrom
    function transferFromTokenId(address creditPositionToken, address from, address to, uint256 tokenId) external {
        _onlyVaultManager();
        IERC721(creditPositionToken).transferFrom(from, to, tokenId);
    }

    // See IERC721.approve
    function approveTokenId(address creditPositionToken, address to, uint256 tokenId) external {
        _onlyVaultManager();
        IERC721(creditPositionToken).approve(to, tokenId);
    }

    // See IERC721.setApproval
    function setApprovalForAllTokenId(address creditPositionToken, address operator, bool approved) external {
        _onlyVaultManager();
        IERC721(creditPositionToken).setApprovalForAll(operator, approved);
    }
}

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

interface ICreditStrategy {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event WithdrawDeposit(address indexed line, uint256 indexed tokenId, uint256 amount);
    event WithdrawProfit(address indexed line, uint256 indexed tokenId, uint256 amount);

    event ManagerDeposit(address indexed sender, uint256 indexed amount);
    event ManagerWithdraw(address indexed sender, uint256 indexed amount);

    event SwapCreditPositionForTokens(
        address indexed line, uint256 indexed tokenId, address indexed buyer, address buyToken, uint256 amount
    );

    event TokensRecovered(address indexed sender, address indexed receiver, address indexed token, uint256 amount);

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

    error CannotLendToSelf();
    error AssetNotSupported();
    error CallerNotLine();
    error NoWithdrawableAssets();
    error OnlyLendingVaultOrManager();


    /*///////////////////////////////////////////////////////////////
                        FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function asset() external returns (address);

    function totalActiveCredit() external view returns (uint256 totalActiveCredit);

    function creditTokenIdToLine(uint256 tokenId) external returns (address);

    function getPositionActiveCredit(address line, uint256 tokenId) external view returns (uint256 deposit, uint256 interest);

    function numCreditPositions() external view returns (uint256);

    function deposit(uint256 amount) external;

    function withdraw(uint256 amount) external;

    function addCredit(
        address line,
        uint256 desiredNonce,
        uint128 drate,
        uint128 frate,
        uint256 amount,
        bool isRestricted,
        uint16 earlyWithdrawalFee,
        uint256 deadline
    ) external returns (bool, uint256 tokenId);

    function revokeProposal(address line, uint256 tokenId, bytes calldata reconstructedMsgData) external;

    function refinanceCredit(
        address line,
        uint256 desiredNonce,
        uint256 tokenId,
        uint256 amount,
        uint128 dRate,
        uint128 fRate,
        uint256 deadline
    ) external returns (bool);

    function useAndRepay(address line, uint256 amount) external;

    function withdrawCredit(address line, uint256 tokenId, uint256 amount) external;

    function addCreditPosition(uint256 tokenId) external returns (bool);

    function swapCreditPositionForTokens(
        address line,
        address creditPositionToken,
        uint256 tokenId,
        address buyer,
        address receiver,
        address buyToken,
        uint256 buyTokenAmount
    ) external returns (bool);

    function sync() external;
}

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 38 of 49 : Denominations.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

library Denominations {
  address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
  address public constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;

  // Fiat currencies follow https://en.wikipedia.org/wiki/ISO_4217
  address public constant USD = address(840);
  address public constant GBP = address(826);
  address public constant EUR = address(978);
  address public constant JPY = address(392);
  address public constant KRW = address(410);
  address public constant CNY = address(156);
  address public constant AUD = address(36);
  address public constant CAD = address(124);
  address public constant CHF = address(756);
  address public constant ARS = address(32);
  address public constant PHP = address(608);
  address public constant NZD = address(554);
  address public constant SGD = address(702);
  address public constant NGN = address(566);
  address public constant ZAR = address(710);
  address public constant RUB = address(643);
  address public constant INR = address(356);
  address public constant BRL = address(986);
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Required interface of an ERC-721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC-721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
     *   {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
     *   a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
     * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
     * understand this adds an external call which potentially creates a reentrancy vulnerability.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 tokenId) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the address zero.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

pragma solidity ^0.8.25;

interface IMutualConsent {
    function mutualConsentProposals(bytes32) external view returns (address);

    function proposalCount() external view returns (uint128);
}

File 41 of 49 : ISpigot.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

import {ISpigotedLine} from "./ISpigotedLine.sol";
import {BeneficiaryData} from "../utils/DiscreteDistribution.sol";
import {IOTC} from "../interfaces/IOTC.sol";

interface ISpigot {
    struct Setting {
        uint8 ownerSplit; // x/100 % to Owner, rest to Operator
        bytes4 claimFunction; // function signature on contract to call and claim tokens
        bytes4 transferOwnerFunction; // function signature on contract to call and transfer ownership
    }

    // in BPS 4 decimals  fee = 50 loan amount = 10000 * (50/100)
    struct Fees {
        uint128 swapFee;
        uint128 servicingFee;
    }

    enum FeeTypes {
        SWAP,
        SERVICING
    }

    // Spigot Events
    event AddSpigot(address indexed borrowerContract, uint256 ownerSplit, bytes4 claimFnSig, bytes4 trsfrFnSig);
    event RemoveSpigot(address indexed borrowerContract, address token);
    event UpdateWhitelistFunction(bytes4 indexed func, bool indexed allowed);
    event ClaimTokens(address indexed token, uint256 indexed amount, uint256 operatorTokens, address borrowerContract);
    event ClaimOwnerTokens(address indexed token, uint256 indexed amount, address owner);
    event ClaimOperatorTokens(address indexed token, uint256 indexed amount, address operator);

    // Stakeholder Events
    event UpdateOwner(address indexed newOwner);
    event UpdateOperator(address indexed newOperator);
    event UpdateBorrowerContractSplit(address indexed borrowerContract, uint8 indexed split);
    event RemoveSpigot(address indexed borrowerContract);

    // Errors
    error BadFunction();
    error OperatorFnNotWhitelisted();
    error OperatorFnNotValid();
    error OperatorFnCallFailed();
    error ClaimFailed();
    error NoClaimableTokens();
    error CallerAccessDenied();
    error BadSetting();
    error InvalidBorrowerContract();
    error InsufficientFunds();
    error InvalidToken();
    error LineMustBeDefaultBeneficiary(address beneficiary);
    error BadAddress();
    error BadStatus();

    error AlreadyInitialized();
    error BeneficiaryDebtOutstanding();
    error LineHasActiveCreditPositions(uint256);
    error InvalidSplit();
    error SpigotSettingsExist();
    error TradeFailed();
    error CannotBeZeroAddress();
    error NoTradedTokens();
    error CannotOverpayBeneficiaryDebt();
    error MustRepayWithTradedTokens();
    error CannotClaimBeneficiaryTradedTokens();
    error BadBeneficiary();

    // initialization funcs

    function initializeFromFactory(address _operator, BeneficiaryData memory defaultBeneficiary, address _weth)
        external;

    // ops funcs

    function pullTokens(address borrowerContract, address token, bytes calldata data)
        external
        returns (uint256 claimed);

    function repayBeneficiary(bytes calldata functionData, address beneficiary, uint256 amount) external;
    function repayBeneficiaryWithTradedTokens(bytes calldata functionData, address beneficiary) external;

    function tradeAndDistribute(
        address beneficiary,
        address sellToken,
        uint256 sellAmount,
        uint256 minBuyAmount,
        bytes calldata zeroExTradeData
    ) external;

    function otcSwap(address beneficiary, address oracle, IOTC.OTC memory otc) external;

    // owner funcs
    function claimOwnerTokens(address token) external returns (uint256 claimed);
    function claimOperatorTokens(address token) external returns (uint256 claimed);
    function sweepOwnerOperatorTokens(address token, address to)
        external
        returns (uint256 operatorTokens, uint256 ownerTokens);

    // admin funcs
    function resetBeneficiaries(
        address operator,
        BeneficiaryData memory defaultBeneficiary,
        BeneficiaryData[] calldata beneficiaries_,
        address[] memory tokens
    ) external;

    function reassignTokens(address beneficiary, address[] calldata tokens) external;

    function impairBeneficiaryDebtOwed(address beneficiary) external;

    // Recovery functionality
    function recoverBeneficiaryTokens(address beneficiary, address token, address to) external;
    function recoverSpigotTokens(address token, address to) external;

    // Update beneficiary functions
    function updateBeneficiaryRepaymentFunc(address beneficiary, bytes4 repaymentFunc) external;
    function updateBeneficiaryPoolAddress(address beneficiary, address poolAddress) external;

    // maintainer funcs
    function addSpigot(address borrowerContract, Setting memory setting) external;
    function removeSpigot(address borrowerContract) external;

    // operator funcs
    function operate(address borrowerContract, bytes calldata data) external returns (bool);

    // stakeholder funcs
    function updateBorrowerContractSplit(address borrowerContract, uint8 ownerSplit) external;
    function updateOwner(address newOwner) external returns (bool);
    function updateOperator(address newOperator) external;
    function updateWhitelistedFunction(bytes4 func, bool allowed) external;
    function updateStableCoinWhitelist(address token, bool includeToken) external;
    function updateAllowedOTCPriceImpact(uint128 allowedPriceImpact) external;

    // Getters

    function allocationOf(address token, address beneficiary) external view returns (uint256);

    function getDefaultBeneficiaryBasicData()
        external
        view
        returns (uint256 allocation, address creditToken, uint256 debtOwed);

    function getBeneficiaryBasicData(address beneficiary)
        external
        view
        returns (uint256 allocation, address creditToken, uint256 debtOwed);

    function getBeneficiaryTokens(address beneficiary, address token) external view returns (uint256);

    function beneficiaries() external view returns (address[] memory);
    function owner() external view returns (address);
    function operator() external view returns (address);
    function isWhitelisted(bytes4 func) external view returns (bool);
    function getOwnerTokens(address token) external view returns (uint256);
    function getOperatorTokens(address token) external view returns (uint256);

    function getSetting(address borrowerContract)
        external
        view
        returns (uint8 split, bytes4 claimFunc, bytes4 transferFunc);

    function hasBeneficiaryDebtOutstanding() external returns (bool);
    function getBeneficiaryData(address beneficiary) external view returns (BeneficiaryData memory);
    function totalBennyAmountTradedFor(address) external view returns (uint256);
    function bennyAmountTradedFor(address) external view returns (uint256); 
}

File 42 of 49 : IEscrowedLine.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

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

interface IEscrowedLine {
    /// @notice the escrow contract backing this Line
    function escrow() external view returns (IEscrow);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

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

interface IFeeManager {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event MintManagementShares(
        address indexed manager, uint256 newManagerShares, address indexed protocolTreasury, uint256 newProtocolShares
    );
    event MintPerformanceShares(
        address indexed manager, uint256 newManagerShares, address indexed protocolTreasury, uint256 newProtocolShares
    );

    event SetHighWaterMarkSharePrice(uint256 indexed sharePrice);

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

    error OnlyLendingVault();
    error OnlyLendingVaultOrOperator();
    error FeeExceedsMaximumAllowed();

    /*//////////////////////////////////////////////////////////////
                                FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    struct Fees {
        uint16 management;
        uint16 performance;
    }

    enum FeeTypes {
        MANAGEMENT,
        PERFORMANCE
    }

    function fees() external returns (uint16, uint16);

    function lastManagementFeeTimestamp() external returns (uint256);

    function highWaterMarkSharePrice() external returns (uint256);

    function lendingVault() external returns (address);

    function setPerformanceFee(uint16 fee) external;

    function performanceFee() external view returns (uint16);

    function setManagementFee(uint16 fee) external;

    function updateProtocolSplit(uint8 newProtocolSplit) external;

    function managementFee() external returns (uint16);

    function accrueFees() external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

import {IVaultAdmin} from "contracts/interfaces/IVaultAdmin.sol";
import {MutualConsent} from "Line-Of-Credit-v2/utils/MutualConsent.sol";

/**
 * @title  - VaultAdmin
 * @author - Credit Cooperative
 * @notice - Provides functions update vault adminstrative roles including the manager, operator, protocol treasury, and whitelisted addresses.
 * @dev    - State changing functions are only callable by allowed roles.
 */
abstract contract VaultAdmin is MutualConsent, IVaultAdmin {
    // protocol address that accrues fees
    address public protocolTreasury;

    // vault manager who directs funds into lines of credit
    address public manager;

    // addresses by the vault manager to conduct vault operations
    mapping(address => bool) public isOperator;

    mapping(address => bool) public whitelistedAddresses;
    bool public whitelistEnabled = true;

    /* ============ Constants ============ */
    uint256 public constant ONE_YEAR_IN_SECONDS = 365.25 days;
    uint256 public constant FEE_COEFFICIENT = 10000; // 100 bps = 1%; 100% = 10000

    /// @notice The base unit of the underlying token and hence vault.
    /// @dev Equal to 10 ** decimals. Used withdrawfor fixed point arithmetic.
    uint256 public immutable baseUnit;

    /*///////////////////////////////////////////////////////////////
                        ACCESS CONTROL LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice - Checks `msg.sender` is `manager`.
     */
    function _onlyVaultManager() internal view {
        if (msg.sender != manager) {
            revert OnlyVaultManager();
        }
    }

    /**
     * @notice - Checks `msg.sender` is `manager` or `operator`.
     */
    function _onlyOperator() internal view {
        if (!isOperator[msg.sender]) {
            revert OnlyOperator();
        }
    }

    /**
     * @notice - Checks `msg.sender` is `protocolTreasury`.
     */
    function _onlyProtocolTreasury() internal view {
        if (msg.sender != protocolTreasury) {
            revert OnlyProtocolTreasury();
        }
    }

    /**
     * @notice - Checks target address is not the zero address.
     */
    function _cannotBeZeroAddress(address target) internal pure {
        if (target == address(0)) {
            revert CannotBeZeroAddress();
        }
    }


    /**
     * @notice - Modifies the `whitelistEnabled` state.
     * @dev    - Only callable by `manager`.
     * @param status - If True then whitelist is enabled, if False then whitelist is disabled.
     */
    function updateWhitelistStatus(bool status) external {
        _onlyVaultManager();
        if (status) {
            whitelistEnabled = true;
        } else {
            whitelistEnabled = false;
        }
        emit SetWhitelistStatus(status);
    }



    /**
     * @notice  - Adds an address to the whitelist.
     * @dev     - Only callable by `manager`.
     * @dev     - Cannot whitelist the zero address.
     * @param depositor - Address to update whitelist status.
     * @param approved  - If True then address is whitelisted, if False then address is not whitelisted.
     */
    function updateWhitelist(address depositor, bool approved) external {
        _onlyVaultManager();
        _cannotBeZeroAddress(depositor);

        emit UpdateWhitelist(depositor, approved);
        whitelistedAddresses[depositor] = approved;
    }

    /**
     * @notice  - Updates the manager of the vault and makes the manager an `operator`.
     * @dev     - Only callable by `manager`.
     * @dev     - The new manager cannot be set to the zero address.
     * @param newManager - Address of the new manager.
     */
    function setManager(address newManager) external {
        _onlyVaultManager();
        _cannotBeZeroAddress(newManager);

        emit SetManager(newManager);
        setOperator(newManager, true);
        manager = newManager;
    }

    /**
     * @notice  - Updates the operator of the vault.
     * @dev     - Only callable by `manager`.
     * @dev     - The new operator cannot be set to the zero address.
     * @param operator - Address of the new operator.
     * @param approved - whether the operator is approved or not
     */
    function setOperator(address operator, bool approved) public {
        _onlyVaultManager();
        _cannotBeZeroAddress(operator);

        emit SetOperator(operator, approved);
        isOperator[operator] = approved;
    }

    /**
     * @notice  - Updates the treasury address of the vault.
     * @dev     - Only callable by `protocolTreasury`.
     * @dev     - The new treasury cannot be set to the zero address.
     * @param newProtocolTreasury - Address of the new treasury.
     */
    function setProtocolTreasury(address newProtocolTreasury) external {
        _onlyProtocolTreasury();
        _cannotBeZeroAddress(newProtocolTreasury);

        emit SetProtocolTreasury(newProtocolTreasury);
        protocolTreasury = newProtocolTreasury;
    }
}

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.25;

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

struct BeneficiaryData {
    address beneficiary;
    uint96 allocation;
    address creditToken;
    bytes4 repaymentFunc;
    address poolAddress;
    bytes4 getDebtFunc;
    bool debtVoided;
}

struct TokenData {
    uint256 index;
    uint256 balanceTracker;
    uint96 reassignedAllocation;
    uint256 initialTokens;
}

struct DistributionData {
    address defaultBeneficiary;
    uint96 totalAllocation;
    mapping(address beneficiary => BeneficiaryData) data;
    mapping(address token => TokenData) tokens;
    mapping(address token => mapping(address beneficiary => uint256)) repaidAmounts;
    address[] beneficiaries;
    address operator;
    mapping(address token => uint256 amount) operatorTokens;
}

/**
 * @title   - DiscreteDistribution
 * @author  - Credit Cooperative
 * @notice  - Core logic used by Spigot contract to distribute tokens to beneficiaries (e.g. LineOfCredit and other pool contracts external to Credit Coop contracts).
 */
abstract contract DiscreteDistribution {
    /// @dev allows to distribute small amounts of distributions correctly
    uint256 private constant MAGNITUDE = 1e30;
    uint256 internal constant MAX_INT = type(uint256).max;

    mapping(uint256 nonce => DistributionData) private _data;
    uint256 private _nonce;

    event BeneficiaryAdded(address indexed beneficiary, uint96 amount);
    event DefaultBeneficiaryUpdated(address indexed oldDefaultBeneficiary, address indexed newDefaultBeneficiary);
    event Distributed(address indexed token, uint256 indexed amount);
    event Repaid(address indexed token, address indexed beneficiary, uint256 indexed amount);
    event Reassigned(address indexed token, address indexed beneficiary);

    error DuplicateDefaultBenenficiary();
    error AmountZero();
    error RepaymentExceedsDistributedAmount();
    error CannotReassignDefaultBeneficiary();
    error AlreadyReassigned(address beneficiary, address token);
    error NotBeneficiary(address beneficiary);

    /**
     * @notice sets up the beneficiaries and their allocations
     * @param defaultBeneficiary the beneficiary that receives unassigned funds once other beneficiaries are repaid
     * @param beneficiaries the other beneficiaries and their allocations
     * @param tokens the tokens to be reassigned to the default beneficiary upon reset
     * @dev any funds present in the contract will be distributed among the beneficiaries according to their allocation
     */
    function _resetBeneficiaries(
        address operator,
        BeneficiaryData memory defaultBeneficiary,
        BeneficiaryData[] memory beneficiaries,
        address[] memory tokens
    ) internal {
        ++_nonce;
        _distributionData().operator = operator;
        _distributionData().defaultBeneficiary = defaultBeneficiary.beneficiary;
        _distributionData().data[defaultBeneficiary.beneficiary] = defaultBeneficiary;
        _distributionData().totalAllocation += defaultBeneficiary.allocation;
        emit BeneficiaryAdded(defaultBeneficiary.beneficiary, defaultBeneficiary.allocation);
        uint256 len = beneficiaries.length;
        for (uint256 i = 0; i < len; ++i) {
            BeneficiaryData memory beneficiary = beneficiaries[i];
            if (beneficiary.allocation == 0) revert AmountZero();
            if (beneficiary.beneficiary == _distributionData().defaultBeneficiary) {
                revert DuplicateDefaultBenenficiary();
            }
            _distributionData().data[beneficiary.beneficiary] = beneficiary;
            _distributionData().totalAllocation += beneficiary.allocation;
            _distributionData().beneficiaries.push(beneficiary.beneficiary);
            emit BeneficiaryAdded(beneficiary.beneficiary, beneficiary.allocation);
        }
        if (_distributionData().totalAllocation == 0) revert AmountZero();
        len = tokens.length;
        for (uint256 i = 0; i < len; ++i) {
            address token = tokens[i];
            uint256 currentBlance = IERC20(token).balanceOf(address(this));
            _distributionData().tokens[token].balanceTracker = currentBlance;
            _distributionData().tokens[token].initialTokens = currentBlance;
        }
    }

    /**
     * @notice registers a repayment of a beneficiary for a certain token
     * @dev this function MUST be called BEFORE transfering out tokens
     * @dev `amount` MUST be transferred out of the contract, otherwise accounting will be incorrect
     */
    function _registerRepayment(address token, address beneficiary, uint256 amount) internal {
        if (amount == 0) revert AmountZero();
        if (_distributionData().data[beneficiary].allocation == 0) revert NotBeneficiary(beneficiary);
        _updateDistributionIndex(token);
        uint256 distributedAmount = _repayableAmountOf(token, beneficiary);
        if (amount > distributedAmount) revert RepaymentExceedsDistributedAmount();
        _distributionData().tokens[token].balanceTracker -= amount;
        _distributionData().repaidAmounts[token][beneficiary] += amount;
        emit Repaid(token, beneficiary, amount);
    }

    /**
     * @notice reassigns tokens to the default beneficiary
     * @dev reassigns all remaining tokens to the beneficiary by updating the `reassignedTokens` variable
     * @dev adjusts the total allocation modifier so distributions do not take the beneficiary into account in the future
     * @dev flags the `repaidAmounts` variable for the beneficiary to mark that the beneficiary has an allocation of 0 for the reassigned token
     * @dev the allocation of the beneficiary is implicitly redistributed amongst all other beneficiaries
     */
    function _reassignTokens(address token, address beneficiary) internal {
        address defaultBeneficiary = _distributionData().defaultBeneficiary;
        if (beneficiary == defaultBeneficiary) revert CannotReassignDefaultBeneficiary();
        if (_distributionData().data[beneficiary].allocation == 0) revert NotBeneficiary(beneficiary);
        uint256 repaidAmount = _distributionData().repaidAmounts[token][beneficiary];
        if (repaidAmount == MAX_INT) revert AlreadyReassigned(beneficiary, token);
        _updateDistributionIndex(token);
        _distributionData().tokens[token].reassignedAllocation += _distributionData().data[beneficiary].allocation;
        _distributionData().repaidAmounts[token][defaultBeneficiary] += repaidAmount;
        _distributionData().repaidAmounts[token][beneficiary] = MAX_INT;
        emit Reassigned(token, beneficiary);
    }

    /// @notice reserves tokens in the contract to be used outside of the distrubution library
    function _reserveTokens(address token, uint256 amount) internal {
        _distributionData().operatorTokens[token] += amount;
    }

    /// @notice should be called after transferring out reserved tokens
    function _releaseTokens(address token, uint256 amount) internal {
        _distributionData().operatorTokens[token] -= amount;
    }

    function _voidDebt(address beneficiary) internal {
        _distributionData().data[beneficiary].debtVoided = true;
    }

    /**
     * @dev returns 0 if the beneficiary was reassigned to the default beneficiary
     * @return the allocation of a beneficiary for a certain token
     */
    function _allocationOf(address token, address beneficiary) internal view returns (uint96) {
        if (beneficiary == _distributionData().defaultBeneficiary) {
            return _distributionData().data[beneficiary].allocation
                + _distributionData().tokens[token].reassignedAllocation;
        }
        if (_distributionData().repaidAmounts[token][beneficiary] == MAX_INT) return 0;
        return _distributionData().data[beneficiary].allocation;
    }

    /**
     * @dev takes into account beneficiaries that were reassigned to the default beneficiary
     * @return the total allocation of a token
     */
    function _totalAllocation() internal view returns (uint96) {
        return _distributionData().totalAllocation;
    }

    /**
     * @dev returns 0 if the beneficiary was reassigned to the default beneficiary
     * @return amount the amount of tokens that can be used for repayment
     */
    function _repayableAmountOf(address token, address beneficiary) internal view returns (uint256 amount) {
        (uint256 index,) = _currentDistributionIndex(token);
        amount = _calculateDistribution(token, beneficiary, index);
        if (beneficiary == _distributionData().defaultBeneficiary) {
            amount += _distributionData().tokens[token].initialTokens;
        }
    }

    function _operator() internal view returns (address) {
        return _distributionData().operator;
    }

    function _operatorTokens(address token) internal view returns (uint256) {
        return _distributionData().operatorTokens[token];
    }

    function _defaultBeneficiary() internal view returns (BeneficiaryData memory) {
        return _distributionData().data[_distributionData().defaultBeneficiary];
    }

    function _owner() internal view returns (address) {
        return _distributionData().defaultBeneficiary;
    }

    function _beneficiaries() internal view returns (address[] memory) {
        return _distributionData().beneficiaries;
    }

    function _beneficiaryData(address beneficiary) internal view returns (BeneficiaryData storage) {
        return _distributionData().data[beneficiary];
    }

    /// HELPERS ///

    /// @notice update the distribution index by registering incoming funds since the last index update
    function _updateDistributionIndex(address token) private {
        (uint256 newIndex, uint256 newDistribution) = _currentDistributionIndex(token);
        if (newDistribution != 0) {
            _distributionData().tokens[token].balanceTracker += newDistribution;
            emit Distributed(token, newDistribution);
        }
        _distributionData().tokens[token].index = newIndex;
    }

    /**
     * @notice calculates the current distribution index based on the current token balance
     * @dev reverts if no beneficiaries are set up
     * @dev keeps track of incoming tokens by caching the previous balance. Whenever funds reach the contract it's assumed that these are funds to be distributed. If funds leave the contract the `_registerRepayment` function must be called in order not to break accounting
     * @dev keeps track of repaid beneficiaries
     * @dev if no beneficiaries are set up this function will revert, as total allocation is 0
     */
    function _currentDistributionIndex(address token)
        private
        view
        returns (uint256 newIndex, uint256 newDistribution)
    {
        uint256 totalAllocation = _totalAllocation();
        uint256 index = _distributionData().tokens[token].index;
        uint256 currentDistributionBalance = IERC20(token).balanceOf(address(this));
        newDistribution = currentDistributionBalance - _distributionData().tokens[token].balanceTracker
            - _distributionData().operatorTokens[token];
        newIndex = index + (newDistribution * MAGNITUDE) / totalAllocation;
    }

    /// @notice calculates the distributed funds of a beneficiary based on a passed index and taking into account prior repaid amounts
    function _calculateDistribution(address token, address beneficiary, uint256 index) private view returns (uint256) {
        uint256 repaidAmount = _distributionData().repaidAmounts[token][beneficiary];
        if (repaidAmount == MAX_INT) return 0;
        uint256 distributedAmount = (_allocationOf(token, beneficiary) * index) / MAGNITUDE;
        return distributedAmount - repaidAmount;
    }

    function _distributionData() internal view returns (DistributionData storage) {
        return _data[_nonce];
    }
}

File 46 of 49 : IOTC.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

interface IOTC {

    struct OTC {
        address counterparty;
        address tokenGoingOut;
        uint256 tokenGoingOutAmount;
        address tokenComingIn;
        uint256 tokenComingInAmount;
        uint256 expiration;
    }

}

File 47 of 49 : IEscrow.sol
// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

import {IOTC} from "../interfaces/IOTC.sol";
import {INonFungiblePositionManager} from "./INonFungiblePositionManager.sol";

interface IEscrow {
    struct Deposit {
        uint256 amount;
        bool isERC4626;
        address asset; // eip4626 asset else the erc20 token itself
        uint8 assetDecimals;
    }

    event AddCollateral(address indexed token, uint256 indexed amount);
    event RemoveCollateral(address indexed token, uint256 indexed amount);
    event EnableCollateral(address indexed token);
    event EnablePairAsCollateral(address indexed tokenA, address indexed tokenB);
    event UpdateMinimumCollateralRatio(uint256 indexed ratio);
    event UpdateBorrower(address indexed borrower, address indexed newBorrower);

    error ExpiredOrder();
    error InvalidCollateral();
    error CallerAccessDenied();
    error UnderCollateralized();
    error NotLiquidatable();
    error InvalidFunctionSelector();
    error InvalidRecipient();
    error AlreadyInitialized();
    error CannotModifyWithActivePosition();
    error UnregisteredContract(address _contract);

    // State var getters.
    function owner() external view returns (address);
    function oracle() external view returns (address, address, address);
    function borrower() external view returns (address);
    function minimumCollateralRatio() external view returns (uint32);
    function getCollateralData(address token) external view returns (Deposit memory);

    function getCollateralRatio() external returns (uint256);
    function getCollateralValue() external returns (uint256);

    // Functions

    function initializeFromFactory(
        uint32 _minimumCollateralRatio,
        address _owner,
        address _borrower,
        address _nftPositionManager,
        address _univ3Manager
    ) external;

    function setUniswapV3Manager(address _manager) external;
    function isLiquidatable() external returns (bool);

    function updateOwner(address owner_) external returns (bool);

    function addCollateral(uint256 amount, address token) external payable;
    function enableTokenPairAsCollateral(address token0, address token1, uint24 fee) external;
    function addUniV3Position(uint256 tokenId) external returns (bool);
    function enableCollateral(address token) external returns (bool);
    function isCollateralEnabled(address token) external view returns (bool);
    function enablePosition(uint256 tokenId) external;
    function releaseCollateral(uint256 amount, address token, address to) external;
    function releasePosition(uint256 tokenId, address to) external;
    function otcSwap(IOTC.OTC memory otc) external;
    function updateAllowedOTCPriceImpact(uint128 allowedPriceImpact) external;
    function updateStableCoinWhitelist(address token, bool isAllowed) external;
    function liquidate(uint256 amount, address token, address to) external;
    function liquidateUniV3Position(uint256 tokenId, address to) external;
    function updateBorrower(address newBorrower) external;
    function updateMinimumCollateralRatio(uint32 minimumCollateralRatio) external;
    function registerBorrowerContract(address contractToRegister, bool state) external;

    // Recovery functionality
    function recoverEscrowTokens(address token, address to, uint256 amount) external;
    function recoverUniV3Positions(address to) external;

    // Operation funcs
    function whitelistFunction(bytes4 functionToRegister, bool state) external;
    function operate(address targetContract, bytes calldata data) external;
    function sync() external;

    // Uniswap V3 functions
    function increaseLiquidity(INonFungiblePositionManager.IncreaseLiquidityParams calldata params) external;
    function decreaseLiquidity(INonFungiblePositionManager.DecreaseLiquidityParams calldata params) external;
    function collect(INonFungiblePositionManager.CollectParams calldata params) external;
    function burn(INonFungiblePositionManager.DecreaseLiquidityParams calldata params) external;
    function mint(address pool, INonFungiblePositionManager.MintParams memory params) external;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.25;

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

interface IVaultAdmin {
    /*//////////////////////////////////////////////////////////////
                                EVENTS
    //////////////////////////////////////////////////////////////*/

    event SetManager(address indexed newManager);
    event SetOperator(address indexed operator, bool indexed approved);
    event SetFee(uint16 feeBps, uint256 indexed feeType);
    event SetProtocolTreasury(address indexed newTreasury);
    event SetWhitelistStatus(bool indexed status);
    event UpdateProtocolSplit(uint8 indexed newSplit);
    event UpdateWhitelist(address indexed whitelistedAddress, bool indexed approved);

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

    error CannotBeZeroAddress();
    error AddressNotWhitelisted();
    error OnlyVaultManager();
    error OnlyOperator();
    error OnlyProtocolTreasury();
    error WhitelistNotEnabled();
    error ProtocolSplitExceedsMaximumAllowed();

    /*//////////////////////////////////////////////////////////////
                                FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function protocolTreasury() external returns (address);

    function manager() external returns (address);

    function isOperator(address operator) external returns (bool);

    function whitelistedAddresses(address) external returns (bool);

    function whitelistEnabled() external returns (bool);

    function protocolSplit() external returns (uint8);

    function MAX_SPLIT() external returns (uint8);

    function baseUnit() external returns (uint256);

    function updateWhitelistStatus(bool status) external;

    function updateWhitelist(address depositor, bool approved) external;

    function setManager(address newManager) external;

    function setOperator(address operator, bool approved) external;

    function setProtocolTreasury(address newProtocolTreasury) external;

}

// SPDX-License-Identifier: GPL-3.0
// Copyright: https://github.com/credit-cooperative/Line-Of-Credit-v2/blob/master/COPYRIGHT.md

pragma solidity ^0.8.25;

interface INonFungiblePositionManager {
    struct Position {
        uint96 nonce;
        address operator;
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint128 liquidity;
        uint256 feeGrowthInside0LastX128;
        uint256 feeGrowthInside1LastX128;
        uint128 tokensOwed0;
        uint128 tokensOwed1;
    }

    function positions(uint256 tokenId) external view returns (Position memory);

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    /// @notice Creates a new position wrapped in a NFT
    /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
    /// a method does not exist, i.e. the pool is assumed to be initialized.
    /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
    /// @return tokenId The ID of the token that represents the minted position
    /// @return liquidity The amount of liquidity for this position
    /// @return amount0 The amount of token0
    /// @return amount1 The amount of token1
    function mint(MintParams calldata params)
        external
        payable
        returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
    /// @param params tokenId The ID of the token for which liquidity is being increased,
    /// amount0Desired The desired amount of token0 to be spent,
    /// amount1Desired The desired amount of token1 to be spent,
    /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
    /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return liquidity The new liquidity amount as a result of the increase
    /// @return amount0 The amount of token0 to acheive resulting liquidity
    /// @return amount1 The amount of token1 to acheive resulting liquidity
    function increaseLiquidity(IncreaseLiquidityParams calldata params)
        external
        payable
        returns (uint128 liquidity, uint256 amount0, uint256 amount1);

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    /// @notice Decreases the amount of liquidity in a position and accounts it to the position
    /// @param params tokenId The ID of the token for which liquidity is being decreased,
    /// amount The amount by which liquidity will be decreased,
    /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
    /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
    /// deadline The time by which the transaction must be included to effect the change
    /// @return amount0 The amount of token0 accounted to the position's tokens owed
    /// @return amount1 The amount of token1 accounted to the position's tokens owed
    function decreaseLiquidity(DecreaseLiquidityParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
    /// @param params tokenId The ID of the NFT for which tokens are being collected,
    /// recipient The account that should receive the tokens,
    /// amount0Max The maximum amount of token0 to collect,
    /// amount1Max The maximum amount of token1 to collect
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
    /// must be collected first.
    /// @param tokenId The ID of the token that is being burned
    function burn(uint256 tokenId) external payable;
}

Settings
{
  "remappings": [
    "chainlink/=lib/chainlink/contracts/src/v0.8/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "Line-Of-Credit-v2/=lib/Line-Of-Credit-v2/contracts/",
    "forge-std/=lib/forge-std/src/",
    "@chimera/=lib/chimera/src/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@uniswap/v3-core/=lib/Line-Of-Credit-v2/lib/v3-core/",
    "ERC4626/=lib/properties/lib/ERC4626/contracts/",
    "chimera/=lib/chimera/src/",
    "ds-test/=lib/Line-Of-Credit-v2/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/erc4626-tests/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "properties/=lib/properties/contracts/",
    "solmate/=lib/properties/lib/solmate/src/",
    "uniswap-periphery/=lib/Line-Of-Credit-v2/lib/v3-periphery/contracts/",
    "uniswap/=lib/Line-Of-Credit-v2/lib/v3-core/contracts/",
    "v3-core/=lib/Line-Of-Credit-v2/lib/v3-core/contracts/",
    "v3-periphery/=lib/Line-Of-Credit-v2/lib/v3-periphery/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"asset_","type":"address"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"liquidStrategy_","type":"address"},{"internalType":"address","name":"factory_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[],"name":"AddressNotWhitelisted","type":"error"},{"inputs":[],"name":"ArrayLengthsMustMatch","type":"error"},{"inputs":[],"name":"CannotBeZeroAddress","type":"error"},{"inputs":[],"name":"CreditStrategyAlreadySet","type":"error"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"uint256","name":"sharesToDeposit","type":"uint256"},{"internalType":"uint256","name":"minShares","type":"uint256"},{"internalType":"uint256","name":"maxShares","type":"uint256"}],"name":"DepositRequestOutsideLimits","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":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxDeposit","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxMint","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxRedeem","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"ERC4626ExceededMaxWithdraw","type":"error"},{"inputs":[],"name":"ERC4626ZeroAssets","type":"error"},{"inputs":[],"name":"ERC4626ZeroShares","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"requestedDepositAssets","type":"uint256"}],"name":"ERC7540ExceededDepositRequest","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"requestedRedeemAssets","type":"uint256"}],"name":"ERC7540ExceededRedeemRequest","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"InsufficientAvailableAssets","type":"error"},{"inputs":[],"name":"OnlyCreditStrategy","type":"error"},{"inputs":[],"name":"OnlyDeployerFactory","type":"error"},{"inputs":[],"name":"OnlyNativeAsset","type":"error"},{"inputs":[],"name":"OnlyOperator","type":"error"},{"inputs":[],"name":"OnlyOwnerOrOperator","type":"error"},{"inputs":[],"name":"OnlyPauser","type":"error"},{"inputs":[],"name":"OnlyVaultManager","type":"error"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"uint256","name":"assetsToWithdraw","type":"uint256"},{"internalType":"uint256","name":"minAssets","type":"uint256"},{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"name":"RedeemRequestOutsideLimits","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"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":"uint256","name":"assets","type":"uint256"}],"name":"CancelDepositRequest","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":"uint256","name":"shares","type":"uint256"}],"name":"CancelRedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creditStrategy","type":"address"}],"name":"CreditStrategySet","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":[{"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":"DepositRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidStrategy","type":"address"}],"name":"LiquidStrategySet","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":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"ProcessDepositRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"ProcessRedeemRequest","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":"shares","type":"uint256"}],"name":"RedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newStrategy","type":"address"}],"name":"StrategyContractTransfered","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":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensRecovered","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":"address","name":"account","type":"address"}],"name":"Unpaused","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":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"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":"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":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOfUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"controllers","type":"address[]"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"uint256[]","name":"minShares","type":"uint256[]"},{"internalType":"uint256[]","name":"maxShares","type":"uint256[]"}],"name":"batchProcessDepositRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"controllers","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"},{"internalType":"uint256[]","name":"minAssets","type":"uint256[]"},{"internalType":"uint256[]","name":"maxAssets","type":"uint256[]"}],"name":"batchProcessRedeemRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"cancelDepositRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"cancelRedeemRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"claimableDepositRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"claimableDepositRequestShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"claimableRedeemRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"claimableRedeemRequestAssets","outputs":[{"internalType":"uint256","name":"","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":"creditStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"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":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"strategyType","type":"uint256"}],"name":"depositIntoStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidStrategy","outputs":[{"internalType":"contract ILiquidStrategy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newLiquidStrategy","type":"address"}],"name":"migrateLiquidStrategy","outputs":[],"stateMutability":"nonpayable","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":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"mintFeeShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"pendingDepositRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"pendingRedeemRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"uint256","name":"minShares","type":"uint256"},{"internalType":"uint256","name":"maxShares","type":"uint256"}],"name":"processDepositRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"minAssets","type":"uint256"},{"internalType":"uint256","name":"maxAssets","type":"uint256"}],"name":"processRedeemRequest","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverTokens","outputs":[],"stateMutability":"nonpayable","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":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"controller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"requestDeposit","outputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"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":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_creditStrategy","type":"address"},{"internalType":"address[]","name":"pausers","type":"address[]"}],"name":"setCreditStrategy","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":[],"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":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":"totalClaimableRedeemAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquidAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPendingDeposits","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":"value","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":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlyingAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","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":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"strategyType","type":"uint256"}],"name":"withdrawFromStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"}]

610100604052600880546001600160a01b03191630179055348015610022575f80fd5b506040516146c53803806146c583398101604081905261004191610321565b84848460036100508382610435565b50600461005d8282610435565b5050505f806100718361018060201b60201c565b9150915081610081576012610083565b805b60ff1660a05250506001600160a01b039081166080526005805460ff191690556001600755851615806100bd57506001600160a01b038216155b806100cf57506001600160a01b038116155b156100ed57604051631e7d738760e21b815260040160405180910390fd5b6001600160a01b03851660e052610102610256565b61010d90600a6105ea565b60c052601380546001600160a01b0319166001600160a01b0384169081179091556040517f5f8a2b972ab25d04d371ec1ca81f5dd55a3fda51cbda074623e61d1870ddf4a2905f90a2601280546001600160a01b0319166001600160a01b03929092169190911790555061064592505050565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b17905290515f918291829182916001600160a01b038716916101c6916105ff565b5f60405180830381855afa9150503d805f81146101fe576040519150601f19603f3d011682016040523d82523d5f602084013e610203565b606091505b509150915081801561021757506020815110155b1561024a575f818060200190518101906102319190610615565b905060ff8111610248576001969095509350505050565b505b505f9485945092505050565b5f8060a051610265919061062c565b905090565b80516001600160a01b0381168114610280575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f8301126102a8575f80fd5b81516001600160401b03808211156102c2576102c2610285565b604051601f8301601f19908116603f011681019082821181831017156102ea576102ea610285565b81604052838152866020858801011115610302575f80fd5b8360208701602083015e5f602085830101528094505050505092915050565b5f805f805f60a08688031215610335575f80fd5b61033e8661026a565b60208701519095506001600160401b038082111561035a575f80fd5b61036689838a01610299565b9550604088015191508082111561037b575f80fd5b5061038888828901610299565b9350506103976060870161026a565b91506103a56080870161026a565b90509295509295909350565b600181811c908216806103c557607f821691505b6020821081036103e357634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561043057805f5260205f20601f840160051c8101602085101561040e5750805b601f840160051c820191505b8181101561042d575f815560010161041a565b50505b505050565b81516001600160401b0381111561044e5761044e610285565b6104628161045c84546103b1565b846103e9565b602080601f831160018114610495575f841561047e5750858301515b5f19600386901b1c1916600185901b1785556104ec565b5f85815260208120601f198616915b828110156104c3578886015182559484019460019091019084016104a4565b50858210156104e057878501515f19600388901b60f8161c191681555b505060018460011b0185555b505050505050565b634e487b7160e01b5f52601160045260245ffd5b600181815b8085111561054257815f1904821115610528576105286104f4565b8085161561053557918102915b93841c939080029061050d565b509250929050565b5f82610558575060016105e4565b8161056457505f6105e4565b816001811461057a5760028114610584576105a0565b60019150506105e4565b60ff841115610595576105956104f4565b50506001821b6105e4565b5060208310610133831016604e8410600b84101617156105c3575081810a6105e4565b6105cd8383610508565b805f19048211156105e0576105e06104f4565b0290505b92915050565b5f6105f860ff84168361054a565b9392505050565b5f82518060208501845e5f920191825250919050565b5f60208284031215610625575f80fd5b5051919050565b60ff81811683821601908111156105e4576105e46104f4565b60805160a05160c05160e051613fc86106fd5f395f818161070301528181610fdd0152818161106f015281816110aa0152818161113c015281816114400152818161159801528181611630015281816117160152818161179d015281816117ec0152818161187901528181611c1b01528181611cb20152818161218701528181612a4b01528181612f5101526131c501525f818161086701528181611514015261154e01525f610e8b01525f61058c0152613fc85ff3fe608060405234801561000f575f80fd5b5060043610610401575f3560e01c80637d41c86e11610216578063ba0876521161012a578063da39b3e7116100b4578063e721135a11610084578063e721135a14610975578063eaed1d0714610988578063ef8b30f714610491578063f5a23d8d146109b2578063f840cb83146109dc575f80fd5b8063da39b3e714610903578063dd62ed3e14610916578063e22aec1e1461094e578063e63ab1e914610961575f80fd5b8063cadd1ec9116100fa578063cadd1ec9146108af578063ce96cb77146108b7578063d1389414146108ca578063d547741f146108dd578063d905777e146108f0575f80fd5b8063ba0876521461084f578063c2930f9114610862578063c63d75b614610889578063c6e6f5921461089c575f80fd5b806395d89b41116101ab578063a8d5fd651161017b578063a8d5fd65146107e9578063a9059cbb146107fc578063b3d7f6b914610491578063b460af941461080f578063b6363cf214610822575f80fd5b806395d89b411461079d578063995ea21a146107a55780639d264f27146107cf578063a217fddf146107e2575f80fd5b80638852b110116101e65780638852b1101461075b578063892b37f51461076e57806391d148541461077757806394bf804d1461078a575f80fd5b80637d41c86e146107255780638456cb591461073857806385b77f45146107405780638726972914610753575f80fd5b806336568abe116103185780634dd04a3c116102a25780636786508f116102725780636786508f146106865780636e553f65146106b05780636fe0edbb146106c357806370a08231146106d65780637158da7c146106fe575f80fd5b80634dd04a3c1461062b578063558a7297146106555780635c975abb146106685780635f3e849f14610673575f80fd5b8063402d267d116102e8578063402d267d146105df5780634a3a418d146105f25780634ab85713146106055780634bb8045e146106185780634cdad50614610491575f80fd5b806336568abe1461057757806338d52e0f1461058a5780633af9e669146105c45780633f4ba83a146105d7575f80fd5b80630fbc66e811610399578063248a9ca311610369578063248a9ca3146104eb57806326c6f96c1461050d5780632e2d2984146105375780632f2ff15d1461054a578063313ce5671461055d575f80fd5b80630fbc66e8146104a857806318160ddd146104bb57806320a7d47f146104c357806323b872dd146104d8575f80fd5b806307a2d13a116103d457806307a2d13a1461046b578063095ea7b31461047e5780630a28a477146104915780630a8810821461049f575f80fd5b806301e1d1141461040557806301ffc9a71461042057806306bf8e951461044357806306fdde0314610456575b5f80fd5b61040d6109ef565b6040519081526020015b60405180910390f35b61043361042e366004613822565b610a71565b6040519015158152602001610417565b61043361045136600461385d565b610b07565b61045e610b37565b604051610417919061389c565b61040d6104793660046138d1565b610bc7565b61043361048c3660046138e8565b610bd2565b61040d6104013660046138d1565b61040d60105481565b6104336104b6366004613a4b565b610be9565b60025461040d565b6104d66104d1366004613af1565b610d19565b005b6104336104e6366004613b3e565b610dc2565b61040d6104f93660046138d1565b5f9081526006602052604090206001015490565b61040d61051b366004613b7c565b600960209081525f928352604080842090915290825290205481565b61040d61054536600461385d565b610de5565b6104d6610558366004613b7c565b610e5a565b610565610e84565b60405160ff9091168152602001610417565b6104d6610585366004613b7c565b610eaf565b7f00000000000000000000000000000000000000000000000000000000000000005b6040516001600160a01b039091168152602001610417565b61040d6105d2366004613baa565b610ee7565b6104d6610f09565b61040d6105ed366004613baa565b610f7d565b6104d66106003660046138e8565b610fa3565b6013546105ac906001600160a01b031681565b6104d6610626366004613bc5565b610fb5565b61040d610639366004613b7c565b600b60209081525f928352604080842090915290825290205481565b610433610663366004613bf2565b611166565b60055460ff16610433565b6104d6610681366004613b3e565b6111d5565b61040d610694366004613b7c565b600e60209081525f928352604080842090915290825290205481565b61040d6106be366004613b7c565b61124d565b6104336106d136600461385d565b6112b3565b61040d6106e4366004613baa565b6001600160a01b03165f9081526020819052604090205490565b6105ac7f000000000000000000000000000000000000000000000000000000000000000081565b61040d61073336600461385d565b6112d0565b6104d6611396565b61040d61074e36600461385d565b611408565b61040d611500565b6104d6610769366004613baa565b611570565b61040d60115481565b610433610785366004613b7c565b6118db565b61040d610798366004613b7c565b611905565b61045e61195d565b61040d6107b3366004613b7c565b600a60209081525f928352604080842090915290825290205481565b6104336107dd366004613c1e565b61196c565b61040d5f81565b6008546105ac906001600160a01b031681565b61043361080a3660046138e8565b6119f7565b61040d61081d36600461385d565b611a04565b610433610830366004613c56565b600f60209081525f928352604080842090915290825290205460ff1681565b61040d61085d36600461385d565b611aa7565b61040d7f000000000000000000000000000000000000000000000000000000000000000081565b61040d610897366004613baa565b611b33565b61040d6108aa3660046138d1565b611b59565b61040d611b64565b61040d6108c5366004613baa565b611d4a565b6104336108d8366004613c1e565b611d83565b6104d66108eb366004613b7c565b611e02565b61040d6108fe366004613baa565b611e26565b61040d61091136600461385d565b611e5f565b61040d610924366004613c56565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6014546105ac906001600160a01b031681565b61040d5f80516020613f3383398151915281565b610433610983366004613a4b565b611ec2565b61040d610996366004613b7c565b600d60209081525f928352604080842090915290825290205481565b61040d6109c0366004613b7c565b600c60209081525f928352604080842090915290825290205481565b6104d66109ea366004613bc5565b611fc6565b60145460408051635764ea4960e11b815290515f926001600160a01b03169163aec9d4929160048083019260209291908290030181865afa158015610a36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a5a9190613c82565b610a62611b64565b610a6c9190613cad565b905090565b5f6001600160e01b03198216630ce3bbe560e41b1480610aa157506001600160e01b03198216631883ba3960e21b145b80610abc57506001600160e01b0319821663e3bc4e6560e01b145b80610ad757506001600160e01b031982166301ffc9a760e01b145b80610af257506001600160e01b03198216632f0a18c560e01b145b80610b015750610b0182612063565b92915050565b5f610b10612097565b610b19826120c1565b610b2484848461211e565b9050610b306001600755565b9392505050565b606060038054610b4690613cc0565b80601f0160208091040260200160405190810160405280929190818152602001828054610b7290613cc0565b8015610bbd5780601f10610b9457610100808354040283529160200191610bbd565b820191905f5260205f20905b815481529060010190602001808311610ba057829003601f168201915b5050505050905090565b5f610b01825f61220e565b5f33610bdf818585612246565b5060019392505050565b5f610bf2612097565b610bfa612253565b610c0484866122da565b610c0e83866122da565b610c1882866122da565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b158015610c64575f80fd5b505af1158015610c76573d5f803e3d5ffd5b5050865191505f90505b81811015610d0157610cf8878281518110610c9d57610c9d613cf8565b6020026020010151878381518110610cb757610cb7613cf8565b6020026020010151878481518110610cd157610cd1613cf8565b6020026020010151878581518110610ceb57610ceb613cf8565b60200260200101516122fc565b50600101610c80565b506001915050610d116001600755565b949350505050565b6012546001600160a01b03163314610d4457604051637c4008fd60e01b815260040160405180910390fd5b6014546001600160a01b031615610d6e5760405163265a24af60e21b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0384169081179091556040517fe0a25868a5caf2b0ce2e18c1f7de575ff631422274e4f6a149ab178d43445c30905f90a2610dbe82826124b1565b5050565b5f33610dcf8582856125c7565b610dda85858561262a565b506001949350505050565b5f610dee612097565b610df6612687565b610e0084836126ab565b9050610e0d30848361262a565b826001600160a01b0316826001600160a01b03165f80516020613f738339815191528684604051610e48929190918252602082015260400190565b60405180910390a3610b306001600755565b5f82815260066020526040902060010154610e7481612794565b610e7e838361279e565b50505050565b5f610a6c817f0000000000000000000000000000000000000000000000000000000000000000613d0c565b6001600160a01b0381163314610ed85760405163334bd91960e11b815260040160405180910390fd5b610ee2828261282f565b505050565b6001600160a01b0381165f90815260208190526040812054610b01905f61220e565b610f1161289a565b60145f9054906101000a90046001600160a01b03166001600160a01b031663f677566e6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015610f5d575f80fd5b505af1158015610f6f573d5f803e3d5ffd5b50505050610f7b6128ce565b565b6001600160a01b03165f9081525f80516020613f53833981519152602052604090205490565b610fab612920565b610dbe828261294b565b610fbd61297f565b610fc682612a24565b801561109957601454611006906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911684612aeb565b60145460405163b6b55f2560e01b8152600481018490526001600160a01b039091169063b6b55f25906024015f604051808303815f87803b158015611049575f80fd5b505af115801561105b573d5f803e3d5ffd5b5050601454610dbe92506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169250165f612aeb565b6013546110d3906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911684612aeb565b60135460405163b6b55f2560e01b8152600481018490526001600160a01b039091169063b6b55f25906024015f604051808303815f87803b158015611116575f80fd5b505af1158015611128573d5f803e3d5ffd5b5050601354610dbe92506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169250165f612aeb565b335f818152600f602090815260408083206001600160a01b038716808552908352818420805460ff191687151590811790915591519182529293917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a350600192915050565b6111dd61297f565b6111f16001600160a01b0384168383612ba8565b826001600160a01b0316826001600160a01b0316336001600160a01b03167fa2231b10d9b4e4166c8a827c99f97691b05aa88fb04e009a4e499005b5c50fcc8460405161124091815260200190565b60405180910390a4505050565b5f611256612097565b61125e612687565b61126883836126ab565b905061127530838361262a565b60408051848152602081018390526001600160a01b0384169133915f80516020613f7383398151915291015b60405180910390a3610b016001600755565b5f6112bc612097565b6112c5826120c1565b610b24848484612bd9565b5f6112d9612097565b6112e1612687565b6112e9612c76565b6112f284612d7a565b6112fb826120c1565b61130682308661262a565b6001600160a01b0383165f9081525f80516020613ef3833981519152602052604081208054869290611339908490613cad565b909155505060408051338152602081018690525f916001600160a01b0380861692908716917f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc57450691015b60405180910390a4505f610b306001600755565b61139e61289a565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156113ea575f80fd5b505af11580156113fc573d5f803e3d5ffd5b50505050610f7b612d9a565b5f611411612097565b611419612687565b611421612c76565b61142a84612dd7565b611433826120c1565b6114686001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016833087612df7565b6001600160a01b0383165f9081525f80516020613ed383398151915260205260408120805486929061149b908490613cad565b925050819055508360105f8282546114b39190613cad565b909155505060408051338152602081018690525f916001600160a01b0380861692908716917fbb58420bb8ce44e11b84e214cc0de10ce5e7c24d0355b2815c3d758b514cae729101611382565b5f61150a60025490565b1561154b57610a6c7f000000000000000000000000000000000000000000000000000000000000000061153c60025490565b6115446109ef565b9190612e30565b507f000000000000000000000000000000000000000000000000000000000000000090565b61157861297f565b61158181612ee6565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156115e5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116099190613c82565b6013546040516370a0823160e01b81526001600160a01b0391821660048201529192505f917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015611677573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061169b9190613c82565b601354604051632e1a7d4d60e01b8152600481018390529192506001600160a01b031690632e1a7d4d906024015f604051808303815f87803b1580156116df575f80fd5b505af11580156116f1573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201525f92508491506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa15801561175b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061177f9190613c82565b6117899190613d25565b6013549091506117c6906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811691165f612aeb565b601380546001600160a01b0319166001600160a01b0386811691909117909155611813907f0000000000000000000000000000000000000000000000000000000000000000168583612aeb565b60135460405163b6b55f2560e01b8152600481018390526001600160a01b039091169063b6b55f25906024015f604051808303815f87803b158015611856575f80fd5b505af1158015611868573d5f803e3d5ffd5b506118a29250506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169050855f612aeb565b6040516001600160a01b038516907fccc22fd23895aef034323695dab4bf49dec908b847c8e311246f1c99961cc1a3905f90a250505050565b5f9182526006602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f61190e612097565b611916612687565b6119208383612f97565b905061192d30838561262a565b60408051828152602081018590526001600160a01b0384169133915f80516020613f7383398151915291016112a1565b606060048054610b4690613cc0565b5f611975612097565b61197d612253565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156119c9575f80fd5b505af11580156119db573d5f803e3d5ffd5b505050506119eb858585856122fc565b9050610d116001600755565b5f33610bdf81858561262a565b5f611a0d612097565b611a15612687565b611a1d612c76565b611a26826120c1565b611a2f84612dd7565b5f611a3983611d4a565b905080851115611a6b57828582604051633fa733bb60e21b8152600401611a6293929190613d38565b60405180910390fd5b5f611a7584611e26565b9050611a848187846001613074565b9250611a8f83612d7a565b611a9b848688866130bf565b5050610b306001600755565b5f611ab0612097565b611ab8612687565b611ac0612c76565b611ac9826120c1565b611ad284612d7a565b5f611adc83611e26565b905080851115611b0557828582604051632e52afbb60e21b8152600401611a6293929190613d38565b5f611b0f84611d4a565b9050611b1c818784612e30565b9250611b2783612dd7565b611a9b848685896130bf565b6001600160a01b03165f9081525f80516020613f13833981519152602052604090205490565b5f610b01825f6131ec565b6013545f9081906001600160a01b031615611bf25760135f9054906101000a90046001600160a01b03166001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bc9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bed9190613c82565b611bf4565b5f5b6014546040516370a0823160e01b81526001600160a01b0391821660048201529192505f917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015611c62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c869190613c82565b6011546010546040516370a0823160e01b81523060048201529293508392859291906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611cf7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d1b9190613c82565b611d259190613d25565b611d2f9190613d25565b611d399190613cad565b611d439190613cad565b9250505090565b6001600160a01b03165f9081527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c602052604090205490565b5f611d8c612097565b611d94612253565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611de0575f80fd5b505af1158015611df2573d5f803e3d5ffd5b505050506119eb8585858561321b565b5f82815260066020526040902060010154611e1c81612794565b610e7e838361282f565b6001600160a01b03165f9081527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee602052604090205490565b5f611e68612097565b611e70612687565b611e7a8483612f97565b9050611e8730848661262a565b826001600160a01b0316826001600160a01b03165f80516020613f738339815191528387604051610e48929190918252602082015260400190565b5f611ecb612097565b611ed3612253565b611edd84866122da565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611f29575f80fd5b505af1158015611f3b573d5f803e3d5ffd5b5050865191505f90505b81811015610d0157611fbd878281518110611f6257611f62613cf8565b6020026020010151878381518110611f7c57611f7c613cf8565b6020026020010151878481518110611f9657611f96613cf8565b6020026020010151878581518110611fb057611fb0613cf8565b602002602001015161321b565b50600101611f45565b611fce61297f565b801561203257601454604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d906024015b5f604051808303815f87803b158015612018575f80fd5b505af115801561202a573d5f803e3d5ffd5b505050505050565b601354604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d90602401612001565b5f6001600160e01b03198216637965db0b60e01b1480610b0157506301ffc9a760e01b6001600160e01b0319831614610b01565b6002600754036120ba57604051633ee5aeb560e01b815260040160405180910390fd5b6002600755565b336001600160a01b038216148015906120fd57506001600160a01b0381165f908152600f6020908152604080832033845290915290205460ff16155b1561211b5760405163089b7a0760e41b815260040160405180910390fd5b50565b5f6121298285613395565b6001600160a01b0382165f9081525f80516020613ed383398151915260205260408120805486929061215c908490613d25565b925050819055508360105f8282546121749190613d25565b909155506121ae90506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168486612ba8565b826001600160a01b0316826001600160a01b03167f7f2037a1b333cb15a71513becf8ad6d0f5a528974d8bcbb8bed7dfbfa275cc125f876040516121fc929190918252602082015260400190565b60405180910390a35060019392505050565b5f610b3061221a6109ef565b612225906001613cad565b6122305f600a613e39565b60025461223d9190613cad565b85919085613074565b610ee28383836001613403565b6014546040516336b87bd760e11b81523360048201526001600160a01b0390911690636d70f7ae90602401602060405180830381865afa158015612299573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122bd9190613e47565b610f7b576040516327e1f1e560e01b815260040160405180910390fd5b8051825114610dbe5760405163587543d160e01b815260040160405180910390fd5b5f61230785856134d5565b6001600160a01b0385165f9081525f80516020613ef383398151915260205260408120805486929061233a908490613d25565b90915550506001600160a01b0385165f9081527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee602052604081208054869290612385908490613cad565b909155505f9050612396858261220e565b90506123a181612a24565b838110806123ae57508281115b156123ec576040516330f605db60e21b81526001600160a01b0387166004820152602481018290526044810185905260648101849052608401611a62565b6001600160a01b0386165f9081527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c602052604081208054839290612432908490613cad565b925050819055508060115f82825461244a9190613cad565b9091555050604080515f8152602081018790529081018290526001600160a01b038716907f896fb80410518fac9575c540037f178a3b76fc7cdc8c6e3e2962d9c08fb7810b9060600160405180910390a26124a53086613543565b50600195945050505050565b61251c5f801b836001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125179190613e62565b61279e565b5061256a5f80516020613f33833981519152836001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f3573d5f803e3d5ffd5b506125825f80516020613f338339815191525f613577565b80515f5b81811015610e7e576125be5f80516020613f338339815191528483815181106125b1576125b1613cf8565b602002602001015161279e565b50600101612586565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610e7e578181101561261c57828183604051637dc7a0d960e11b8152600401611a6293929190613d38565b610e7e84848484035f613403565b6001600160a01b03831661265357604051634b637e8f60e11b81525f6004820152602401611a62565b6001600160a01b03821661267c5760405163ec442f0560e01b81525f6004820152602401611a62565b610ee28383836135c1565b60055460ff1615610f7b5760405163d93c066560e01b815260040160405180910390fd5b5f6126b4612c76565b6126bd826120c1565b6126c683612dd7565b5f6126d083610f7d565b9050808411156126f957828482604051633c8097d960e11b8152600401611a6293929190613d38565b5f61270384611b33565b9050612710818684612e30565b925061271b83612d7a565b6001600160a01b0384165f9081525f80516020613f5383398151915260205260408120805487929061274e908490613d25565b90915550506001600160a01b0384165f9081525f80516020613f13833981519152602052604081208054859290612786908490613d25565b909155509295945050505050565b61211b81336136d4565b5f6127a983836118db565b612828575f8381526006602090815260408083206001600160a01b03861684529091529020805460ff191660011790556127e03390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610b01565b505f610b01565b5f61283a83836118db565b15612828575f8381526006602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610b01565b6128b15f80516020613f33833981519152336118db565b610f7b57604051631d77d47760e21b815260040160405180910390fd5b6128d661370d565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6014546001600160a01b03163314610f7b57604051637c41332f60e01b815260040160405180910390fd5b6001600160a01b0382166129745760405163ec442f0560e01b81525f6004820152602401611a62565b610dbe5f83836135c1565b60145f9054906101000a90046001600160a01b03166001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129f39190613e62565b6001600160a01b0316336001600160a01b031614610f7b5760405163612bde3d60e01b815260040160405180910390fd5b6011546010546040516370a0823160e01b81523060048201525f9291906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015612a90573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ab49190613c82565b612abe9190613d25565b612ac89190613d25565b905081811015610dbe576040516359f5886d60e11b815260040160405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052612b3c8482613730565b610e7e576040516001600160a01b0384811660248301525f6044830152612b9e91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613779565b610e7e8482613779565b6040516001600160a01b03838116602483015260448201839052610ee291859182169063a9059cbb90606401612b6c565b5f612be482856134d5565b6001600160a01b0382165f9081525f80516020613ef3833981519152602052604081208054869290612c17908490613d25565b90915550612c28905030848661262a565b826001600160a01b0316826001600160a01b03167fa9f12bee47e2782b02677ac80f5d81540d9c9f9754e88e86190c120bf0e798065f876040516121fc929190918252602082015260400190565b60145f9054906101000a90046001600160a01b03166001600160a01b03166351fb012d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cc6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cea9190613e47565b8015612d5c575060145460405162d9267b60e31b81523360048201526001600160a01b03909116906306c933d890602401602060405180830381865afa158015612d36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d5a9190613e47565b155b15610f7b5760405163ad7acb4760e01b815260040160405180910390fd5b805f0361211b57604051636646782360e01b815260040160405180910390fd5b612da2612687565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586129033390565b805f0361211b57604051634fa802fd60e01b815260040160405180910390fd5b6040516001600160a01b038481166024830152838116604483015260648201839052610e7e9186918216906323b872dd90608401612b6c565b5f838302815f1985870982811083820303915050805f03612e6457838281612e5a57612e5a613e7d565b0492505050610b30565b808411612e7b57612e7b60038515026011186137e5565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b806001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f22573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f469190613e62565b6001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161461211b576040516383fc2dd960e01b815260040160405180910390fd5b5f612fa0612c76565b612fa9826120c1565b612fb283612d7a565b5f612fbc83611b33565b905080841115612fe55782848260405163284ff66760e01b8152600401611a6293929190613d38565b5f612fef84610f7d565b9050612ffe8186846001613074565b925061300983612dd7565b6001600160a01b0384165f9081525f80516020613f5383398151915260205260408120805485929061303c908490613d25565b90915550506001600160a01b0384165f9081525f80516020613f13833981519152602052604081208054879290612786908490613d25565b5f6130a1613081836137f6565b801561309c57505f848061309757613097613e7d565b868809115b151590565b6130ac868686612e30565b6130b69190613cad565b95945050505050565b6001600160a01b0384165f9081527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee602052604081208054839290613105908490613d25565b90915550506001600160a01b0384165f9081527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c602052604081208054849290613150908490613d25565b925050819055508160115f8282546131689190613d25565b909155505060408051838152602081018390526001600160a01b03808716929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4610e7e6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168484612ba8565b5f610b306131fb82600a613e39565b6002546132089190613cad565b6132106109ef565b61223d906001613cad565b5f6132268585613395565b6001600160a01b0385165f9081525f80516020613ed3833981519152602052604081208054869290613259908490613d25565b90915550506001600160a01b0385165f9081525f80516020613f53833981519152602052604081208054869290613291908490613cad565b909155505f90506132a285826131ec565b9050838110806132b157508281115b156132ef576040516364259a2160e11b81526001600160a01b0387166004820152602481018290526044810185905260648101849052608401611a62565b6001600160a01b0386165f9081525f80516020613f13833981519152602052604081208054839290613322908490613cad565b925050819055508460105f82825461333a9190613d25565b9091555050604080515f8152602081018390529081018690526001600160a01b038716907fe837e30d8e7c5a0036b281f23284d0aecdb5550b7067960d27e4c3f81064cb9c9060600160405180910390a26124a5308261294b565b6001600160a01b0382165f9081525f80516020613ed38339815191526020526040902054811115610dbe576001600160a01b0382165f9081525f80516020613ed383398151915260205260409081902054905163fe4a762b60e01b8152611a62918491849190600401613d38565b6001600160a01b03841661342c5760405163e602df0560e01b81525f6004820152602401611a62565b6001600160a01b03831661345557604051634a1406b160e11b81525f6004820152602401611a62565b6001600160a01b038085165f9081526001602090815260408083209387168352929052208290558015610e7e57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516134c791815260200190565b60405180910390a350505050565b6001600160a01b0382165f9081525f80516020613ef38339815191526020526040902054811115610dbe576001600160a01b0382165f9081525f80516020613ef38339815191526020526040908190205490516383000e7760e01b8152611a62918491849190600401613d38565b6001600160a01b03821661356c57604051634b637e8f60e11b81525f6004820152602401611a62565b610dbe825f836135c1565b5f82815260066020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6001600160a01b0383166135eb578060025f8282546135e09190613cad565b909155506136489050565b6001600160a01b0383165f908152602081905260409020548181101561362a5783818360405163391434e360e21b8152600401611a6293929190613d38565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661366457600280548290039055613682565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516136c791815260200190565b60405180910390a3505050565b6136de82826118db565b610dbe5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401611a62565b60055460ff16610f7b57604051638dfc202b60e01b815260040160405180910390fd5b5f805f8060205f8651602088015f8a5af192503d91505f51905082801561376f57508115613761578060011461376f565b5f866001600160a01b03163b115b9695505050505050565b5f8060205f8451602086015f885af180613798576040513d5f823e3d81fd5b50505f513d915081156137af5780600114156137bc565b6001600160a01b0384163b155b15610e7e57604051635274afe760e01b81526001600160a01b0385166004820152602401611a62565b634e487b715f52806020526024601cfd5b5f600282600381111561380b5761380b613e91565b6138159190613ea5565b60ff166001149050919050565b5f60208284031215613832575f80fd5b81356001600160e01b031981168114610b30575f80fd5b6001600160a01b038116811461211b575f80fd5b5f805f6060848603121561386f575f80fd5b83359250602084013561388181613849565b9150604084013561389181613849565b809150509250925092565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f602082840312156138e1575f80fd5b5035919050565b5f80604083850312156138f9575f80fd5b823561390481613849565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561394f5761394f613912565b604052919050565b5f67ffffffffffffffff82111561397057613970613912565b5060051b60200190565b5f82601f830112613989575f80fd5b8135602061399e61399983613957565b613926565b8083825260208201915060208460051b8701019350868411156139bf575f80fd5b602086015b848110156139e45780356139d781613849565b83529183019183016139c4565b509695505050505050565b5f82601f8301126139fe575f80fd5b81356020613a0e61399983613957565b8083825260208201915060208460051b870101935086841115613a2f575f80fd5b602086015b848110156139e45780358352918301918301613a34565b5f805f8060808587031215613a5e575f80fd5b843567ffffffffffffffff80821115613a75575f80fd5b613a818883890161397a565b95506020870135915080821115613a96575f80fd5b613aa2888389016139ef565b94506040870135915080821115613ab7575f80fd5b613ac3888389016139ef565b93506060870135915080821115613ad8575f80fd5b50613ae5878288016139ef565b91505092959194509250565b5f8060408385031215613b02575f80fd5b8235613b0d81613849565b9150602083013567ffffffffffffffff811115613b28575f80fd5b613b348582860161397a565b9150509250929050565b5f805f60608486031215613b50575f80fd5b8335613b5b81613849565b92506020840135613b6b81613849565b929592945050506040919091013590565b5f8060408385031215613b8d575f80fd5b823591506020830135613b9f81613849565b809150509250929050565b5f60208284031215613bba575f80fd5b8135610b3081613849565b5f8060408385031215613bd6575f80fd5b50508035926020909101359150565b801515811461211b575f80fd5b5f8060408385031215613c03575f80fd5b8235613c0e81613849565b91506020830135613b9f81613be5565b5f805f8060808587031215613c31575f80fd5b8435613c3c81613849565b966020860135965060408601359560600135945092505050565b5f8060408385031215613c67575f80fd5b8235613c7281613849565b91506020830135613b9f81613849565b5f60208284031215613c92575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b0157610b01613c99565b600181811c90821680613cd457607f821691505b602082108103613cf257634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b60ff8181168382160190811115610b0157610b01613c99565b81810381811115610b0157610b01613c99565b6001600160a01b039390931683526020830191909152604082015260600190565b600181815b80851115613d9357815f1904821115613d7957613d79613c99565b80851615613d8657918102915b93841c9390800290613d5e565b509250929050565b5f82613da957506001610b01565b81613db557505f610b01565b8160018114613dcb5760028114613dd557613df1565b6001915050610b01565b60ff841115613de657613de6613c99565b50506001821b610b01565b5060208310610133831016604e8410600b8410161715613e14575081810a610b01565b613e1e8383613d59565b805f1904821115613e3157613e31613c99565b029392505050565b5f610b3060ff841683613d9b565b5f60208284031215613e57575f80fd5b8151610b3081613be5565b5f60208284031215613e72575f80fd5b8151610b3081613849565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60ff831680613ec357634e487b7160e01b5f52601260045260245ffd5b8060ff8416069150509291505056feec8156718a8372b1db44bb411437d0870f3e3790d4a08526d024ce1b0b668f6b13649b2456f1b42fef0f0040b3aaeabcd21a76a0f3f5defd4f583839455116e8df7de25b7f1fd6d0b5205f0e18f1f35bd7b8d84cce336588d184533ce43a6f7665d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a13da86008ba1c6922daee3e07db95305ef49ebced9f5467a0b8613fcc6b343e3dcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7a264697066735822122031b8de7e5358884c1aaf7f114113d010b1779e685e7286f831b4c3030735cfc464736f6c63430008190033000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000006df7ff4b8c6743b3ded7bb3c8f21ce7f5b9d11f5000000000000000000000000ecfd3e9333dd3d06588ed368e4696480aba6fa63000000000000000000000000000000000000000000000000000000000000001943726564697420436f6f70204c656e64696e67205661756c740000000000000000000000000000000000000000000000000000000000000000000000000000066363555344430000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610401575f3560e01c80637d41c86e11610216578063ba0876521161012a578063da39b3e7116100b4578063e721135a11610084578063e721135a14610975578063eaed1d0714610988578063ef8b30f714610491578063f5a23d8d146109b2578063f840cb83146109dc575f80fd5b8063da39b3e714610903578063dd62ed3e14610916578063e22aec1e1461094e578063e63ab1e914610961575f80fd5b8063cadd1ec9116100fa578063cadd1ec9146108af578063ce96cb77146108b7578063d1389414146108ca578063d547741f146108dd578063d905777e146108f0575f80fd5b8063ba0876521461084f578063c2930f9114610862578063c63d75b614610889578063c6e6f5921461089c575f80fd5b806395d89b41116101ab578063a8d5fd651161017b578063a8d5fd65146107e9578063a9059cbb146107fc578063b3d7f6b914610491578063b460af941461080f578063b6363cf214610822575f80fd5b806395d89b411461079d578063995ea21a146107a55780639d264f27146107cf578063a217fddf146107e2575f80fd5b80638852b110116101e65780638852b1101461075b578063892b37f51461076e57806391d148541461077757806394bf804d1461078a575f80fd5b80637d41c86e146107255780638456cb591461073857806385b77f45146107405780638726972914610753575f80fd5b806336568abe116103185780634dd04a3c116102a25780636786508f116102725780636786508f146106865780636e553f65146106b05780636fe0edbb146106c357806370a08231146106d65780637158da7c146106fe575f80fd5b80634dd04a3c1461062b578063558a7297146106555780635c975abb146106685780635f3e849f14610673575f80fd5b8063402d267d116102e8578063402d267d146105df5780634a3a418d146105f25780634ab85713146106055780634bb8045e146106185780634cdad50614610491575f80fd5b806336568abe1461057757806338d52e0f1461058a5780633af9e669146105c45780633f4ba83a146105d7575f80fd5b80630fbc66e811610399578063248a9ca311610369578063248a9ca3146104eb57806326c6f96c1461050d5780632e2d2984146105375780632f2ff15d1461054a578063313ce5671461055d575f80fd5b80630fbc66e8146104a857806318160ddd146104bb57806320a7d47f146104c357806323b872dd146104d8575f80fd5b806307a2d13a116103d457806307a2d13a1461046b578063095ea7b31461047e5780630a28a477146104915780630a8810821461049f575f80fd5b806301e1d1141461040557806301ffc9a71461042057806306bf8e951461044357806306fdde0314610456575b5f80fd5b61040d6109ef565b6040519081526020015b60405180910390f35b61043361042e366004613822565b610a71565b6040519015158152602001610417565b61043361045136600461385d565b610b07565b61045e610b37565b604051610417919061389c565b61040d6104793660046138d1565b610bc7565b61043361048c3660046138e8565b610bd2565b61040d6104013660046138d1565b61040d60105481565b6104336104b6366004613a4b565b610be9565b60025461040d565b6104d66104d1366004613af1565b610d19565b005b6104336104e6366004613b3e565b610dc2565b61040d6104f93660046138d1565b5f9081526006602052604090206001015490565b61040d61051b366004613b7c565b600960209081525f928352604080842090915290825290205481565b61040d61054536600461385d565b610de5565b6104d6610558366004613b7c565b610e5a565b610565610e84565b60405160ff9091168152602001610417565b6104d6610585366004613b7c565b610eaf565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485b6040516001600160a01b039091168152602001610417565b61040d6105d2366004613baa565b610ee7565b6104d6610f09565b61040d6105ed366004613baa565b610f7d565b6104d66106003660046138e8565b610fa3565b6013546105ac906001600160a01b031681565b6104d6610626366004613bc5565b610fb5565b61040d610639366004613b7c565b600b60209081525f928352604080842090915290825290205481565b610433610663366004613bf2565b611166565b60055460ff16610433565b6104d6610681366004613b3e565b6111d5565b61040d610694366004613b7c565b600e60209081525f928352604080842090915290825290205481565b61040d6106be366004613b7c565b61124d565b6104336106d136600461385d565b6112b3565b61040d6106e4366004613baa565b6001600160a01b03165f9081526020819052604090205490565b6105ac7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b61040d61073336600461385d565b6112d0565b6104d6611396565b61040d61074e36600461385d565b611408565b61040d611500565b6104d6610769366004613baa565b611570565b61040d60115481565b610433610785366004613b7c565b6118db565b61040d610798366004613b7c565b611905565b61045e61195d565b61040d6107b3366004613b7c565b600a60209081525f928352604080842090915290825290205481565b6104336107dd366004613c1e565b61196c565b61040d5f81565b6008546105ac906001600160a01b031681565b61043361080a3660046138e8565b6119f7565b61040d61081d36600461385d565b611a04565b610433610830366004613c56565b600f60209081525f928352604080842090915290825290205460ff1681565b61040d61085d36600461385d565b611aa7565b61040d7f00000000000000000000000000000000000000000000000000000000000f424081565b61040d610897366004613baa565b611b33565b61040d6108aa3660046138d1565b611b59565b61040d611b64565b61040d6108c5366004613baa565b611d4a565b6104336108d8366004613c1e565b611d83565b6104d66108eb366004613b7c565b611e02565b61040d6108fe366004613baa565b611e26565b61040d61091136600461385d565b611e5f565b61040d610924366004613c56565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6014546105ac906001600160a01b031681565b61040d5f80516020613f3383398151915281565b610433610983366004613a4b565b611ec2565b61040d610996366004613b7c565b600d60209081525f928352604080842090915290825290205481565b61040d6109c0366004613b7c565b600c60209081525f928352604080842090915290825290205481565b6104d66109ea366004613bc5565b611fc6565b60145460408051635764ea4960e11b815290515f926001600160a01b03169163aec9d4929160048083019260209291908290030181865afa158015610a36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a5a9190613c82565b610a62611b64565b610a6c9190613cad565b905090565b5f6001600160e01b03198216630ce3bbe560e41b1480610aa157506001600160e01b03198216631883ba3960e21b145b80610abc57506001600160e01b0319821663e3bc4e6560e01b145b80610ad757506001600160e01b031982166301ffc9a760e01b145b80610af257506001600160e01b03198216632f0a18c560e01b145b80610b015750610b0182612063565b92915050565b5f610b10612097565b610b19826120c1565b610b2484848461211e565b9050610b306001600755565b9392505050565b606060038054610b4690613cc0565b80601f0160208091040260200160405190810160405280929190818152602001828054610b7290613cc0565b8015610bbd5780601f10610b9457610100808354040283529160200191610bbd565b820191905f5260205f20905b815481529060010190602001808311610ba057829003601f168201915b5050505050905090565b5f610b01825f61220e565b5f33610bdf818585612246565b5060019392505050565b5f610bf2612097565b610bfa612253565b610c0484866122da565b610c0e83866122da565b610c1882866122da565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b158015610c64575f80fd5b505af1158015610c76573d5f803e3d5ffd5b5050865191505f90505b81811015610d0157610cf8878281518110610c9d57610c9d613cf8565b6020026020010151878381518110610cb757610cb7613cf8565b6020026020010151878481518110610cd157610cd1613cf8565b6020026020010151878581518110610ceb57610ceb613cf8565b60200260200101516122fc565b50600101610c80565b506001915050610d116001600755565b949350505050565b6012546001600160a01b03163314610d4457604051637c4008fd60e01b815260040160405180910390fd5b6014546001600160a01b031615610d6e5760405163265a24af60e21b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0384169081179091556040517fe0a25868a5caf2b0ce2e18c1f7de575ff631422274e4f6a149ab178d43445c30905f90a2610dbe82826124b1565b5050565b5f33610dcf8582856125c7565b610dda85858561262a565b506001949350505050565b5f610dee612097565b610df6612687565b610e0084836126ab565b9050610e0d30848361262a565b826001600160a01b0316826001600160a01b03165f80516020613f738339815191528684604051610e48929190918252602082015260400190565b60405180910390a3610b306001600755565b5f82815260066020526040902060010154610e7481612794565b610e7e838361279e565b50505050565b5f610a6c817f0000000000000000000000000000000000000000000000000000000000000006613d0c565b6001600160a01b0381163314610ed85760405163334bd91960e11b815260040160405180910390fd5b610ee2828261282f565b505050565b6001600160a01b0381165f90815260208190526040812054610b01905f61220e565b610f1161289a565b60145f9054906101000a90046001600160a01b03166001600160a01b031663f677566e6040518163ffffffff1660e01b81526004015f604051808303815f87803b158015610f5d575f80fd5b505af1158015610f6f573d5f803e3d5ffd5b50505050610f7b6128ce565b565b6001600160a01b03165f9081525f80516020613f53833981519152602052604090205490565b610fab612920565b610dbe828261294b565b610fbd61297f565b610fc682612a24565b801561109957601454611006906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488116911684612aeb565b60145460405163b6b55f2560e01b8152600481018490526001600160a01b039091169063b6b55f25906024015f604051808303815f87803b158015611049575f80fd5b505af115801561105b573d5f803e3d5ffd5b5050601454610dbe92506001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881169250165f612aeb565b6013546110d3906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488116911684612aeb565b60135460405163b6b55f2560e01b8152600481018490526001600160a01b039091169063b6b55f25906024015f604051808303815f87803b158015611116575f80fd5b505af1158015611128573d5f803e3d5ffd5b5050601354610dbe92506001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881169250165f612aeb565b335f818152600f602090815260408083206001600160a01b038716808552908352818420805460ff191687151590811790915591519182529293917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a350600192915050565b6111dd61297f565b6111f16001600160a01b0384168383612ba8565b826001600160a01b0316826001600160a01b0316336001600160a01b03167fa2231b10d9b4e4166c8a827c99f97691b05aa88fb04e009a4e499005b5c50fcc8460405161124091815260200190565b60405180910390a4505050565b5f611256612097565b61125e612687565b61126883836126ab565b905061127530838361262a565b60408051848152602081018390526001600160a01b0384169133915f80516020613f7383398151915291015b60405180910390a3610b016001600755565b5f6112bc612097565b6112c5826120c1565b610b24848484612bd9565b5f6112d9612097565b6112e1612687565b6112e9612c76565b6112f284612d7a565b6112fb826120c1565b61130682308661262a565b6001600160a01b0383165f9081525f80516020613ef3833981519152602052604081208054869290611339908490613cad565b909155505060408051338152602081018690525f916001600160a01b0380861692908716917f1fdc681a13d8c5da54e301c7ce6542dcde4581e4725043fdab2db12ddc57450691015b60405180910390a4505f610b306001600755565b61139e61289a565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156113ea575f80fd5b505af11580156113fc573d5f803e3d5ffd5b50505050610f7b612d9a565b5f611411612097565b611419612687565b611421612c76565b61142a84612dd7565b611433826120c1565b6114686001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816833087612df7565b6001600160a01b0383165f9081525f80516020613ed383398151915260205260408120805486929061149b908490613cad565b925050819055508360105f8282546114b39190613cad565b909155505060408051338152602081018690525f916001600160a01b0380861692908716917fbb58420bb8ce44e11b84e214cc0de10ce5e7c24d0355b2815c3d758b514cae729101611382565b5f61150a60025490565b1561154b57610a6c7f00000000000000000000000000000000000000000000000000000000000f424061153c60025490565b6115446109ef565b9190612e30565b507f00000000000000000000000000000000000000000000000000000000000f424090565b61157861297f565b61158181612ee6565b6040516370a0823160e01b81523060048201525f907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a0823190602401602060405180830381865afa1580156115e5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116099190613c82565b6013546040516370a0823160e01b81526001600160a01b0391821660048201529192505f917f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48909116906370a0823190602401602060405180830381865afa158015611677573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061169b9190613c82565b601354604051632e1a7d4d60e01b8152600481018390529192506001600160a01b031690632e1a7d4d906024015f604051808303815f87803b1580156116df575f80fd5b505af11580156116f1573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201525f92508491506001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823190602401602060405180830381865afa15801561175b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061177f9190613c82565b6117899190613d25565b6013549091506117c6906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48811691165f612aeb565b601380546001600160a01b0319166001600160a01b0386811691909117909155611813907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168583612aeb565b60135460405163b6b55f2560e01b8152600481018390526001600160a01b039091169063b6b55f25906024015f604051808303815f87803b158015611856575f80fd5b505af1158015611868573d5f803e3d5ffd5b506118a29250506001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169050855f612aeb565b6040516001600160a01b038516907fccc22fd23895aef034323695dab4bf49dec908b847c8e311246f1c99961cc1a3905f90a250505050565b5f9182526006602090815260408084206001600160a01b0393909316845291905290205460ff1690565b5f61190e612097565b611916612687565b6119208383612f97565b905061192d30838561262a565b60408051828152602081018590526001600160a01b0384169133915f80516020613f7383398151915291016112a1565b606060048054610b4690613cc0565b5f611975612097565b61197d612253565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156119c9575f80fd5b505af11580156119db573d5f803e3d5ffd5b505050506119eb858585856122fc565b9050610d116001600755565b5f33610bdf81858561262a565b5f611a0d612097565b611a15612687565b611a1d612c76565b611a26826120c1565b611a2f84612dd7565b5f611a3983611d4a565b905080851115611a6b57828582604051633fa733bb60e21b8152600401611a6293929190613d38565b60405180910390fd5b5f611a7584611e26565b9050611a848187846001613074565b9250611a8f83612d7a565b611a9b848688866130bf565b5050610b306001600755565b5f611ab0612097565b611ab8612687565b611ac0612c76565b611ac9826120c1565b611ad284612d7a565b5f611adc83611e26565b905080851115611b0557828582604051632e52afbb60e21b8152600401611a6293929190613d38565b5f611b0f84611d4a565b9050611b1c818784612e30565b9250611b2783612dd7565b611a9b848685896130bf565b6001600160a01b03165f9081525f80516020613f13833981519152602052604090205490565b5f610b01825f6131ec565b6013545f9081906001600160a01b031615611bf25760135f9054906101000a90046001600160a01b03166001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611bc9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bed9190613c82565b611bf4565b5f5b6014546040516370a0823160e01b81526001600160a01b0391821660048201529192505f917f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48909116906370a0823190602401602060405180830381865afa158015611c62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c869190613c82565b6011546010546040516370a0823160e01b81523060048201529293508392859291906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823190602401602060405180830381865afa158015611cf7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d1b9190613c82565b611d259190613d25565b611d2f9190613d25565b611d399190613cad565b611d439190613cad565b9250505090565b6001600160a01b03165f9081527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c602052604090205490565b5f611d8c612097565b611d94612253565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611de0575f80fd5b505af1158015611df2573d5f803e3d5ffd5b505050506119eb8585858561321b565b5f82815260066020526040902060010154611e1c81612794565b610e7e838361282f565b6001600160a01b03165f9081527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee602052604090205490565b5f611e68612097565b611e70612687565b611e7a8483612f97565b9050611e8730848661262a565b826001600160a01b0316826001600160a01b03165f80516020613f738339815191528387604051610e48929190918252602082015260400190565b5f611ecb612097565b611ed3612253565b611edd84866122da565b60145f9054906101000a90046001600160a01b03166001600160a01b03166337a4e8346040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611f29575f80fd5b505af1158015611f3b573d5f803e3d5ffd5b5050865191505f90505b81811015610d0157611fbd878281518110611f6257611f62613cf8565b6020026020010151878381518110611f7c57611f7c613cf8565b6020026020010151878481518110611f9657611f96613cf8565b6020026020010151878581518110611fb057611fb0613cf8565b602002602001015161321b565b50600101611f45565b611fce61297f565b801561203257601454604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d906024015b5f604051808303815f87803b158015612018575f80fd5b505af115801561202a573d5f803e3d5ffd5b505050505050565b601354604051632e1a7d4d60e01b8152600481018490526001600160a01b0390911690632e1a7d4d90602401612001565b5f6001600160e01b03198216637965db0b60e01b1480610b0157506301ffc9a760e01b6001600160e01b0319831614610b01565b6002600754036120ba57604051633ee5aeb560e01b815260040160405180910390fd5b6002600755565b336001600160a01b038216148015906120fd57506001600160a01b0381165f908152600f6020908152604080832033845290915290205460ff16155b1561211b5760405163089b7a0760e41b815260040160405180910390fd5b50565b5f6121298285613395565b6001600160a01b0382165f9081525f80516020613ed383398151915260205260408120805486929061215c908490613d25565b925050819055508360105f8282546121749190613d25565b909155506121ae90506001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168486612ba8565b826001600160a01b0316826001600160a01b03167f7f2037a1b333cb15a71513becf8ad6d0f5a528974d8bcbb8bed7dfbfa275cc125f876040516121fc929190918252602082015260400190565b60405180910390a35060019392505050565b5f610b3061221a6109ef565b612225906001613cad565b6122305f600a613e39565b60025461223d9190613cad565b85919085613074565b610ee28383836001613403565b6014546040516336b87bd760e11b81523360048201526001600160a01b0390911690636d70f7ae90602401602060405180830381865afa158015612299573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122bd9190613e47565b610f7b576040516327e1f1e560e01b815260040160405180910390fd5b8051825114610dbe5760405163587543d160e01b815260040160405180910390fd5b5f61230785856134d5565b6001600160a01b0385165f9081525f80516020613ef383398151915260205260408120805486929061233a908490613d25565b90915550506001600160a01b0385165f9081527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee602052604081208054869290612385908490613cad565b909155505f9050612396858261220e565b90506123a181612a24565b838110806123ae57508281115b156123ec576040516330f605db60e21b81526001600160a01b0387166004820152602481018290526044810185905260648101849052608401611a62565b6001600160a01b0386165f9081527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c602052604081208054839290612432908490613cad565b925050819055508060115f82825461244a9190613cad565b9091555050604080515f8152602081018790529081018290526001600160a01b038716907f896fb80410518fac9575c540037f178a3b76fc7cdc8c6e3e2962d9c08fb7810b9060600160405180910390a26124a53086613543565b50600195945050505050565b61251c5f801b836001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125179190613e62565b61279e565b5061256a5f80516020613f33833981519152836001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124f3573d5f803e3d5ffd5b506125825f80516020613f338339815191525f613577565b80515f5b81811015610e7e576125be5f80516020613f338339815191528483815181106125b1576125b1613cf8565b602002602001015161279e565b50600101612586565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610e7e578181101561261c57828183604051637dc7a0d960e11b8152600401611a6293929190613d38565b610e7e84848484035f613403565b6001600160a01b03831661265357604051634b637e8f60e11b81525f6004820152602401611a62565b6001600160a01b03821661267c5760405163ec442f0560e01b81525f6004820152602401611a62565b610ee28383836135c1565b60055460ff1615610f7b5760405163d93c066560e01b815260040160405180910390fd5b5f6126b4612c76565b6126bd826120c1565b6126c683612dd7565b5f6126d083610f7d565b9050808411156126f957828482604051633c8097d960e11b8152600401611a6293929190613d38565b5f61270384611b33565b9050612710818684612e30565b925061271b83612d7a565b6001600160a01b0384165f9081525f80516020613f5383398151915260205260408120805487929061274e908490613d25565b90915550506001600160a01b0384165f9081525f80516020613f13833981519152602052604081208054859290612786908490613d25565b909155509295945050505050565b61211b81336136d4565b5f6127a983836118db565b612828575f8381526006602090815260408083206001600160a01b03861684529091529020805460ff191660011790556127e03390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001610b01565b505f610b01565b5f61283a83836118db565b15612828575f8381526006602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610b01565b6128b15f80516020613f33833981519152336118db565b610f7b57604051631d77d47760e21b815260040160405180910390fd5b6128d661370d565b6005805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b6014546001600160a01b03163314610f7b57604051637c41332f60e01b815260040160405180910390fd5b6001600160a01b0382166129745760405163ec442f0560e01b81525f6004820152602401611a62565b610dbe5f83836135c1565b60145f9054906101000a90046001600160a01b03166001600160a01b031663481c6a756040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129cf573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129f39190613e62565b6001600160a01b0316336001600160a01b031614610f7b5760405163612bde3d60e01b815260040160405180910390fd5b6011546010546040516370a0823160e01b81523060048201525f9291906001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823190602401602060405180830381865afa158015612a90573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ab49190613c82565b612abe9190613d25565b612ac89190613d25565b905081811015610dbe576040516359f5886d60e11b815260040160405180910390fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052612b3c8482613730565b610e7e576040516001600160a01b0384811660248301525f6044830152612b9e91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050613779565b610e7e8482613779565b6040516001600160a01b03838116602483015260448201839052610ee291859182169063a9059cbb90606401612b6c565b5f612be482856134d5565b6001600160a01b0382165f9081525f80516020613ef3833981519152602052604081208054869290612c17908490613d25565b90915550612c28905030848661262a565b826001600160a01b0316826001600160a01b03167fa9f12bee47e2782b02677ac80f5d81540d9c9f9754e88e86190c120bf0e798065f876040516121fc929190918252602082015260400190565b60145f9054906101000a90046001600160a01b03166001600160a01b03166351fb012d6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612cc6573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cea9190613e47565b8015612d5c575060145460405162d9267b60e31b81523360048201526001600160a01b03909116906306c933d890602401602060405180830381865afa158015612d36573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d5a9190613e47565b155b15610f7b5760405163ad7acb4760e01b815260040160405180910390fd5b805f0361211b57604051636646782360e01b815260040160405180910390fd5b612da2612687565b6005805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586129033390565b805f0361211b57604051634fa802fd60e01b815260040160405180910390fd5b6040516001600160a01b038481166024830152838116604483015260648201839052610e7e9186918216906323b872dd90608401612b6c565b5f838302815f1985870982811083820303915050805f03612e6457838281612e5a57612e5a613e7d565b0492505050610b30565b808411612e7b57612e7b60038515026011186137e5565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b806001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612f22573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f469190613e62565b6001600160a01b03167f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b03161461211b576040516383fc2dd960e01b815260040160405180910390fd5b5f612fa0612c76565b612fa9826120c1565b612fb283612d7a565b5f612fbc83611b33565b905080841115612fe55782848260405163284ff66760e01b8152600401611a6293929190613d38565b5f612fef84610f7d565b9050612ffe8186846001613074565b925061300983612dd7565b6001600160a01b0384165f9081525f80516020613f5383398151915260205260408120805485929061303c908490613d25565b90915550506001600160a01b0384165f9081525f80516020613f13833981519152602052604081208054879290612786908490613d25565b5f6130a1613081836137f6565b801561309c57505f848061309757613097613e7d565b868809115b151590565b6130ac868686612e30565b6130b69190613cad565b95945050505050565b6001600160a01b0384165f9081527f81955a0a11e65eac625c29e8882660bae4e165a75d72780094acae8ece9a29ee602052604081208054839290613105908490613d25565b90915550506001600160a01b0384165f9081527fe710864318d4a32f37d6ce54cb3fadbef648dd12d8dbdf53973564d56b7f881c602052604081208054849290613150908490613d25565b925050819055508160115f8282546131689190613d25565b909155505060408051838152602081018390526001600160a01b03808716929086169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4610e7e6001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168484612ba8565b5f610b306131fb82600a613e39565b6002546132089190613cad565b6132106109ef565b61223d906001613cad565b5f6132268585613395565b6001600160a01b0385165f9081525f80516020613ed3833981519152602052604081208054869290613259908490613d25565b90915550506001600160a01b0385165f9081525f80516020613f53833981519152602052604081208054869290613291908490613cad565b909155505f90506132a285826131ec565b9050838110806132b157508281115b156132ef576040516364259a2160e11b81526001600160a01b0387166004820152602481018290526044810185905260648101849052608401611a62565b6001600160a01b0386165f9081525f80516020613f13833981519152602052604081208054839290613322908490613cad565b925050819055508460105f82825461333a9190613d25565b9091555050604080515f8152602081018390529081018690526001600160a01b038716907fe837e30d8e7c5a0036b281f23284d0aecdb5550b7067960d27e4c3f81064cb9c9060600160405180910390a26124a5308261294b565b6001600160a01b0382165f9081525f80516020613ed38339815191526020526040902054811115610dbe576001600160a01b0382165f9081525f80516020613ed383398151915260205260409081902054905163fe4a762b60e01b8152611a62918491849190600401613d38565b6001600160a01b03841661342c5760405163e602df0560e01b81525f6004820152602401611a62565b6001600160a01b03831661345557604051634a1406b160e11b81525f6004820152602401611a62565b6001600160a01b038085165f9081526001602090815260408083209387168352929052208290558015610e7e57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516134c791815260200190565b60405180910390a350505050565b6001600160a01b0382165f9081525f80516020613ef38339815191526020526040902054811115610dbe576001600160a01b0382165f9081525f80516020613ef38339815191526020526040908190205490516383000e7760e01b8152611a62918491849190600401613d38565b6001600160a01b03821661356c57604051634b637e8f60e11b81525f6004820152602401611a62565b610dbe825f836135c1565b5f82815260066020526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6001600160a01b0383166135eb578060025f8282546135e09190613cad565b909155506136489050565b6001600160a01b0383165f908152602081905260409020548181101561362a5783818360405163391434e360e21b8152600401611a6293929190613d38565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661366457600280548290039055613682565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516136c791815260200190565b60405180910390a3505050565b6136de82826118db565b610dbe5760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401611a62565b60055460ff16610f7b57604051638dfc202b60e01b815260040160405180910390fd5b5f805f8060205f8651602088015f8a5af192503d91505f51905082801561376f57508115613761578060011461376f565b5f866001600160a01b03163b115b9695505050505050565b5f8060205f8451602086015f885af180613798576040513d5f823e3d81fd5b50505f513d915081156137af5780600114156137bc565b6001600160a01b0384163b155b15610e7e57604051635274afe760e01b81526001600160a01b0385166004820152602401611a62565b634e487b715f52806020526024601cfd5b5f600282600381111561380b5761380b613e91565b6138159190613ea5565b60ff166001149050919050565b5f60208284031215613832575f80fd5b81356001600160e01b031981168114610b30575f80fd5b6001600160a01b038116811461211b575f80fd5b5f805f6060848603121561386f575f80fd5b83359250602084013561388181613849565b9150604084013561389181613849565b809150509250925092565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f602082840312156138e1575f80fd5b5035919050565b5f80604083850312156138f9575f80fd5b823561390481613849565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff8111828210171561394f5761394f613912565b604052919050565b5f67ffffffffffffffff82111561397057613970613912565b5060051b60200190565b5f82601f830112613989575f80fd5b8135602061399e61399983613957565b613926565b8083825260208201915060208460051b8701019350868411156139bf575f80fd5b602086015b848110156139e45780356139d781613849565b83529183019183016139c4565b509695505050505050565b5f82601f8301126139fe575f80fd5b81356020613a0e61399983613957565b8083825260208201915060208460051b870101935086841115613a2f575f80fd5b602086015b848110156139e45780358352918301918301613a34565b5f805f8060808587031215613a5e575f80fd5b843567ffffffffffffffff80821115613a75575f80fd5b613a818883890161397a565b95506020870135915080821115613a96575f80fd5b613aa2888389016139ef565b94506040870135915080821115613ab7575f80fd5b613ac3888389016139ef565b93506060870135915080821115613ad8575f80fd5b50613ae5878288016139ef565b91505092959194509250565b5f8060408385031215613b02575f80fd5b8235613b0d81613849565b9150602083013567ffffffffffffffff811115613b28575f80fd5b613b348582860161397a565b9150509250929050565b5f805f60608486031215613b50575f80fd5b8335613b5b81613849565b92506020840135613b6b81613849565b929592945050506040919091013590565b5f8060408385031215613b8d575f80fd5b823591506020830135613b9f81613849565b809150509250929050565b5f60208284031215613bba575f80fd5b8135610b3081613849565b5f8060408385031215613bd6575f80fd5b50508035926020909101359150565b801515811461211b575f80fd5b5f8060408385031215613c03575f80fd5b8235613c0e81613849565b91506020830135613b9f81613be5565b5f805f8060808587031215613c31575f80fd5b8435613c3c81613849565b966020860135965060408601359560600135945092505050565b5f8060408385031215613c67575f80fd5b8235613c7281613849565b91506020830135613b9f81613849565b5f60208284031215613c92575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610b0157610b01613c99565b600181811c90821680613cd457607f821691505b602082108103613cf257634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52603260045260245ffd5b60ff8181168382160190811115610b0157610b01613c99565b81810381811115610b0157610b01613c99565b6001600160a01b039390931683526020830191909152604082015260600190565b600181815b80851115613d9357815f1904821115613d7957613d79613c99565b80851615613d8657918102915b93841c9390800290613d5e565b509250929050565b5f82613da957506001610b01565b81613db557505f610b01565b8160018114613dcb5760028114613dd557613df1565b6001915050610b01565b60ff841115613de657613de6613c99565b50506001821b610b01565b5060208310610133831016604e8410600b8410161715613e14575081810a610b01565b613e1e8383613d59565b805f1904821115613e3157613e31613c99565b029392505050565b5f610b3060ff841683613d9b565b5f60208284031215613e57575f80fd5b8151610b3081613be5565b5f60208284031215613e72575f80fd5b8151610b3081613849565b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b5f60ff831680613ec357634e487b7160e01b5f52601260045260245ffd5b8060ff8416069150509291505056feec8156718a8372b1db44bb411437d0870f3e3790d4a08526d024ce1b0b668f6b13649b2456f1b42fef0f0040b3aaeabcd21a76a0f3f5defd4f583839455116e8df7de25b7f1fd6d0b5205f0e18f1f35bd7b8d84cce336588d184533ce43a6f7665d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a13da86008ba1c6922daee3e07db95305ef49ebced9f5467a0b8613fcc6b343e3dcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7a264697066735822122031b8de7e5358884c1aaf7f114113d010b1779e685e7286f831b4c3030735cfc464736f6c63430008190033

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

000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000006df7ff4b8c6743b3ded7bb3c8f21ce7f5b9d11f5000000000000000000000000ecfd3e9333dd3d06588ed368e4696480aba6fa63000000000000000000000000000000000000000000000000000000000000001943726564697420436f6f70204c656e64696e67205661756c740000000000000000000000000000000000000000000000000000000000000000000000000000066363555344430000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : asset_ (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [1] : name_ (string): Credit Coop Lending Vault
Arg [2] : symbol_ (string): ccUSDC
Arg [3] : liquidStrategy_ (address): 0x6Df7FF4b8C6743B3ded7Bb3C8f21cE7F5B9D11F5
Arg [4] : factory_ (address): 0xecfd3e9333Dd3d06588ed368e4696480aBa6FA63

-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [3] : 0000000000000000000000006df7ff4b8c6743b3ded7bb3c8f21ce7f5b9d11f5
Arg [4] : 000000000000000000000000ecfd3e9333dd3d06588ed368e4696480aba6fa63
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000019
Arg [6] : 43726564697420436f6f70204c656e64696e67205661756c7400000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [8] : 6363555344430000000000000000000000000000000000000000000000000000


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

OVERVIEW

Credit Coop's Card Financing Vault claims to provide institutional-grade infrastructure for financing crypto card issuers' settlement gaps, offering high-yield returns backed by card receivables with programmatic controls.

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