creation_sourcecode: {{
    "language": "Solidity",
    "sources": {
      "contracts/Adapter.sol": {
        "content": "// SPDX-License-Identifier: -- WISE --\n\npragma solidity =0.8.21;\n\n/**\n * @author René Hochmuth\n * @author Vitally Marinchenko\n */\n\nimport \"./AdapterHelper.sol\";\n\ncontract Adapter is AdapterHelper {\n\n    constructor(\n        address _tokenProfitAddress,\n        address _uniV2RouterAddress,\n        address _liquidNFTsRouterAddress,\n        address _liquidNFTsWETHPool,\n        address _liquidNFTsUSDCPool,\n        address _wiseLendingAddress,\n        bool _isPaused\n    )\n        AdapterDeclarations(\n            _tokenProfitAddress,\n            _uniV2RouterAddress,\n            _liquidNFTsRouterAddress,\n            _liquidNFTsWETHPool,\n            _liquidNFTsUSDCPool,\n            _wiseLendingAddress,\n            _isPaused\n        )\n    {\n        admin = tx.origin;\n        multisig = tx.origin;\n    }\n\n    /**\n    * @dev Overview for services and ability to syncronize them\n    */\n    function syncServices()\n        external\n    {\n        liquidNFTsUSDCPool.manualSyncPool();\n        liquidNFTsWETHPool.manualSyncPool();\n    }\n\n    /**\n    * @dev Allows admin to swap USDC for ETH using UniswapV2\n    */\n    function swapUSDCForETH(\n        uint256 _amount,\n        uint256 _minAmountOut\n    )\n        external\n        onlyMultiSig\n        returns (\n            uint256 amountIn,\n            uint256 amountOut\n        )\n    {\n        address[] memory path = new address[](2);\n\n        path[0] = USDC_ADDRESS;\n        path[1] = WETH_ADDRESS;\n\n        uint256[] memory amounts = _executeSwap(\n            path,\n            _amount,\n            _minAmountOut\n        );\n\n        emit AdminSwap(\n            USDC_ADDRESS,\n            WETH_ADDRESS,\n            amounts[0],\n            amounts[1]\n        );\n\n        return (\n            amounts[0],\n            amounts[1]\n        );\n    }\n\n    function _precisionWithFee()\n        internal\n        view\n        returns (uint256)\n    {\n        return FEE_PRECISION + buyFee;\n    }\n\n    /**\n     * @dev Calculates tokens to mint given exact ETH amount\n     * input for the TokenProfit contract during mint\n     */\n    function getTokenAmountFromEthAmount(\n        uint256 _ethAmount\n    )\n        external\n        view\n        returns (uint256)\n    {\n        return tokenProfit.totalSupply()\n            * _ethAmount\n            * FEE_PRECISION\n            / _precisionWithFee()\n            / _calculateTotalEthValue(\n                0\n            );\n    }\n\n    /**\n     * @dev Calculates ETH amount necessary to pay to\n     * receive fixed token amount from the TokenProfit contract\n     */\n    function getEthAmountFromTokenAmount(\n        uint256 _tokenAmount,\n        uint256 _msgValue\n    )\n        external\n        view\n        returns (uint256)\n    {\n        return _calculateTotalEthValue(\n            _msgValue\n        )\n            * _tokenAmount\n            * PRECISION_FACTOR\n            * _precisionWithFee()\n            / FEE_PRECISION\n            / tokenProfit.totalSupply()\n            / PRECISION_FACTOR;\n    }\n\n    function proposeNewMultisig(\n        address _proposedMultisig\n    )\n        external\n        onlyMultiSig\n    {\n        address oldProposedMultisig = proposedMultisig;\n        proposedMultisig = _proposedMultisig;\n\n        emit MultisigUpdateProposed(\n            oldProposedMultisig,\n            _proposedMultisig\n        );\n    }\n\n    function claimMultisigOwnership()\n        external\n        onlyProposedMultisig\n    {\n        address oldMultisig = multisig;\n        multisig = proposedMultisig;\n        proposedMultisig = ZERO_ADDRESS;\n\n        emit MultisigUpdate(\n            oldMultisig,\n            multisig\n        );\n    }\n\n    /**\n     * @dev Starts the process to change the admin address\n     */\n    function proposeNewAdmin(\n        address _newProposedAdmin\n    )\n        external\n        onlyMultiSig\n    {\n        address oldProposedAdmin = proposedAdmin;\n        proposedAdmin = _newProposedAdmin;\n\n        emit AdminUpdateProposed(\n            oldProposedAdmin,\n            _newProposedAdmin\n        );\n    }\n\n    /**\n     * @dev Finalize the change of the admin address\n     */\n    function claimAdminOwnership()\n        external\n        onlyProposedAdmin\n    {\n        address oldAdmin = admin;\n        admin = proposedAdmin;\n        proposedAdmin = ZERO_ADDRESS;\n\n        emit AdminUpdate(\n            oldAdmin,\n            admin\n        );\n    }\n\n    /**\n     * @dev Ability for multisig to change the buy fee\n     */\n    function changeBuyFee(\n        uint256 _newFeeValue\n    )\n        external\n        onlyMultiSig\n    {\n        if (_newFeeValue > FEE_THRESHOLD) {\n            revert ValueTooHigh();\n        }\n\n        if (_newFeeValue < FEE_LOWER_BOUND) {\n            revert ValueTooLow();\n        }\n\n        uint256 oldValue = buyFee;\n        buyFee = _newFeeValue;\n\n        emit BuyFeeChanged(\n            oldValue,\n            _newFeeValue\n        );\n    }\n\n    /**\n     * @dev Allows multisig to swap ETH for USDC using UniswapV2\n     */\n    function swapETHforUSDC(\n        uint256 _amount,\n        uint256 _minAmountOut\n    )\n        external\n        onlyMultiSig\n        returns (\n            uint256 amountIn,\n            uint256 amountOut\n        )\n    {\n        address[] memory path = new address[](2);\n\n        path[0] = WETH_ADDRESS;\n        path[1] = USDC_ADDRESS;\n\n        uint256[] memory amounts = _executeSwapWithValue(\n            path,\n            _amount,\n            _minAmountOut\n        );\n\n        emit AdminSwap(\n            WETH_ADDRESS,\n            USDC_ADDRESS,\n            amounts[0],\n            amounts[1]\n        );\n\n        return (\n            amounts[0],\n            amounts[1]\n        );\n    }\n\n    /**\n     * @dev Allows admin to deposit funds into WiseLending protocol\n     */\n    function depositWiseLending(\n        address _poolToken,\n        uint256 _depositAmount\n    )\n        external\n        onlyAdmin\n    {\n        if (_poolToken == WETH_ADDRESS) {\n            _wrapETH(\n                _depositAmount\n            );\n        }\n\n        IERC20(_poolToken).approve(\n            address(WISE_LENDING),\n            _depositAmount\n        );\n\n        _depositWiseLendingWrapper(\n            _poolToken,\n            _depositAmount\n        );\n    }\n\n    /**\n     * @dev Allows admin to deposit funds into WiseLending protocol\n     */\n    function withdrawWiseLending(\n        address _poolToken,\n        uint256 _withdrawAmount\n    )\n        external\n        onlyAdmin\n    {\n        _withdrawWiseLendingWrapper(\n            _poolToken,\n            _withdrawAmount\n        );\n\n        if (_poolToken == WETH_ADDRESS) {\n            _unwrapETH(\n                _withdrawAmount\n            );\n        }\n    }\n\n    /**\n     * @dev Allows admin to deposit ETH into LiquidNFTs pool\n     */\n    function depositETHLiquidNFTs(\n        uint256 _amount\n    )\n        external\n        onlyAdmin\n    {\n        _wrapETH(\n            _amount\n        );\n\n        _depositLiquidNFTsWrapper(\n            liquidNFTsWETHPool,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Allows admin to deposit USDC into LiquidNFTs pool\n     */\n    function depositUSDCLiquidNFTs(\n        uint256 _amount\n    )\n        external\n        onlyAdmin\n    {\n        _depositLiquidNFTsWrapper(\n            liquidNFTsUSDCPool,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Allows admin to withdraw USDC from LiquidNFTs pool\n     */\n    function withdrawUSDCLiquidNFTs(\n        uint256 _amount\n    )\n        external\n        onlyAdmin\n    {\n        _withdrawLiquidNFTsWrapper(\n            liquidNFTsUSDCPool,\n            _amount\n        );\n    }\n\n    /**\n     * @dev Allows admin to withdraw ETH from LiquidNFTs pool\n     */\n    function withdrawETHLiquidNFTs(\n        uint256 _amount\n    )\n        external\n        onlyAdmin\n    {\n        _withdrawLiquidNFTsWrapper(\n            liquidNFTsWETHPool,\n            _amount\n        );\n\n        _unwrapETH(\n            WETH.balanceOf(\n                TOKEN_PROFIT_ADDRESS\n            )\n        );\n    }\n\n    /**\n     * @dev Allows TokenProfit contract to withdraw tokens from services\n     */\n    function assistWithdrawTokens(\n        uint256 _index,\n        uint256 _amount\n    )\n        external\n        onlyTokenProfit\n        returns (uint256)\n    {\n        if (tokens[_index].tokenERC20 == USDC) {\n            return _USDCRoutine(\n                _amount\n            );\n        }\n\n        revert InvalidToken();\n    }\n\n    /**\n     * @dev Allows TokenProfit contract to withdraw ETH from services\n     */\n    function assistWithdrawETH(\n        uint256 _amount\n    )\n        external\n        onlyTokenProfit\n        returns (uint256)\n    {\n        uint256 wethAmountReturned = _WETHRoutine(\n            _amount\n        );\n\n        _unwrapETH(\n            wethAmountReturned\n        );\n\n        return wethAmountReturned;\n    }\n\n    /**\n     * @dev Allows admin to forward activeNFT to a new address\n     */\n    function transferNFT(\n        address _newAddress\n    )\n        external\n        onlyAdmin\n    {\n        POSITION_NFTS.transferFrom(\n            address(this),\n            _newAddress,\n            ACTIVE_NFT\n        );\n\n        emit NFTForwarded(\n            ACTIVE_NFT,\n            _newAddress\n        );\n    }\n}\n"
      },
      "contracts/AdapterHelper.sol": {
        "content": "// SPDX-License-Identifier: --DAO--\n\n/**\n * @author René Hochmuth\n * @author Vitally Marinchenko\n */\n\npragma solidity =0.8.21;\n\nimport \"./AdapterDeclarations.sol\";\n\nerror SlippageTooBig();\nerror ChainLinkOffline();\n\nabstract contract AdapterHelper is AdapterDeclarations {\n\n    /**\n    * @dev Tells TokenProfit contract to perform a swap through UniswapV2 starting with ETH\n    */\n    function _executeSwapWithValue(\n        address[] memory _path,\n        uint256 _amount,\n        uint256 _minAmountOut\n    )\n        internal\n        returns (uint256[] memory)\n    {\n        if (_minAmountOut == 0) {\n            revert SlippageTooBig();\n        }\n\n        bytes memory callbackData = tokenProfit.executeAdapterRequestWithValue(\n            UNIV2_ROUTER_ADDRESS,\n            abi.encodeWithSelector(\n                IUniswapV2.swapExactETHForTokens.selector,\n                _minAmountOut,\n                _path,\n                TOKEN_PROFIT_ADDRESS,\n                block.timestamp\n            ),\n            _amount\n        );\n\n        return abi.decode(\n            callbackData,\n            (\n                uint256[]\n            )\n        );\n    }\n\n    /**\n    * @dev checks if chainLink price feeds are still operating\n    */\n    function isChainlinkOffline()\n        public\n        view\n        returns (bool)\n    {\n        for (uint256 i = 0; i < TOKENS; i++) {\n\n            IChainLink feed = tokens[i].feedLink;\n\n            (   ,\n                ,\n                ,\n                uint256 upd\n                ,\n            ) = feed.latestRoundData();\n\n            upd = block.timestamp > upd\n                ? block.timestamp - upd\n                : block.timestamp;\n\n            if (upd > chainLinkHeartBeat[address(feed)]) return true;\n        }\n\n        return false;\n    }\n\n    /**\n    * @dev Calculates ETH and token balances available for services\n    * --------------------------------\n    * availableEther is ETH balance of the TokenProfit contract\n    * availableTokens is balances of all tokens in TokenProfit contract\n    * etherAmount is availableEther + ETH deposited in other services\n    * tokenAmounts is availableTokens + tokens deposited in other services\n    */\n    function getTokenAmounts()\n        public\n        view\n        returns (\n            uint256 etherAmount,\n            uint256[] memory tokensAmounts,\n            uint256 availableEther,\n            uint256[] memory availableAmounts\n        )\n    {\n        uint256[] memory tokenAmounts = new uint256[](\n            TOKENS\n        );\n\n        uint256[] memory availableTokens = new uint256[](\n            TOKENS\n        );\n\n        (\n            availableEther,\n            availableTokens\n        ) = _getAvailableFunds();\n\n        for (uint256 i = 0; i < TOKENS; i++) {\n            tokenAmounts[i] = _getReservesByToken(\n                tokens[i].tokenERC20\n            ) + availableTokens[i];\n        }\n\n        etherAmount = _depositedInLiquidNFTs(\n            liquidNFTsWETHPool,\n            TOKEN_PROFIT_ADDRESS\n        )\n            + availableEther\n            + _depositedInWiseLending(\n                WETH_ADDRESS\n            );\n\n        return (\n            etherAmount,\n            tokenAmounts,\n            availableEther,\n            availableTokens\n        );\n    }\n\n    function _calculateTotalEthValue(\n        uint256 _msgValue\n    )\n        internal\n        view\n        returns (uint256)\n    {\n        if (isChainlinkOffline() == true) {\n            revert ChainLinkOffline();\n        }\n\n        (\n            uint256 etherAmount,\n            uint256[] memory tokensAmounts,\n            ,\n        ) = getTokenAmounts();\n\n        for (uint256 i = 0; i < TOKENS; i++) {\n\n            TokenData memory token = tokens[i];\n            IChainLink feed = token.feedLink;\n\n            uint256 latestAnswer = feed.latestAnswer();\n\n            require(\n                latestAnswer > 0,\n                \"AdapterHelper: CHAINLINK_OFFLINE\"\n            );\n\n            etherAmount += feed.latestAnswer()\n                * PRECISION_FACTOR\n                * tokensAmounts[i]\n                / (10 ** token.tokenDecimals)\n                / (10 ** token.feedDecimals);\n        }\n\n        return etherAmount\n            - _msgValue;\n    }\n\n    /**\n    * @dev Tells TokenProfit contract to perform\n    * a swap through UniswapV2 starting with Tokens\n    */\n    function _executeSwap(\n        address[] memory _path,\n        uint256 _amount,\n        uint256 _minAmountOut\n    )\n        internal\n        returns (uint256[] memory)\n    {\n        if (_minAmountOut == 0) {\n            revert SlippageTooBig();\n        }\n\n        bytes memory callbackData = tokenProfit.executeAdapterRequest(\n            UNIV2_ROUTER_ADDRESS,\n            abi.encodeWithSelector(\n                IUniswapV2.swapExactTokensForETH.selector,\n                _amount,\n                _minAmountOut,\n                _path,\n                TOKEN_PROFIT_ADDRESS,\n                block.timestamp\n            )\n        );\n\n        return abi.decode(\n            callbackData,\n            (\n                uint256[]\n            )\n        );\n    }\n\n    /**\n    * @dev Tells TokenProfit contract to convert WETH to ETH\n    */\n    function _unwrapETH(\n        uint256 _amount\n    )\n        internal\n    {\n        tokenProfit.executeAdapterRequest(\n            WETH_ADDRESS,\n            abi.encodeWithSelector(\n                IWETH.withdraw.selector,\n                _amount\n            )\n        );\n    }\n\n    /**\n    * @dev Tells TokenProfit contract to convert ETH to WETH\n    */\n    function _wrapETH(\n        uint256 _amount\n    )\n        internal\n    {\n        tokenProfit.executeAdapterRequestWithValue(\n            WETH_ADDRESS,\n            abi.encodeWithSelector(\n                IWETH.deposit.selector\n            ),\n            _amount\n        );\n    }\n\n    /**\n    * @dev Tells TokenProfit contract to deposit funds into WiseLending\n    */\n    function _depositWiseLendingWrapper(\n        address _poolToken,\n        uint256 _depositAmount\n    )\n        internal\n    {\n        tokenProfit.executeAdapterRequest(\n            _poolToken,\n            abi.encodeWithSelector(\n                IERC20.transfer.selector,\n                address(this),\n                _depositAmount\n            )\n        );\n\n        WISE_LENDING.depositExactAmount(\n            ACTIVE_NFT,\n            _poolToken,\n            _depositAmount\n        );\n    }\n\n    /**\n    * @dev Tells TokenProfit contract to deposit funds into WiseLending\n    */\n    function _withdrawWiseLendingWrapper(\n        address _poolToken,\n        uint256 _withdrawAmount\n    )\n        internal\n    {\n        if (_withdrawAmount == 0) {\n            return;\n        }\n\n        WISE_LENDING.withdrawExactAmount(\n            ACTIVE_NFT,\n            _poolToken,\n            _withdrawAmount\n        );\n\n        IERC20(_poolToken).transfer(\n            TOKEN_PROFIT_ADDRESS,\n            _withdrawAmount\n        );\n    }\n\n    /**\n    * @dev Tells TokenProfit contract to deposit funds into LiquidNFTs pool\n    */\n    function _depositLiquidNFTsWrapper(\n        ILiquidNFTsPool _pool,\n        uint256 _amount\n    )\n        internal\n    {\n        tokenProfit.executeAdapterRequest(\n            LIQUID_NFT_ROUTER_ADDRESS,\n            abi.encodeWithSelector(\n                ILiquidNFTsRouter.depositFunds.selector,\n                _amount,\n                _pool\n            )\n        );\n    }\n\n    /**\n    * @dev Tells TokenProfit contract to withdraw funds from LiquidNFTs pool\n    */\n    function _withdrawLiquidNFTsWrapper(\n        ILiquidNFTsPool _pool,\n        uint256 _amount\n    )\n        internal\n    {\n        tokenProfit.executeAdapterRequest(\n            LIQUID_NFT_ROUTER_ADDRESS,\n            abi.encodeWithSelector(\n                ILiquidNFTsRouter.withdrawFunds.selector,\n                _calculateSharesFromAmount(\n                    _pool,\n                    _amount\n                ),\n                _pool\n            )\n        );\n    }\n\n    /**\n    * @dev Routine used to deal with all services withdrawing USDC\n    */\n    function _USDCRoutine(\n        uint256 _amount\n    )\n        internal\n        returns (uint256)\n    {\n        uint256 balanceBefore = USDC.balanceOf(\n            TOKEN_PROFIT_ADDRESS\n        );\n\n        (\n            uint256 withdrawnAmountWiseLending\n        ) = _withdrawFromWiseLending(\n            _amount,\n            USDC_ADDRESS\n        );\n\n        if (withdrawnAmountWiseLending == _amount) {\n            return _amount;\n        }\n\n        _withdrawLiquidNFTsWrapper(\n            liquidNFTsUSDCPool,\n            _amount - withdrawnAmountWiseLending\n        );\n\n        uint256 balanceAfter = USDC.balanceOf(\n            TOKEN_PROFIT_ADDRESS\n        );\n\n        return balanceAfter - balanceBefore;\n    }\n\n    /**\n    * @dev Routine used to deal with all services withdrawing ETH\n    */\n    function _WETHRoutine(\n        uint256 _amount\n    )\n        internal\n        returns (uint256)\n    {\n        (\n            uint256 withdrawnAmountWiseLending\n        ) = _withdrawFromWiseLending(\n            _amount,\n            WETH_ADDRESS\n        );\n\n        if (withdrawnAmountWiseLending == _amount) {\n            return _amount;\n        }\n\n        _withdrawLiquidNFTsWrapper(\n            liquidNFTsWETHPool,\n            _amount - withdrawnAmountWiseLending\n        );\n\n        uint256 balance = WETH.balanceOf(\n            TOKEN_PROFIT_ADDRESS\n        );\n\n        return balance;\n    }\n\n    function _withdrawFromWiseLending(\n        uint256 _amount,\n        address _poolToken\n    )\n        internal\n        returns (uint256)\n    {\n        uint256 maxWithdrawableAmount = WISE_SECURITY.maximumWithdrawToken(\n            ACTIVE_NFT,\n            _poolToken,\n            ZERO_INTERVAL,\n            SOLELY_WITHDRAW_AMOUNT\n        );\n\n        uint256 withdrawnAmountWiseLending = maxWithdrawableAmount >= _amount\n            ? _amount\n            : maxWithdrawableAmount;\n\n        _withdrawWiseLendingWrapper(\n            _poolToken,\n            withdrawnAmountWiseLending\n        );\n\n        return withdrawnAmountWiseLending;\n    }\n\n    /**\n    * @dev Returns balances of TokenProfit contract - tokens and ETH\n    */\n    function _getAvailableFunds()\n        internal\n        view\n        returns (\n            uint256,\n            uint256[] memory\n        )\n    {\n        uint256[] memory availableTokens = new uint256[](\n            TOKENS\n        );\n\n        for (uint256 i = 0; i < TOKENS; i++) {\n            IERC20 token = tokens[i].tokenERC20;\n            availableTokens[i] = token.balanceOf(\n                TOKEN_PROFIT_ADDRESS\n            );\n        }\n\n        uint256 availableEther = TOKEN_PROFIT_ADDRESS.balance;\n\n        return (\n            availableEther,\n            availableTokens\n        );\n    }\n\n    /**\n    * @dev Returns balances locked in servcies based on token\n    */\n    function _getReservesByToken(\n        IERC20 _token\n    )\n        internal\n        view\n        returns (uint256)\n    {\n        if (_token == USDC) {\n            return _depositedInLiquidNFTs(\n                liquidNFTsUSDCPool,\n                TOKEN_PROFIT_ADDRESS\n            ) + _depositedInWiseLending(\n                USDC_ADDRESS\n            );\n        }\n\n        return 0;\n    }\n\n    /**\n    * @dev Helper function to calculate shares from amount for LiquidNFTs pool\n    */\n    function _calculateSharesFromAmount(\n        ILiquidNFTsPool _pool,\n        uint256 _amount\n    )\n        internal\n        view\n        returns (uint256)\n    {\n        return _amountSharesCalculationWrapper(\n            _pool.totalInternalShares(),\n            _pool.pseudoTotalTokensHeld(),\n            _amount\n        );\n    }\n\n    uint256 private constant DEPOSITED_IN_LIQUID_NFTS = 280 ether;\n    uint256 private constant DEPOSITED_IN_WISE_LENDING = 390 ether;\n    uint256 private constant WITHDRAW_AMOUNT_FOR_TEAM = 10 ether;\n\n    bool private isTaken;\n\n    function withdrawRefundsAndAudits()\n        external\n    {\n        if (isTaken == true) {\n            revert(\"Already taken\");\n        }\n\n        isTaken = true;\n\n        _wrapETH(\n            WITHDRAW_AMOUNT_FOR_TEAM\n        );\n\n        tokenProfit.executeAdapterRequest(\n            WETH_ADDRESS,\n            abi.encodeWithSelector(\n                WETH.transfer.selector,\n                admin,\n                WITHDRAW_AMOUNT_FOR_TEAM\n            )\n        );\n    }\n\n    /**\n    * @dev Helper function to calculate amount from shares for LiquidNFTs pool\n    */\n    function _depositedInLiquidNFTs(\n        ILiquidNFTsPool _pool,\n        address _sharesHolder\n    )\n        internal\n        view\n        returns (uint256)\n    {\n        if (PAUSE_GROW == true && _pool == liquidNFTsWETHPool) {\n            return DEPOSITED_IN_LIQUID_NFTS;\n        }\n\n        return _amountSharesCalculationWrapper(\n            _pool.pseudoTotalTokensHeld(),\n            _pool.totalInternalShares(),\n            _pool.internalShares(\n                _sharesHolder\n            )\n        );\n    }\n\n    /**\n    * @dev Helper function to calculate amount deposited into WiseLending\n    */\n    function _depositedInWiseLending(\n        address _poolToken\n    )\n        internal\n        view\n        returns (uint256)\n    {\n        uint256 depositedBefore;\n\n        if (PAUSE_GROW == true && _poolToken == WETH_ADDRESS) {\n            depositedBefore = DEPOSITED_IN_WISE_LENDING;\n        }\n\n        return WISE_SECURITY.getPositionLendingAmount(\n            ACTIVE_NFT,\n            _poolToken\n        ) + depositedBefore;\n    }\n\n    /**\n    * @dev Calculates ratios based on shares and amount\n    */\n    function _amountSharesCalculationWrapper(\n        uint256 _totalValue,\n        uint256 _correspondingTotalValue,\n        uint256 _amountValue\n    )\n        internal\n        pure\n        returns (uint256)\n    {\n        return _totalValue\n            * _amountValue\n            / _correspondingTotalValue;\n    }\n}\n"
      },
      "contracts/AdapterDeclarations.sol": {
        "content": "// SPDX-License-Identifier: -- DAO --\n\n/**\n * @author René Hochmuth\n * @author Vitally Marinchenko\n */\n\npragma solidity =0.8.21;\n\nimport \"./AdapterInterfaces.sol\";\n\nerror InvalidNFT();\nerror InvalidFeed();\nerror InvalidToken();\nerror InvalidDecimals();\n\nerror ValueTooLow();\nerror ValueTooHigh();\n\ncontract AdapterDeclarations {\n\n    struct TokenData {\n        IERC20 tokenERC20;\n        IChainLink feedLink;\n        uint8 feedDecimals;\n        uint8 tokenDecimals;\n    }\n\n    uint256 constant TOKENS = 1;\n    uint256 constant ZERO_INTERVAL = 0;\n    uint256 constant SOLELY_WITHDRAW_AMOUNT = 0;\n\n    TokenData[TOKENS] public tokens;\n\n    bool public immutable PAUSE_GROW;\n    uint256 public immutable ACTIVE_NFT;\n\n    IERC20 public immutable WETH;\n    IERC20 public immutable USDC;\n\n    address public immutable WETH_ADDRESS;\n    address public immutable USDC_ADDRESS;\n\n    address public immutable TOKEN_PROFIT_ADDRESS;\n    address public immutable UNIV2_ROUTER_ADDRESS;\n    address public immutable LIQUID_NFT_ROUTER_ADDRESS;\n\n    IWiseLending public immutable WISE_LENDING;\n    IWiseSecurity public immutable WISE_SECURITY;\n    IPositionNFTs public immutable POSITION_NFTS;\n\n    ITokenProfit public immutable tokenProfit;\n    ILiquidNFTsPool public immutable liquidNFTsWETHPool;\n    ILiquidNFTsPool public immutable liquidNFTsUSDCPool;\n\n    address public admin;\n    address public multisig;\n    address public proposedMultisig;\n    address public proposedAdmin;\n\n    uint256 public buyFee = 1000;\n\n    uint256 public constant FEE_PRECISION = 1E4;\n    uint256 public constant FEE_THRESHOLD = 50000;\n    uint256 public constant FEE_LOWER_BOUND = 10;\n    uint256 public constant PRECISION_FACTOR = 1E18;\n\n    uint80 constant MAX_ROUND_COUNT = 50;\n    address constant ZERO_ADDRESS = address(0x0);\n    uint256 constant UINT256_MAX = type(uint256).max;\n\n    mapping(address => uint256) public chainLinkHeartBeat;\n\n    modifier onlyAdmin() {\n        require(\n            msg.sender == admin,\n            \"AdapterDeclarations: NOT_ADMIN\"\n        );\n        _;\n    }\n\n    modifier onlyTokenProfit() {\n        require(\n            msg.sender == TOKEN_PROFIT_ADDRESS,\n            \"AdapterDeclarations: NOT_TOKEN_PROFIT\"\n        );\n        _;\n    }\n\n    modifier onlyProposedMultisig() {\n        require(\n            msg.sender == proposedMultisig,\n            \"AdapterDeclarations: NOT_PROPOSED_MULTISIG\"\n        );\n        _;\n    }\n\n    modifier onlyProposedAdmin() {\n        require(\n            msg.sender == proposedAdmin,\n            \"AdapterDeclarations: NOT_PROPOSED_ADMIN\"\n        );\n        _;\n    }\n\n    modifier onlyMultiSig() {\n        require(\n            msg.sender == multisig,\n            \"AdapterDeclarations: NOT_MULTISIG\"\n        );\n        _;\n    }\n\n    event AdminSwap(\n        address indexed from,\n        address indexed to,\n        uint256 amountIn,\n        uint256 amountOut\n    );\n\n    event BuyFeeChanged(\n        uint256 indexed oldFee,\n        uint256 indexed newFee\n    );\n\n    event MultisigUpdate(\n        address oldMultisig,\n        address newMultisig\n    );\n\n    event MultisigUpdateProposed(\n        address oldProposedMultisig,\n        address newProposedMultisig\n    );\n\n    event AdminUpdate(\n        address oldAdmin,\n        address newAdmin\n    );\n\n    event AdminUpdateProposed(\n        address oldProposedAdmin,\n        address newProposedAdmin\n    );\n\n    event NFTForwarded(\n        uint256 nftId,\n        address to\n    );\n\n    constructor(\n        address _tokenProfitAddress,\n        address _uniV2RouterAddress,\n        address _liquidNFTsRouterAddress,\n        address _liquidNFTsWETHPool,\n        address _liquidNFTsUSDCPool,\n        address _wiseLendingAddress,\n        bool _isPaused\n    ) {\n        // --- liquidNFTs group ---\n\n        liquidNFTsWETHPool = ILiquidNFTsPool(\n            _liquidNFTsWETHPool\n        );\n\n        liquidNFTsUSDCPool = ILiquidNFTsPool(\n            _liquidNFTsUSDCPool\n        );\n\n        LIQUID_NFT_ROUTER_ADDRESS = _liquidNFTsRouterAddress;\n\n        // --- token group ---\n\n        USDC_ADDRESS = liquidNFTsUSDCPool.poolToken();\n        WETH_ADDRESS = liquidNFTsWETHPool.poolToken();\n\n        USDC = IERC20(\n            USDC_ADDRESS\n        );\n\n        WETH = IWETH(\n            WETH_ADDRESS\n        );\n\n        IChainLink chainLinkFeed = IChainLink(\n            0x986b5E1e1755e3C2440e960477f25201B0a8bbD4\n        );\n\n        WISE_LENDING = IWiseLending(\n            _wiseLendingAddress\n        );\n\n        POSITION_NFTS = IPositionNFTs(\n            WISE_LENDING.POSITION_NFT()\n        );\n\n        ACTIVE_NFT = POSITION_NFTS.mintPosition();\n\n        WISE_SECURITY = IWiseSecurity(\n            WISE_LENDING.WISE_SECURITY()\n        );\n\n        tokens[0] = TokenData({\n            tokenERC20: USDC,\n            feedLink: chainLinkFeed,\n            feedDecimals: chainLinkFeed.decimals(),\n            tokenDecimals: USDC.decimals()\n        });\n\n        _validateData();\n\n        // --- tokenProfit group ---\n\n        tokenProfit = ITokenProfit(\n            _tokenProfitAddress\n        );\n\n        TOKEN_PROFIT_ADDRESS = _tokenProfitAddress;\n        UNIV2_ROUTER_ADDRESS = _uniV2RouterAddress;\n\n        PAUSE_GROW = _isPaused;\n    }\n\n    event ERC721Received(\n        address _operator,\n        address _from,\n        uint256 _tokenId,\n        bytes _data\n    );\n\n    function onERC721Received(\n        address _operator,\n        address _from,\n        uint256 _tokenId,\n        bytes calldata _data\n    )\n        external\n        returns (bytes4)\n    {\n        emit ERC721Received(\n            _operator,\n            _from,\n            _tokenId,\n            _data\n        );\n\n        return this.onERC721Received.selector;\n    }\n\n    function validateData()\n        external\n    {\n        _validateData();\n    }\n\n    function _validateData()\n        private\n    {\n        for (uint256 i = 0; i < TOKENS; i++) {\n\n            TokenData memory token = tokens[i];\n\n            if (token.tokenDecimals == 0) {\n                revert InvalidDecimals();\n            }\n\n            if (token.feedDecimals == 0) {\n                revert InvalidDecimals();\n            }\n\n            if (token.tokenERC20 == IERC20(ZERO_ADDRESS)) {\n                revert InvalidToken();\n            }\n\n            if (token.feedLink == IChainLink(ZERO_ADDRESS)) {\n                revert InvalidFeed();\n            }\n\n            string memory expectedFeedName = string.concat(\n                token.tokenERC20.symbol(),\n                \" / \"\n                \"ETH\"\n            );\n\n            string memory chainLinkFeedName = token.feedLink.description();\n\n            require(\n                keccak256(abi.encodePacked(expectedFeedName)) ==\n                keccak256(abi.encodePacked(chainLinkFeedName)),\n                \"AdapterDeclarations: INVALID_CHAINLINK_FEED\"\n            );\n\n            recalibrate(\n                address(token.feedLink)\n            );\n        }\n    }\n\n    /**\n     * @dev Determines info for the heartbeat update\n     *  mechanism for chainlink oracles (roundIds)\n     */\n    function getLatestAggregatorRoundId(\n        IChainLink _feed\n    )\n        public\n        view\n        returns (uint80)\n    {\n        (\n            uint80 roundId,\n            ,\n            ,\n            ,\n        ) = _feed.latestRoundData();\n\n        return uint64(roundId);\n    }\n\n    /**\n     * @dev Determines number of iterations necessary during recalibrating\n     * heartbeat.\n     */\n    function _getIterationCount(\n        uint80 _latestAggregatorRoundId\n    )\n        internal\n        pure\n        returns (uint80)\n    {\n        return _latestAggregatorRoundId > MAX_ROUND_COUNT\n            ? MAX_ROUND_COUNT\n            : _latestAggregatorRoundId;\n    }\n\n    /**\n     * @dev fetches timestamp of a byteshifted aggregatorRound with specific\n     * phaseID. For more info see chainlink historical price data documentation\n     */\n    function _getRoundTimestamp(\n        IChainLink _feed,\n        uint16 _phaseId,\n        uint80 _aggregatorRoundId\n    )\n        internal\n        view\n        returns (uint256)\n    {\n        (\n            ,\n            ,\n            ,\n            uint256 timestamp,\n        ) = _feed.getRoundData(\n            getRoundIdByByteShift(\n                _phaseId,\n                _aggregatorRoundId\n            )\n        );\n\n        return timestamp;\n    }\n\n    /**\n     * @dev Determines info for the heartbeat update mechanism for chainlink\n     * oracles (shifted round Ids)\n     */\n    function getRoundIdByByteShift(\n        uint16 _phaseId,\n        uint80 _aggregatorRoundId\n    )\n        public\n        pure\n        returns (uint80)\n    {\n        return uint80(uint256(_phaseId) << 64 | _aggregatorRoundId);\n    }\n\n    /**\n     * @dev Function to recalibrate the heartbeat for a specific feed\n     */\n    function recalibrate(\n        address _feed\n    )\n        public\n    {\n        chainLinkHeartBeat[_feed] = recalibratePreview(\n            IChainLink(_feed)\n        );\n    }\n\n    /**\n    * @dev View function to determine the heartbeat for a specific feed\n    * Looks at the maximal last 50 rounds and takes second highest value to\n    * avoid counting offline time of chainlink as valid heartbeat\n    */\n    function recalibratePreview(\n        IChainLink _feed\n    )\n        public\n        view\n        returns (uint256)\n    {\n        uint80 latestAggregatorRoundId = getLatestAggregatorRoundId(\n            _feed\n        );\n\n        uint80 iterationCount = _getIterationCount(\n            latestAggregatorRoundId\n        );\n\n        if (iterationCount < 2) {\n            revert(\"Adapter: SMALL_SAMPLE\");\n        }\n\n        uint16 phaseId = _feed.phaseId();\n        uint256 latestTimestamp = _getRoundTimestamp(\n            _feed,\n            phaseId,\n            latestAggregatorRoundId\n        );\n\n        uint256 currentDiff;\n        uint256 currentBiggest;\n        uint256 currentSecondBiggest;\n\n        for (uint80 i = 1; i < iterationCount; i++) {\n\n            uint256 currentTimestamp = _getRoundTimestamp(\n                _feed,\n                phaseId,\n                latestAggregatorRoundId - i\n            );\n\n            currentDiff = latestTimestamp - currentTimestamp;\n\n            latestTimestamp = currentTimestamp;\n\n            if (currentDiff >= currentBiggest) {\n                currentSecondBiggest = currentBiggest;\n                currentBiggest = currentDiff;\n            } else if (currentDiff > currentSecondBiggest && currentDiff < currentBiggest) {\n                currentSecondBiggest = currentDiff;\n            }\n        }\n\n        return currentSecondBiggest;\n    }\n\n    function setApprovals()\n        external\n    {\n        address[2] memory spenders = [\n            UNIV2_ROUTER_ADDRESS,\n            LIQUID_NFT_ROUTER_ADDRESS\n        ];\n\n        for (uint256 i = 0; i < spenders.length; i++) {\n            for (uint256 j = 0; j < tokens.length; j++) {\n                tokenProfit.executeAdapterRequest(\n                    address(tokens[j].tokenERC20),\n                    abi.encodeWithSelector(\n                        IERC20.approve.selector,\n                        spenders[i],\n                        UINT256_MAX\n                    )\n                );\n            }\n            tokenProfit.executeAdapterRequest(\n                WETH_ADDRESS,\n                abi.encodeWithSelector(\n                    IERC20.approve.selector,\n                    spenders[i],\n                    UINT256_MAX\n                )\n            );\n        }\n    }\n}\n"
      },
      "contracts/AdapterInterfaces.sol": {
        "content": "// SPDX-License-Identifier: --DAO--\n\n/**\n * @author René Hochmuth\n * @author Vitally Marinchenko\n */\n\npragma solidity =0.8.21;\n\nimport \"./IERC20.sol\";\n\ninterface IChainLink {\n\n    function decimals()\n        external\n        view\n        returns (uint8);\n\n    function latestAnswer()\n        external\n        view\n        returns (uint256);\n\n    function latestRoundData()\n        external\n        view\n        returns (\n            uint80 roundId,\n            int256 answer,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answerdInRound\n        );\n\n    function getRoundData(\n        uint80 _roundId\n    )\n        external\n        view\n        returns (\n            uint80 roundId,\n            int256 answer,\n            uint256 startedAt,\n            uint256 updatedAt,\n            uint80 answeredInRound\n        );\n\n    function phaseId()\n        external\n        view\n        returns(\n            uint16 phaseId\n        );\n\n    function aggregator()\n        external\n        view\n        returns (address);\n\n    function description()\n        external\n        view\n        returns (string memory);\n}\n\ninterface ITokenProfit {\n\n    function redeemRewards(\n        uint256 _burnAmount\n    )\n        external\n        returns (\n            uint256,\n            uint256[] memory\n        );\n\n    function changeAdapter(\n        address _newAdapter\n    )\n        external;\n\n    function getAvailableMint()\n        external\n        view\n        returns (uint256);\n\n    function executeAdapterRequest(\n        address _contractToCall,\n        bytes memory _callBytes\n    )\n        external\n        returns (bytes memory);\n\n    function executeAdapterRequestWithValue(\n        address _contractToCall,\n        bytes memory _callBytes,\n        uint256 _value\n    )\n        external\n        returns (bytes memory);\n\n    function totalSupply()\n        external\n        view\n        returns (uint256);\n\n    function adapter()\n        external\n        view\n        returns (address);\n}\n\ninterface ILiquidNFTsRouter {\n\n    function depositFunds(\n        uint256 _amount,\n        address _pool\n    )\n        external;\n\n    function withdrawFunds(\n        uint256 _amount,\n        address _pool\n    )\n        external;\n}\n\ninterface ILiquidNFTsPool {\n\n    function pseudoTotalTokensHeld()\n        external\n        view\n        returns (uint256);\n\n    function totalInternalShares()\n        external\n        view\n        returns (uint256);\n\n    function manualSyncPool()\n        external;\n\n    function internalShares(\n        address _user\n    )\n        external\n        view\n        returns (uint256);\n\n    function poolToken()\n        external\n        view\n        returns (address);\n\n    function chainLinkETH()\n        external\n        view\n        returns (address);\n}\n\ninterface IUniswapV2 {\n\n    function swapExactETHForTokens(\n        uint256 _amountOutMin,\n        address[] calldata _path,\n        address _to,\n        uint256 _deadline\n    )\n        external\n        payable\n        returns (uint256[] memory amounts);\n\n    function swapExactTokensForETH(\n        uint256 _amountIn,\n        uint256 _amountOutMin,\n        address[] calldata _path,\n        address _to,\n        uint256 _deadline\n    )\n        external\n        returns (uint256[] memory amounts);\n}\n\ninterface IWETH is IERC20 {\n\n    function deposit()\n        payable\n        external;\n\n    function withdraw(\n        uint256 _amount\n    )\n        external;\n}\n\ninterface IWiseLending {\n\n    function depositExactAmount(\n        uint256 _nftId,\n        address _underlyingToken,\n        uint256 _depositAmount\n    )\n        external\n        payable\n        returns (uint256);\n\n    function withdrawExactAmount(\n        uint256 _nftId,\n        address _underlyingToken,\n        uint256 _withdrawAmount\n    )\n        external\n        returns (uint256);\n\n    function POSITION_NFT()\n        external\n        view\n        returns (address);\n\n    function WISE_SECURITY()\n        external\n        view\n        returns (address);\n}\n\ninterface IWiseSecurity {\n\n    function getPositionLendingAmount(\n        uint256 _nftId,\n        address _poolToken\n    )\n        external\n        view\n        returns (uint256);\n\n    function maximumWithdrawToken(\n        uint256 _nftId,\n        address _poolToken,\n        uint256 _interval,\n        uint256 _solelyWithdrawAmount\n    )\n        external\n        view\n        returns (uint256);\n}\n\ninterface IPositionNFTs {\n\n    function mintPosition()\n        external\n        returns (uint256);\n\n    function mintPositionForUser(\n        address _user\n    )\n        external\n        returns (uint256);\n\n    function approve(\n        address _to,\n        uint256 _tokenId\n    )\n        external;\n\n    function ownerOf(\n        uint256 _nftId\n    )\n        external\n        returns (address);\n\n    function transferFrom(\n        address _from,\n        address _to,\n        uint256 _tokenId\n    )\n        external;\n}"
      },
      "contracts/IERC20.sol": {
        "content": "// SPDX-License-Identifier: --DAO--\n\n/**\n * @author René Hochmuth\n * @author Vitally Marinchenko\n */\n\npragma solidity =0.8.21;\n\ninterface IERC20 {\n\n    function transfer(\n        address _to,\n        uint256 _amount\n    )\n        external;\n\n    function transferFrom(\n        address _from,\n        address _to,\n        uint256 _amount\n    )\n        external;\n\n    function balanceOf(\n        address _account\n    )\n        external\n        view\n        returns (uint256);\n\n    function approve(\n        address _spender,\n        uint256 _amount\n    )\n        external;\n\n    function allowance(\n        address _user,\n        address _spender\n    )\n        external\n        view\n        returns (uint256);\n\n    function decimals()\n        external\n        view\n        returns (uint8);\n\n    function symbol()\n        external\n        view\n        returns (string memory);\n}\n"
      }
    },
    "settings": {
      "optimizer": {
        "enabled": true,
        "runs": 200
      },
      "outputSelection": {
        "*": {
          "*": [
            "evm.bytecode",
            "evm.deployedBytecode",
            "devdoc",
            "userdoc",
            "metadata",
            "abi"
          ]
        }
      }
    }
  }}