UniswapV2Facet

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/facets/UniswapV2Facet.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {MoreVaultsLib} from "../libraries/MoreVaultsLib.sol";
import {IUniswapV2Router02} from "@uniswap-v2/v2-periphery/interfaces/IUniswapV2Router02.sol";
import {IUniswapV2Factory} from "../interfaces/Uniswap/v2/IUniswapV2Factory.sol";
import {IUniswapV2Pair} from "../interfaces/Uniswap/v2/IUniswapV2Pair.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {AccessControlLib} from "../libraries/AccessControlLib.sol";
import {BaseFacetInitializer} from "./BaseFacetInitializer.sol";
import {IUniswapV2Facet} from "../interfaces/facets/IUniswapV2Facet.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ILiquidityGaugeV6} from "../interfaces/Curve/ILiquidityGaugeV6.sol";
import {IMultiRewards} from "../interfaces/Curve/IMultiRewards.sol";

contract UniswapV2Facet is BaseFacetInitializer, IUniswapV2Facet {
    using SafeERC20 for IERC20;
    using EnumerableSet for EnumerableSet.AddressSet;
    using EnumerableSet for EnumerableSet.Bytes32Set;
    using Math for uint256;

    bytes32 constant UNISWAP_V2_LP_TOKENS_ID =
        keccak256("UNISWAP_V2_LP_TOKENS_ID");

    function INITIALIZABLE_STORAGE_SLOT()
        internal
        pure
        override
        returns (bytes32)
    {
        return keccak256("MoreVaults.storage.initializable.UniswapV2Facet");
    }

    function initialize(bytes calldata data) external initializerFacet {
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        bytes32 facetSelector = abi.decode(data, (bytes32));
        ds.facetsForAccounting.push(facetSelector);
        ds.vaultExternalAssets[MoreVaultsLib.TokenType.HeldToken].add(
            UNISWAP_V2_LP_TOKENS_ID
        );
    }

    function facetName() public pure returns (string memory) {
        return "UniswapV2Facet";
    }

    function facetVersion() external pure returns (string memory) {
        return "1.0.0";
    }

    function onFacetRemoval(address facetAddress, bool isReplacing) external {
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        ds.supportedInterfaces[type(IUniswapV2Facet).interfaceId] = false;

        MoreVaultsLib.removeFromFacetsForAccounting(
            ds,
            facetAddress,
            isReplacing
        );
        if (!isReplacing) {
            ds.vaultExternalAssets[MoreVaultsLib.TokenType.HeldToken].remove(
                UNISWAP_V2_LP_TOKENS_ID
            );
        }
    }

    function accountingUniswapV2Facet()
        public
        view
        returns (uint256 sum, bool isPositive)
    {
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        EnumerableSet.AddressSet storage tokensHeld = ds.tokensHeld[
            UNISWAP_V2_LP_TOKENS_ID
        ];
        for (uint i = 0; i < tokensHeld.length(); ) {
            address lpToken = tokensHeld.at(i);
            // if the lp token is available asset, then it should be already accounted
            if (ds.isAssetAvailable[lpToken]) {
                unchecked {
                    ++i;
                }
                continue;
            }

            uint multiRewardsBalance = IMultiRewards(
                ds.stakingTokenToMultiRewards[lpToken]
            ).balanceOf(address(this));

            uint totalSupply = IERC20(lpToken).totalSupply();
            uint balance = IERC20(lpToken).balanceOf(address(this)) +
                multiRewardsBalance;

            // Get token addresses from the pair
            address token0 = IUniswapV2Pair(lpToken).token0();
            address token1 = IUniswapV2Pair(lpToken).token1();

            // Get current reserves and k
            (uint reserve0, uint reserve1, ) = IUniswapV2Pair(lpToken)
                .getReserves();

            uint d0 = IERC20Metadata(token0).decimals();
            uint d1 = IERC20Metadata(token1).decimals();

            // Get prices from oracle
            uint price0 = MoreVaultsLib.convertToUnderlying(
                token0,
                10 ** d0,
                Math.Rounding.Floor
            );
            uint price1 = MoreVaultsLib.convertToUnderlying(
                token1,
                10 ** d1,
                Math.Rounding.Floor
            );

            uint totalValues = 2 *
                Math.sqrt(
                    (((price0 * reserve0) / 10 ** d0) * price1 * reserve1) /
                        10 ** d1
                );
            sum += totalValues.mulDiv(balance, totalSupply);

            unchecked {
                ++i;
            }
        }
        isPositive = true;
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function addLiquidity(
        address router,
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        uint deadline
    ) external returns (uint amountA, uint amountB, uint liquidity) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(tokenA);
        MoreVaultsLib.validateAssetAvailable(tokenB);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();

        IERC20(tokenA).forceApprove(router, amountADesired);
        IERC20(tokenB).forceApprove(router, amountBDesired);

        address defaultUniswapFactory = IUniswapV2Router02(router).factory();
        address liquidityToken = IUniswapV2Factory(defaultUniswapFactory)
            .getPair(tokenA, tokenB);

        if (liquidityToken == address(0)) {
            liquidityToken = IUniswapV2Factory(defaultUniswapFactory)
                .createPair(tokenA, tokenB);
        }

        ds.tokensHeld[UNISWAP_V2_LP_TOKENS_ID].add(liquidityToken);

        return
            IUniswapV2Router02(router).addLiquidity(
                tokenA,
                tokenB,
                amountADesired,
                amountBDesired,
                amountAMin,
                amountBMin,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function addLiquidityETH(
        address router,
        address token,
        uint amountTokenDesired,
        uint amountETHDesired,
        uint amountTokenMin,
        uint amountETHMin,
        uint deadline
    ) external returns (uint amountToken, uint amountETH, uint liquidity) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(token);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);
        IERC20(token).forceApprove(router, amountTokenDesired);

        address defaultUniswapFactory = IUniswapV2Router02(router).factory();

        address liquidityToken = IUniswapV2Factory(defaultUniswapFactory)
            .getPair(token, ds.wrappedNative);
        if (liquidityToken == address(0)) {
            liquidityToken = IUniswapV2Factory(defaultUniswapFactory)
                .createPair(token, ds.wrappedNative);
        }
        ds.tokensHeld[UNISWAP_V2_LP_TOKENS_ID].add(liquidityToken);

        return
            IUniswapV2Router02(router).addLiquidityETH{value: amountETHDesired}(
                token,
                amountTokenDesired,
                amountTokenMin,
                amountETHMin,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function removeLiquidity(
        address router,
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        uint deadline
    ) external returns (uint amountA, uint amountB) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(tokenA);
        MoreVaultsLib.validateAssetAvailable(tokenB);
        (amountA, amountB) = _removeLiquidity(
            router,
            tokenA,
            tokenB,
            liquidity,
            amountAMin,
            amountBMin,
            deadline
        );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function removeLiquidityETH(
        address router,
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        uint deadline
    ) external returns (uint amountToken, uint amountETH) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(token);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);
        (amountToken, amountETH) = _removeLiquidityETH(
            router,
            token,
            liquidity,
            amountTokenMin,
            amountETHMin,
            deadline
        );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapExactTokensForTokens(
        address router,
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        uint deadline
    ) external returns (uint[] memory amounts) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[0]);
        MoreVaultsLib.validateAssetAvailable(path[path.length - 1]);

        IERC20(path[0]).forceApprove(router, amountIn);
        return
            IUniswapV2Router02(router).swapExactTokensForTokens(
                amountIn,
                amountOutMin,
                path,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapTokensForExactTokens(
        address router,
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        uint deadline
    ) external returns (uint[] memory amounts) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[0]);
        MoreVaultsLib.validateAssetAvailable(path[path.length - 1]);

        IERC20(path[0]).forceApprove(router, amountInMax);
        return
            IUniswapV2Router02(router).swapTokensForExactTokens(
                amountOut,
                amountInMax,
                path,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapExactETHForTokens(
        address router,
        uint256 amountIn,
        uint amountOutMin,
        address[] calldata path,
        uint deadline
    ) external returns (uint[] memory amounts) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[path.length - 1]);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);
        return
            IUniswapV2Router02(router).swapExactETHForTokens{value: amountIn}(
                amountOutMin,
                path,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapTokensForExactETH(
        address router,
        uint amountOut,
        uint amountInMax,
        address[] calldata path,
        uint deadline
    ) external returns (uint[] memory amounts) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[0]);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);

        IERC20(path[0]).forceApprove(router, amountInMax);
        return
            IUniswapV2Router02(router).swapTokensForExactETH(
                amountOut,
                amountInMax,
                path,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapExactTokensForETH(
        address router,
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        uint deadline
    ) external returns (uint[] memory amounts) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[0]);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);
        IERC20(path[0]).forceApprove(router, amountIn);
        return
            IUniswapV2Router02(router).swapExactTokensForETH(
                amountIn,
                amountOutMin,
                path,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapETHForExactTokens(
        address router,
        uint256 amountInMax,
        uint amountOut,
        address[] calldata path,
        uint deadline
    ) external returns (uint[] memory amounts) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[path.length - 1]);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);
        return
            IUniswapV2Router02(router).swapETHForExactTokens{
                value: amountInMax
            }(amountOut, path, address(this), deadline);
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address router,
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        uint deadline
    ) external returns (uint amountETH) {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(token);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);
        address defaultUniswapFactory = IUniswapV2Router02(router).factory();
        address liquidityToken = IUniswapV2Factory(defaultUniswapFactory)
            .getPair(token, ds.wrappedNative);

        IERC20(liquidityToken).forceApprove(router, liquidity);
        amountETH = IUniswapV2Router02(router)
            .removeLiquidityETHSupportingFeeOnTransferTokens(
                token,
                liquidity,
                amountTokenMin,
                amountETHMin,
                address(this),
                deadline
            );

        MoreVaultsLib.removeTokenIfnecessary(
            ds.tokensHeld[UNISWAP_V2_LP_TOKENS_ID],
            liquidityToken
        );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        address router,
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        uint deadline
    ) external {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[0]);
        MoreVaultsLib.validateAssetAvailable(path[path.length - 1]);

        IERC20(path[0]).forceApprove(router, amountIn);
        IUniswapV2Router02(router)
            .swapExactTokensForTokensSupportingFeeOnTransferTokens(
                amountIn,
                amountOutMin,
                path,
                address(this),
                deadline
            );
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        address router,
        uint256 amountIn,
        uint amountOutMin,
        address[] calldata path,
        uint deadline
    ) external {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[path.length - 1]);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);

        IUniswapV2Router02(router)
            .swapExactETHForTokensSupportingFeeOnTransferTokens{
            value: amountIn
        }(amountOutMin, path, address(this), deadline);
    }

    /**
     * @inheritdoc IUniswapV2Facet
     */
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        address router,
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        uint deadline
    ) external {
        AccessControlLib.validateDiamond(msg.sender);
        MoreVaultsLib.validateAddressWhitelisted(router);
        MoreVaultsLib.validateAssetAvailable(path[0]);
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        MoreVaultsLib.validateAssetAvailable(ds.wrappedNative);

        IERC20(path[0]).forceApprove(router, amountIn);
        IUniswapV2Router02(router)
            .swapExactTokensForETHSupportingFeeOnTransferTokens(
                amountIn,
                amountOutMin,
                path,
                address(this),
                deadline
            );
    }

    function _removeLiquidityETH(
        address router,
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        uint deadline
    ) internal returns (uint amountToken, uint amountETH) {
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        address defaultUniswapFactory = IUniswapV2Router02(router).factory();
        address liquidityToken = IUniswapV2Factory(defaultUniswapFactory)
            .getPair(token, ds.wrappedNative);

        IERC20(liquidityToken).forceApprove(router, liquidity);
        (amountToken, amountETH) = IUniswapV2Router02(router)
            .removeLiquidityETH(
                token,
                liquidity,
                amountTokenMin,
                amountETHMin,
                address(this),
                deadline
            );

        MoreVaultsLib.removeTokenIfnecessary(
            ds.tokensHeld[UNISWAP_V2_LP_TOKENS_ID],
            liquidityToken
        );
    }

    function _removeLiquidity(
        address router,
        address tokenA,
        address tokenB,
        uint liquidity,
        uint amountAMin,
        uint amountBMin,
        uint deadline
    ) internal returns (uint amountA, uint amountB) {
        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        address defaultUniswapFactory = IUniswapV2Router02(router).factory();
        address liquidityToken = IUniswapV2Factory(defaultUniswapFactory)
            .getPair(tokenA, tokenB);

        IERC20(liquidityToken).forceApprove(router, liquidity);
        (amountA, amountB) = IUniswapV2Router02(router).removeLiquidity(
            tokenA,
            tokenB,
            liquidity,
            amountAMin,
            amountBMin,
            address(this),
            deadline
        );

        MoreVaultsLib.removeTokenIfnecessary(
            ds.tokensHeld[UNISWAP_V2_LP_TOKENS_ID],
            liquidityToken
        );
    }
}
"
    },
    "src/libraries/MoreVaultsLib.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {AccessControlLib} from "./AccessControlLib.sol";
import {IDiamondCut} from "../interfaces/facets/IDiamondCut.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IOracleRegistry} from "../interfaces/IOracleRegistry.sol";
import {IMoreVaultsRegistry} from "../interfaces/IMoreVaultsRegistry.sol";
import {IVaultsFactory} from "../interfaces/IVaultsFactory.sol";
import {IAggregatorV2V3Interface} from "../interfaces/Chainlink/IAggregatorV2V3Interface.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IGenericMoreVaultFacet, IGenericMoreVaultFacetInitializable} from "../interfaces/facets/IGenericMoreVaultFacetInitializable.sol";

bytes32 constant BEFORE_ACCOUNTING_SELECTOR = 0xa85367f800000000000000000000000000000000000000000000000000000000;
bytes32 constant BEFORE_ACCOUNTING_FAILED_ERROR = 0xc5361f8d00000000000000000000000000000000000000000000000000000000;
bytes32 constant ACCOUNTING_FAILED_ERROR = 0x712f778400000000000000000000000000000000000000000000000000000000;
bytes32 constant BALANCE_OF_SELECTOR = 0x70a0823100000000000000000000000000000000000000000000000000000000;
bytes32 constant TOTAL_ASSETS_SELECTOR = 0x01e1d11400000000000000000000000000000000000000000000000000000000;
bytes32 constant TOTAL_ASSETS_RUN_FAILED = 0xb5a7047700000000000000000000000000000000000000000000000000000000;

uint256 constant MAX_WITHDRAWAL_DELAY = 14 days;

library MoreVaultsLib {
    error InitializationFunctionReverted(
        address _initializationContractAddress,
        bytes _calldata
    );
    error UnsupportedAsset(address);
    error FacetNotAllowed(address facet);
    error SelectorNotAllowed(bytes4 selector);
    error InvalidSelectorForFacet(bytes4 selector, address facet);
    error IncorrectFacetCutAction(uint8 action);
    error ContractDoesntHaveCode(string errorMessage);
    error ZeroAddress();
    error ImmutableFunction();
    error FunctionDoesNotExist();
    error NoSelectorsInFacetToCut();
    error FunctionAlreadyExists(address oldFacetAddress, bytes4 selector);
    error OraclePriceIsOld();
    error OraclePriceIsNegative();
    error InvalidFee();
    error AssetAlreadyAvailable();
    error InvalidAddress();
    error NoOracleForAsset();
    error FacetHasBalance(address facet);
    error AccountingFailed(bytes32 selector);
    error UnsupportedProtocol(address protocol);
    error AccountingGasLimitExceeded(uint256 limit, uint256 consumption);
    error RestrictedActionInsideMulticall();
    error OnFacetRemovalFailed(address facet, bytes data);
    error FacetNameFailed(address facet);

    using EnumerableSet for EnumerableSet.AddressSet;
    using EnumerableSet for EnumerableSet.Bytes32Set;
    using Math for uint256;

    // 32 bytes keccak hash of a string to use as a diamond storage location.
    bytes32 constant MORE_VAULTS_STORAGE_POSITION =
        keccak256("MoreVaults.diamond.storage");

    uint96 constant FEE_BASIS_POINT = 10000; // 100%

    uint96 constant MAX_FEE = 5000; // 50%

    struct ERC4626Storage {
        IERC20 _asset;
        uint8 _underlyingDecimals;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC4626")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 constant ERC4626StorageLocation =
        0x0773e532dfede91f04b12a73d3d2acd361424f41f76b4fb79f090161e36b4e00;

    struct FacetAddressAndPosition {
        address facetAddress;
        uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
    }

    struct FacetFunctionSelectors {
        bytes4[] functionSelectors;
        uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
    }

    struct PendingActions {
        bytes[] actionsData;
        uint256 pendingUntil;
    }

    struct GasLimit {
        uint48 availableTokenAccountingGas;
        uint48 heldTokenAccountingGas;
        uint48 facetAccountingGas;
        uint48 stakingTokenAccountingGas;
        uint48 nestedVaultsGas;
        uint48 value;
    }

    struct WithdrawRequest {
        uint256 timelockEndsAt;
        uint256 shares;
    }

    enum TokenType {
        HeldToken,
        StakingToken
    }

    struct MoreVaultsStorage {
        // maps function selector to the facet address and
        // the position of the selector in the facetFunctionSelectors.selectors array
        mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
        // maps facet addresses to function selectors
        mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
        // facet addresses
        address[] facetAddresses;
        bytes32[] facetsForAccounting;
        // Used to query if a contract implements an interface.
        // Used to implement ERC-165.
        mapping(bytes4 => bool) supportedInterfaces;
        mapping(address => bool) isAssetAvailable;
        address[] availableAssets;
        mapping(address => bool) isAssetDepositable;
        mapping(bytes32 => EnumerableSet.AddressSet) tokensHeld;
        address wrappedNative;
        address feeRecipient;
        uint96 fee;
        uint256 depositCapacity;
        uint256 lastTotalAssets;
        uint256 actionNonce;
        mapping(uint256 => PendingActions) pendingActions;
        uint256 timeLockPeriod;
        mapping(bytes32 => EnumerableSet.AddressSet) stakingAddresses;
        mapping(address => uint256) lockedTokens;
        address minter;
        bool isNativeDeposit;
        address[] beforeAccountingFacets;
        mapping(address => address) stakingTokenToGauge;
        mapping(address => address) stakingTokenToMultiRewards;
        GasLimit gasLimit;
        mapping(TokenType => EnumerableSet.Bytes32Set) vaultExternalAssets;
        uint64 timelockDuration;
        mapping(address => WithdrawRequest) withdrawalRequests;
        uint256 maxSlippagePercent;
        bool isMulticall;
        address factory;
        mapping(address => uint256) curvePoolLength;
        mapping(address => uint256) depositWhitelist;
        mapping(address => bool) isNecessaryToCheckLock;
        bool isWhitelistEnabled;
        address[] depositableAssets;
    }

    event DiamondCut(IDiamondCut.FacetCut[] _diamondCut);
    event FeeSet(uint96 previousFee, uint96 newFee);
    event FeeRecipientSet(
        address indexed previousRecipient,
        address indexed newRecipient
    );
    event AssetToManageAdded(address indexed asset);
    event AssetToDepositEnabled(address indexed asset);
    event AssetToDepositDisabled(address indexed asset);
    event TimeLockPeriodSet(uint256 previousPeriod, uint256 newPeriod);
    event DepositCapacitySet(uint256 previousCapacity, uint256 newCapacity);

    function moreVaultsStorage()
        internal
        pure
        returns (MoreVaultsStorage storage ds)
    {
        bytes32 position = MORE_VAULTS_STORAGE_POSITION;
        // assigns struct storage slot to the storage position
        assembly {
            ds.slot := position
        }
    }

    function getERC4626Storage()
        internal
        pure
        returns (ERC4626Storage storage $)
    {
        assembly {
            $.slot := ERC4626StorageLocation
        }
    }

    function validateAddressWhitelisted(address protocol) internal view {
        AccessControlLib.AccessControlStorage storage acs = AccessControlLib
            .accessControlStorage();
        if (
            !IMoreVaultsRegistry(acs.moreVaultsRegistry).isWhitelisted(protocol)
        ) revert UnsupportedProtocol(protocol);
    }

    function validateAssetAvailable(address asset) internal view {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        if (asset == address(0)) asset = ds.wrappedNative;
        if (!ds.isAssetAvailable[asset]) revert UnsupportedAsset(asset);
    }

    function validateAssetDepositable(address asset) internal view {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        if (asset == address(0)) asset = ds.wrappedNative;
        if (!ds.isAssetDepositable[asset]) revert UnsupportedAsset(asset);
    }

    function validateMulticall() internal view {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        if (ds.isMulticall) {
            revert RestrictedActionInsideMulticall();
        }
    }

    function removeTokenIfnecessary(
        EnumerableSet.AddressSet storage tokensHeld,
        address token
    ) internal {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        if (
            IERC20(token).balanceOf(address(this)) + ds.lockedTokens[token] <
            10e3
        ) {
            tokensHeld.remove(token);
        }
    }

    function convertToUnderlying(
        address _token,
        uint amount,
        Math.Rounding rounding
    ) internal view returns (uint) {
        if (amount == 0) return 0;
        MoreVaultsStorage storage ds = moreVaultsStorage();

        if (_token == address(0)) {
            _token = address(ds.wrappedNative);
        }
        address underlyingToken = address(getERC4626Storage()._asset);
        if (_token == underlyingToken) {
            return amount;
        }

        IMoreVaultsRegistry registry = IMoreVaultsRegistry(
            AccessControlLib.vaultRegistry()
        );
        IOracleRegistry oracle = registry.oracle();
        address oracleDenominationAsset = registry.getDenominationAsset();
        IAggregatorV2V3Interface aggregator = IAggregatorV2V3Interface(
            oracle.getOracleInfo(_token).aggregator
        );
        uint256 inputTokenPrice = oracle.getAssetPrice(_token);
        uint8 inputTokenOracleDecimals = aggregator.decimals();

        uint256 finalPriceForConversion = inputTokenPrice;
        if (underlyingToken != oracleDenominationAsset) {
            aggregator = IAggregatorV2V3Interface(
                oracle.getOracleInfo(underlyingToken).aggregator
            );
            uint256 underlyingTokenPrice = oracle.getAssetPrice(
                underlyingToken
            );
            uint8 underlyingTokenOracleDecimals = aggregator.decimals();
            uint256 inputToUnderlyingPrice = inputTokenPrice.mulDiv(
                10 ** underlyingTokenOracleDecimals,
                underlyingTokenPrice,
                rounding
            );
            finalPriceForConversion = inputToUnderlyingPrice;
        }

        uint256 convertedAmount = amount.mulDiv(
            finalPriceForConversion *
                10 ** IERC20Metadata(underlyingToken).decimals(),
            10 **
                (inputTokenOracleDecimals + IERC20Metadata(_token).decimals()),
            rounding
        );

        return convertedAmount;
    }

    function _setFeeRecipient(address recipient) internal {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        if (recipient == address(0)) {
            revert ZeroAddress();
        }
        address previousRecipient = ds.feeRecipient;
        ds.feeRecipient = recipient;
        emit FeeRecipientSet(previousRecipient, recipient);
    }

    function _setFee(uint96 fee) internal {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        uint96 previousFee = ds.fee;
        if (fee > MAX_FEE) {
            revert InvalidFee();
        }
        ds.fee = fee;

        emit FeeSet(previousFee, fee);
    }

    function _setDepositCapacity(uint256 capacity) internal {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        uint256 previousCapacity = ds.depositCapacity;
        ds.depositCapacity = capacity;

        emit DepositCapacitySet(previousCapacity, capacity);
    }

    function _setDepositWhitelist(
        address[] calldata depositors,
        uint256[] calldata undelyingAssetCaps
    ) internal {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        for (uint256 i; i < depositors.length; ) {
            ds.depositWhitelist[depositors[i]] = undelyingAssetCaps[i];
            unchecked {
                ++i;
            }
        }
    }

    function _setTimeLockPeriod(uint256 period) internal {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        uint256 previousPeriod = ds.timeLockPeriod;
        ds.timeLockPeriod = period;

        emit TimeLockPeriodSet(previousPeriod, period);
    }

    function _addAvailableAsset(address asset) internal {
        if (asset == address(0)) {
            revert InvalidAddress();
        }

        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        if (ds.isAssetAvailable[asset]) {
            revert AssetAlreadyAvailable();
        }

        AccessControlLib.AccessControlStorage storage acs = AccessControlLib
            .accessControlStorage();
        IMoreVaultsRegistry registry = IMoreVaultsRegistry(
            acs.moreVaultsRegistry
        );
        IOracleRegistry oracle = registry.oracle();
        if (address(oracle.getOracleInfo(asset).aggregator) == address(0)) {
            revert NoOracleForAsset();
        }

        ds.isAssetAvailable[asset] = true;
        ds.availableAssets.push(asset);

        emit AssetToManageAdded(asset);
    }

    function _enableAssetToDeposit(address asset) internal {
        if (asset == address(0)) {
            revert InvalidAddress();
        }

        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        if (!ds.isAssetAvailable[asset]) {
            revert UnsupportedAsset(asset);
        }
        if (ds.isAssetDepositable[asset]) {
            revert AssetAlreadyAvailable();
        }
        ds.isAssetDepositable[asset] = true;
        ds.depositableAssets.push(asset);

        emit AssetToDepositEnabled(asset);
    }

    function _disableAssetToDeposit(address asset) internal {
        if (asset == address(0)) {
            revert InvalidAddress();
        }

        MoreVaultsLib.MoreVaultsStorage storage ds = MoreVaultsLib
            .moreVaultsStorage();
        if (!ds.isAssetDepositable[asset]) {
            revert UnsupportedAsset(asset);
        }

        ds.isAssetDepositable[asset] = false;
        for (uint256 i; i < ds.depositableAssets.length; ) {
            if (ds.depositableAssets[i] == asset) {
                ds.depositableAssets[i] = ds.depositableAssets[
                    ds.depositableAssets.length - 1
                ];
                ds.depositableAssets.pop();
                break;
            }
            unchecked {
                ++i;
            }
        }

        emit AssetToDepositDisabled(asset);
    }

    // Internal function version of diamondCut
    function diamondCut(IDiamondCut.FacetCut[] memory _diamondCut) internal {
        AccessControlLib.AccessControlStorage storage acs = AccessControlLib
            .accessControlStorage();
        IMoreVaultsRegistry registry = IMoreVaultsRegistry(
            acs.moreVaultsRegistry
        );

        for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
            IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
            address facetAddress = _diamondCut[facetIndex].facetAddress;

            // Validate facet and selectors for Add and Replace actions
            if (
                action == IDiamondCut.FacetCutAction.Add ||
                action == IDiamondCut.FacetCutAction.Replace
            ) {
                // Check if facet is allowed in registry
                if (!registry.isPermissionless()) {
                    if (!registry.isFacetAllowed(facetAddress)) {
                        revert FacetNotAllowed(facetAddress);
                    }

                    for (
                        uint256 selectorIndex;
                        selectorIndex <
                        _diamondCut[facetIndex].functionSelectors.length;

                    ) {
                        if (
                            registry.selectorToFacet(
                                _diamondCut[facetIndex].functionSelectors[
                                    selectorIndex
                                ]
                            ) != facetAddress
                        ) {
                            revert SelectorNotAllowed(
                                _diamondCut[facetIndex].functionSelectors[
                                    selectorIndex
                                ]
                            );
                        }
                        unchecked {
                            ++selectorIndex;
                        }
                    }
                }
            }

            if (action == IDiamondCut.FacetCutAction.Add) {
                addFunctions(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].functionSelectors
                );
                initializeAfterAddition(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].initData
                );
            } else if (action == IDiamondCut.FacetCutAction.Replace) {
                replaceFunctions(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].functionSelectors
                );
                initializeAfterAddition(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].initData
                );
            } else if (action == IDiamondCut.FacetCutAction.Remove) {
                removeFunctions(
                    _diamondCut[facetIndex].facetAddress,
                    _diamondCut[facetIndex].functionSelectors
                );
            } else {
                revert IncorrectFacetCutAction(uint8(action));
            }
            unchecked {
                ++facetIndex;
            }
        }
        emit DiamondCut(_diamondCut);
    }

    function addFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        if (_functionSelectors.length == 0) {
            revert NoSelectorsInFacetToCut();
        }
        MoreVaultsStorage storage ds = moreVaultsStorage();
        if (_facetAddress == address(0)) {
            revert ZeroAddress();
        }
        uint96 selectorPosition = uint96(
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.length
        );
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;

        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            if (oldFacetAddress != address(0)) {
                revert FunctionAlreadyExists(oldFacetAddress, selector);
            }
            addFunction(ds, selector, selectorPosition, _facetAddress);
            selectorPosition++;
            unchecked {
                ++selectorIndex;
            }
        }
        if (msg.sender != factoryAddress()) {
            IVaultsFactory(ds.factory).link(_facetAddress);
        }
    }

    function replaceFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        if (_functionSelectors.length == 0) {
            revert NoSelectorsInFacetToCut();
        }
        MoreVaultsStorage storage ds = moreVaultsStorage();
        if (_facetAddress == address(0)) {
            revert ZeroAddress();
        }
        uint96 selectorPosition = uint96(
            ds.facetFunctionSelectors[_facetAddress].functionSelectors.length
        );
        // add new facet address if it does not exist
        if (selectorPosition == 0) {
            addFacet(ds, _facetAddress);
        }

        address factory = ds.factory;
        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;

        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            if (oldFacetAddress == _facetAddress) {
                revert FunctionAlreadyExists(oldFacetAddress, selector);
            }
            removeFunction(ds, oldFacetAddress, selector, true);
            addFunction(ds, selector, selectorPosition, _facetAddress);
            selectorPosition++;
            unchecked {
                ++selectorIndex;
            }
        }
        IVaultsFactory(factory).link(_facetAddress);
    }

    function removeFunctions(
        address _facetAddress,
        bytes4[] memory _functionSelectors
    ) internal {
        if (_functionSelectors.length == 0) {
            revert NoSelectorsInFacetToCut();
        }
        MoreVaultsStorage storage ds = moreVaultsStorage();
        // if function does not exist then do nothing and return
        if (_facetAddress != address(0)) {
            revert ZeroAddress();
        }

        for (
            uint256 selectorIndex;
            selectorIndex < _functionSelectors.length;

        ) {
            bytes4 selector = _functionSelectors[selectorIndex];
            address oldFacetAddress = ds
                .selectorToFacetAndPosition[selector]
                .facetAddress;
            removeFunction(ds, oldFacetAddress, selector, false);
            unchecked {
                ++selectorIndex;
            }
        }
    }

    function addFacet(
        MoreVaultsStorage storage ds,
        address _facetAddress
    ) internal {
        enforceHasContractCode(
            _facetAddress,
            "MoreVaultsLibCut: New facet has no code"
        );
        ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds
            .facetAddresses
            .length;
        ds.facetAddresses.push(_facetAddress);
    }

    function addFunction(
        MoreVaultsStorage storage ds,
        bytes4 _selector,
        uint96 _selectorPosition,
        address _facetAddress
    ) internal {
        ds
            .selectorToFacetAndPosition[_selector]
            .functionSelectorPosition = _selectorPosition;
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(
            _selector
        );
        ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
    }

    function removeFunction(
        MoreVaultsStorage storage ds,
        address _facetAddress,
        bytes4 _selector,
        bool _isReplacing
    ) internal {
        if (_facetAddress == address(0)) {
            revert FunctionDoesNotExist();
        }
        // an immutable function is a function defined directly in a diamond
        if (_facetAddress == address(this)) {
            revert ImmutableFunction();
        }
        // replace selector with last selector, then delete last selector
        uint256 selectorPosition = ds
            .selectorToFacetAndPosition[_selector]
            .functionSelectorPosition;
        uint256 lastSelectorPosition = ds
            .facetFunctionSelectors[_facetAddress]
            .functionSelectors
            .length - 1;
        // if not the same then replace _selector with lastSelector
        if (selectorPosition != lastSelectorPosition) {
            bytes4 lastSelector = ds
                .facetFunctionSelectors[_facetAddress]
                .functionSelectors[lastSelectorPosition];
            ds.facetFunctionSelectors[_facetAddress].functionSelectors[
                selectorPosition
            ] = lastSelector;
            ds
                .selectorToFacetAndPosition[lastSelector]
                .functionSelectorPosition = uint96(selectorPosition);
        }

        // if no more selectors for facet address then delete the facet address
        if (lastSelectorPosition == 0) {
            // replace facet address with last facet address and delete last facet address
            uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
            uint256 facetAddressPosition = ds
                .facetFunctionSelectors[_facetAddress]
                .facetAddressPosition;
            if (facetAddressPosition != lastFacetAddressPosition) {
                address lastFacetAddress = ds.facetAddresses[
                    lastFacetAddressPosition
                ];
                ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
                ds
                    .facetFunctionSelectors[lastFacetAddress]
                    .facetAddressPosition = facetAddressPosition;
            }
            ds.facetAddresses.pop();
            delete ds
                .facetFunctionSelectors[_facetAddress]
                .facetAddressPosition;
            address factory = ds.factory;
            IVaultsFactory(factory).unlink(_facetAddress);

            (bool success, bytes memory result) = address(_facetAddress)
                .delegatecall(
                    abi.encodeWithSelector(
                        bytes4(
                            IGenericMoreVaultFacetInitializable
                                .onFacetRemoval
                                .selector
                        ),
                        _facetAddress,
                        _isReplacing
                    )
                );
            // revert if onFacetRemoval exists on facet and failed
            // TODO: remove second condition after all facets will be upgraded
            if (!success && result.length > 0) {
                revert OnFacetRemovalFailed(_facetAddress, result);
            }
        }

        // delete the last selector
        ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
        delete ds.selectorToFacetAndPosition[_selector];
    }

    function initializeAfterAddition(
        address _facetAddress,
        bytes memory _initData
    ) internal {
        enforceHasContractCode(
            _facetAddress,
            "MoreVaultsLibCut: _facetAddress has no code"
        );

        bytes memory callData = abi.encodeWithSelector(
            IGenericMoreVaultFacetInitializable.initialize.selector,
            _initData
        );
        (bool success, bytes memory error) = _facetAddress.delegatecall(
            callData
        );
        // 0x0dc149f0 is selector of error AlreadyInitialized()
        if (bytes4(error) == bytes4(hex"0dc149f0")) {
            return;
        }
        if (!success) {
            if (error.length > 0) {
                // bubble up error
                /// @solidity memory-safe-assembly
                assembly {
                    let returndata_size := mload(error)
                    revert(add(32, error), returndata_size)
                }
            } else {
                revert InitializationFunctionReverted(_facetAddress, callData);
            }
        }
    }

    function removeFromBeforeAccounting(
        MoreVaultsStorage storage ds,
        address _facetAddress,
        bool _isReplacing
    ) internal {
        for (uint256 i; i < ds.beforeAccountingFacets.length; ) {
            if (ds.beforeAccountingFacets[i] == _facetAddress) {
                if (!_isReplacing) {
                    (bool success, ) = address(_facetAddress).delegatecall(
                        abi.encodeWithSelector(
                            bytes4(BEFORE_ACCOUNTING_SELECTOR)
                        )
                    );
                    assembly {
                        if iszero(success) {
                            mstore(0x40, BEFORE_ACCOUNTING_FAILED_ERROR)
                            mstore(add(0x40, 0x04), _facetAddress)
                            revert(0x40, 0x24)
                        }
                    }
                }
                ds.beforeAccountingFacets[i] = ds.beforeAccountingFacets[
                    ds.beforeAccountingFacets.length - 1
                ];
                ds.beforeAccountingFacets.pop();
                break;
            }
            unchecked {
                ++i;
            }
        }
    }

    function removeFromFacetsForAccounting(
        MoreVaultsStorage storage ds,
        address _facetAddress,
        bool _isReplacing
    ) internal {
        (bool success, bytes memory result) = address(_facetAddress).staticcall(
            abi.encodeWithSelector(IGenericMoreVaultFacet.facetName.selector)
        );
        if (!success) {
            revert FacetNameFailed(_facetAddress);
        }
        string memory facetName = abi.decode(result, (string));

        bytes4 selector = bytes4(
            keccak256(abi.encodePacked("accounting", facetName, "()"))
        );
        for (uint256 i; i < ds.facetsForAccounting.length; ) {
            if (ds.facetsForAccounting[i] == selector) {
                if (!_isReplacing) {
                    (success, result) = address(this).staticcall(
                        abi.encodeWithSelector(selector)
                    );
                    if (!success) {
                        revert AccountingFailed(selector);
                    }
                    uint256 decodedAmount = abi.decode(result, (uint256));
                    if (decodedAmount > 10e4) {
                        revert FacetHasBalance(_facetAddress);
                    }
                }
                ds.facetsForAccounting[i] = ds.facetsForAccounting[
                    ds.facetsForAccounting.length - 1
                ];
                ds.facetsForAccounting.pop();
                break;
            }
            unchecked {
                ++i;
            }
        }
    }

    function enforceHasContractCode(
        address _contract,
        string memory _errorMessage
    ) internal view {
        uint256 contractSize;
        assembly {
            contractSize := extcodesize(_contract)
        }
        if (contractSize == 0) {
            revert ContractDoesntHaveCode(_errorMessage);
        }
    }

    function checkGasLimitOverflow() internal view {
        MoreVaultsStorage storage ds = moreVaultsStorage();

        GasLimit storage gl = ds.gasLimit;

        if (gl.value == 0) return;

        bytes32[] memory stakingIds = ds
            .vaultExternalAssets[TokenType.StakingToken]
            .values();
        bytes32[] memory heldIds = ds
            .vaultExternalAssets[TokenType.HeldToken]
            .values();

        uint256 stakingTokensLength;
        for (uint256 i = 0; i < stakingIds.length; ) {
            unchecked {
                stakingTokensLength += ds
                    .stakingAddresses[stakingIds[i]]
                    .length();
                ++i;
            }
        }

        uint256 tokensHeldLength;
        for (uint256 i = 0; i < heldIds.length; ) {
            unchecked {
                tokensHeldLength += ds.tokensHeld[heldIds[i]].length();
                ++i;
            }
        }

        uint256 consumption;
        unchecked {
            consumption =
                tokensHeldLength *
                gl.heldTokenAccountingGas +
                stakingTokensLength *
                gl.stakingTokenAccountingGas +
                ds.availableAssets.length *
                gl.availableTokenAccountingGas +
                ds.facetsForAccounting.length *
                gl.facetAccountingGas +
                gl.nestedVaultsGas;
        }

        if (consumption > ds.gasLimit.value) {
            revert AccountingGasLimitExceeded(ds.gasLimit.value, consumption);
        }
    }

    function withdrawFromRequest(
        address _requester,
        uint256 _shares
    ) internal returns (bool) {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        WithdrawRequest storage request = ds.withdrawalRequests[_requester];
        if (
            isWithdrawableRequest(
                request.timelockEndsAt,
                ds.timelockDuration
            ) && request.shares >= _shares
        ) {
            request.shares -= _shares;
            return true;
        }

        return false;
    }

    function isWithdrawableRequest(
        uint256 _timelockEndsAt,
        uint256 _timelockDuration
    ) private view returns (bool) {
        uint256 requestTimestamp = _timelockEndsAt - _timelockDuration;
        return
            block.timestamp >= _timelockEndsAt ||
            block.timestamp - requestTimestamp > MAX_WITHDRAWAL_DELAY;
    }

    function factoryAddress() internal view returns (address) {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        return ds.factory;
    }

    function _setWhitelistFlag(bool isEnabled) internal {
        MoreVaultsStorage storage ds = moreVaultsStorage();
        ds.isWhitelistEnabled = isEnabled;
    }
}
"
    },
    "lib/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol": {
      "content": "pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}
"
    },
    "src/interfaces/Uniswap/v2/IUniswapV2Factory.sol": {
      "content": "// // SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IUniswapV2Factory {
    event PairCreated(
        address indexed token0,
        address indexed token1,
        address pair,
        uint
    );

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(
        address tokenA,
        address tokenB
    ) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(
        address tokenA,
        address tokenB
    ) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}
"
    },
    "src/interfaces/Uniswap/v2/IUniswapV2Pair.sol": {
      "content": "// // SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(
        address owner,
        address spender
    ) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(
        address from,
        address to,
        uint value
    ) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(
        address owner,
        address spender,
        uint value,
        uint deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(
        address indexed sender,
        uint amount0,
        uint amount1,
        address indexed to
    );
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves()
        external
        view
        returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(
        uint amount0Out,
        uint amount1Out,
        address to,
        bytes calldata data
    ) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}
"
    },
    "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol": {
      "content": "// 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";
"
    },
    "lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
"
    },
    "lib/openzeppelin-contracts/contracts/utils/structs/EnumerableSet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**\

Tags:
ERC20, ERC165, Multisig, Mintable, Burnable, Swap, Liquidity, Staking, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0xd53e2d54572709b685072d14140311c15cbeadf1|verified:true|block:23382195|tx:0xa778140ba1325b433dd203ad0d32aea319e9145633929b6da49aa23975585a98|first_check:1758108403

Submitted on: 2025-09-17 13:26:44

Comments

Log in to comment.

No comments yet.