From 532100bd83d18b9028d76860d7220a2564e08760 Mon Sep 17 00:00:00 2001 From: Alien Deployer Date: Sun, 10 Mar 2024 00:05:05 +0300 Subject: [PATCH] GRMF 2.0.0, fixes --- chains/PolygonLib.sol | 96 ++++-------- .../GammaRetroMerklFarmStrategy.sol | 142 ++++++++++++++---- src/strategies/IchiRetroMerklFarmStrategy.sol | 1 - src/strategies/libs/GRMFLib.sol | 102 +++++++++++++ src/strategies/libs/IRMFLib.sol | 22 +-- test/strategies/GRMF.Polygon.t.sol | 9 ++ test/strategies/IRMF.Polygon.t.sol | 9 ++ 7 files changed, 271 insertions(+), 110 deletions(-) create mode 100644 src/strategies/libs/GRMFLib.sol diff --git a/chains/PolygonLib.sol b/chains/PolygonLib.sol index 12b0758a..25b8af83 100644 --- a/chains/PolygonLib.sol +++ b/chains/PolygonLib.sol @@ -464,51 +464,21 @@ library PolygonLib { //endregion -- QuickSwap V3 farms ----- //region ----- Gamma QuickSwap Merkl farms ----- - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_USDCe_USDT, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.STABLE - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WMATIC_WETH_NARROW, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.NARROW - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WBTC_WETH_NARROW, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.NARROW - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_USDCe_WETH_NARROW, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.NARROW - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WMATIC_USDCe_NARROW, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.NARROW - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WMATIC_WETH_WIDE, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.WIDE - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WBTC_USDCe_NARROW, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.NARROW - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WMATIC_USDT_NARROW, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.NARROW - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_USDCe_WETH_WIDE, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.WIDE - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WBTC_WETH_WIDE, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.WIDE - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WETH_USDT_NARROW, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.NARROW - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WMATIC_USDCe_WIDE, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.WIDE - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WBTC_USDCe_WIDE, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.WIDE - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WETH_USDT_WIDE, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.WIDE - ); - _farms[i++] = _makeGammaQuickSwapMerklFarm( - TOKEN_dQUICK, GAMMA_QUICKSWAP_WMATIC_USDT_WIDE, GAMMA_QUICKSWAP_UNIPROXY, ALMPositionNameLib.WIDE - ); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_USDCe_USDT, ALMPositionNameLib.STABLE); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WMATIC_WETH_NARROW, ALMPositionNameLib.NARROW); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WBTC_WETH_NARROW, ALMPositionNameLib.NARROW); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_USDCe_WETH_NARROW, ALMPositionNameLib.NARROW); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WMATIC_USDCe_NARROW, ALMPositionNameLib.NARROW); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WMATIC_WETH_WIDE, ALMPositionNameLib.WIDE); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WBTC_USDCe_NARROW, ALMPositionNameLib.NARROW); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WMATIC_USDT_NARROW, ALMPositionNameLib.NARROW); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_USDCe_WETH_WIDE, ALMPositionNameLib.WIDE); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WBTC_WETH_WIDE, ALMPositionNameLib.WIDE); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WETH_USDT_NARROW, ALMPositionNameLib.NARROW); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WMATIC_USDCe_WIDE, ALMPositionNameLib.WIDE); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WBTC_USDCe_WIDE, ALMPositionNameLib.WIDE); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WETH_USDT_WIDE, ALMPositionNameLib.WIDE); + _farms[i++] = _makeGammaQuickSwapMerklFarm(GAMMA_QUICKSWAP_WMATIC_USDT_WIDE, ALMPositionNameLib.WIDE); //endregion -- Gamma QuickSwap farms ----- } @@ -706,25 +676,17 @@ library PolygonLib { uint i; // [29] - _farms[i++] = _makeGammaRetroMerklFarm( - TOKEN_oRETRO, GAMMA_RETRO_WMATIC_USDCe_NARROW, GAMMA_RETRO_UNIPROXY, ALMPositionNameLib.NARROW - ); + _farms[i++] = _makeGammaRetroMerklFarm(GAMMA_RETRO_WMATIC_USDCe_NARROW, ALMPositionNameLib.NARROW); // [30] - _farms[i++] = _makeGammaRetroMerklFarm( - TOKEN_oRETRO, GAMMA_RETRO_WMATIC_WETH_NARROW, GAMMA_RETRO_UNIPROXY, ALMPositionNameLib.NARROW - ); + _farms[i++] = _makeGammaRetroMerklFarm(GAMMA_RETRO_WMATIC_WETH_NARROW, ALMPositionNameLib.NARROW); // [31] - _farms[i++] = _makeGammaRetroMerklFarm( - TOKEN_oRETRO, GAMMA_RETRO_WBTC_WETH_WIDE, GAMMA_RETRO_UNIPROXY, ALMPositionNameLib.WIDE - ); + _farms[i++] = _makeGammaRetroMerklFarm(GAMMA_RETRO_WBTC_WETH_WIDE, ALMPositionNameLib.WIDE); } function _makeGammaQuickSwapMerklFarm( - address rewardAsset0, address hypervisor, - address uniProxy, uint preset ) internal view returns (IFactory.Farm memory) { IFactory.Farm memory farm; @@ -732,9 +694,9 @@ library PolygonLib { farm.pool = IHypervisor(hypervisor).pool(); farm.strategyLogicId = StrategyIdLib.GAMMA_QUICKSWAP_MERKL_FARM; farm.rewardAssets = new address[](1); - farm.rewardAssets[0] = rewardAsset0; + farm.rewardAssets[0] = TOKEN_dQUICK; farm.addresses = new address[](2); - farm.addresses[0] = uniProxy; + farm.addresses[0] = GAMMA_QUICKSWAP_UNIPROXY; farm.addresses[1] = hypervisor; farm.nums = new uint[](1); farm.nums[0] = preset; @@ -742,21 +704,21 @@ library PolygonLib { return farm; } - function _makeGammaRetroMerklFarm( - address rewardAsset0, - address hypervisor, - address uniProxy, - uint preset - ) internal view returns (IFactory.Farm memory) { + function _makeGammaRetroMerklFarm(address hypervisor, uint preset) internal view returns (IFactory.Farm memory) { IFactory.Farm memory farm; farm.status = 0; farm.pool = IHypervisor(hypervisor).pool(); farm.strategyLogicId = StrategyIdLib.GAMMA_RETRO_MERKL_FARM; farm.rewardAssets = new address[](1); - farm.rewardAssets[0] = rewardAsset0; - farm.addresses = new address[](2); - farm.addresses[0] = uniProxy; + farm.rewardAssets[0] = TOKEN_oRETRO; + farm.addresses = new address[](7); + farm.addresses[0] = GAMMA_RETRO_UNIPROXY; farm.addresses[1] = hypervisor; + farm.addresses[2] = TOKEN_CASH; + farm.addresses[3] = POOL_RETRO_USDCe_CASH_100; + farm.addresses[4] = POOL_RETRO_oRETRO_RETRO_10000; + farm.addresses[5] = POOL_RETRO_CASH_RETRO_10000; + farm.addresses[6] = RETRO_QUOTER; farm.nums = new uint[](1); farm.nums[0] = preset; farm.ticks = new int24[](0); diff --git a/src/strategies/GammaRetroMerklFarmStrategy.sol b/src/strategies/GammaRetroMerklFarmStrategy.sol index c3e83f07..ade5b38a 100644 --- a/src/strategies/GammaRetroMerklFarmStrategy.sol +++ b/src/strategies/GammaRetroMerklFarmStrategy.sol @@ -7,6 +7,7 @@ import "./libs/StrategyIdLib.sol"; import "./libs/FarmMechanicsLib.sol"; import "./libs/UniswapV3MathLib.sol"; import "./libs/ALMPositionNameLib.sol"; +import "./libs/GRMFLib.sol"; import "../integrations/gamma/IUniProxy.sol"; import "../integrations/gamma/IHypervisor.sol"; import "../integrations/uniswapv3/IUniswapV3Pool.sol"; @@ -14,6 +15,7 @@ import "../core/libs/CommonLib.sol"; import "../adapters/libs/AmmAdapterIdLib.sol"; /// @title Earning Merkl rewards on Retro by underlying Gamma Hypervisor +/// @dev 2.0.0: oRETRO transmutation through CASH flash loan /// @author Alien Deployer (https://github.com/a17) contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { using SafeERC20 for IERC20; @@ -23,7 +25,7 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @inheritdoc IControllable - string public constant VERSION = "1.0.0"; + string public constant VERSION = "2.0.0"; uint internal constant _PRECISION = 1e36; @@ -32,13 +34,11 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { 0x46595ab865e543d547ad8669c6b3d688cf90b51012c63b16ac16869cad017f00; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ - /* STORAGE */ + /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ - /// @custom:storage-location erc7201:stability.GammaRetroFarmStrategy - struct GammaRetroFarmStrategyStorage { - IUniProxy uniProxy; - } + error NotFlashPool(); + error PairReentered(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INITIALIZATION */ @@ -51,10 +51,10 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { } IFactory.Farm memory farm = _getFarm(addresses[0], nums[0]); - if (farm.addresses.length != 2 || farm.nums.length != 1 || farm.ticks.length != 0) { + if (farm.addresses.length != 7 || farm.nums.length != 1 || farm.ticks.length != 0) { revert IFarmingStrategy.BadFarm(); } - GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); + GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); $.uniProxy = IUniProxy(farm.addresses[0]); __LPStrategyBase_init( @@ -69,9 +69,104 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { __FarmingStrategyBase_init(addresses[0], nums[0]); + $.paymentToken = farm.addresses[2]; + $.flashPool = farm.addresses[3]; + $.oPool = farm.addresses[4]; + $.uToPaymentTokenPool = farm.addresses[5]; + $.quoter = farm.addresses[6]; + address[] memory _assets = assets(); + address oToken = _getFarmingStrategyBaseStorage()._rewardAssets[0]; + address uToken = GRMFLib.getOtherTokenFromPool(farm.addresses[4], oToken); + address swapper = IPlatform(addresses[0]).swapper(); IERC20(_assets[0]).forceApprove(farm.addresses[1], type(uint).max); IERC20(_assets[1]).forceApprove(farm.addresses[1], type(uint).max); + IERC20(farm.addresses[2]).forceApprove(oToken, type(uint).max); + IERC20(uToken).forceApprove(swapper, type(uint).max); + IERC20(farm.addresses[2]).forceApprove(swapper, type(uint).max); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CALLBACKS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + // Call back function, called by the pair during our flashloan. + function uniswapV3FlashCallback(uint, uint fee1, bytes calldata) external { + GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); + address flashPool = $.flashPool; + address paymentToken = $.paymentToken; + address oToken = _getFarmingStrategyBaseStorage()._rewardAssets[0]; + address uToken = GRMFLib.getOtherTokenFromPool($.oPool, oToken); + address _platform = platform(); + + if (msg.sender != flashPool) { + revert NotFlashPool(); + } + if (!$.flashOn) { + revert PairReentered(); + } + + // Exercise the oToken + uint paymentTokenAmount = IERC20(paymentToken).balanceOf(address(this)); + uint oTokenAmt = IERC20(oToken).balanceOf(address(this)); + + IOToken(oToken).exercise(oTokenAmt, paymentTokenAmount, address(this)); + + // Swap underlying to payment token + address swapper = IPlatform(_platform).swapper(); + + ISwapper.PoolData[] memory route = new ISwapper.PoolData[](1); + route[0].pool = $.uToPaymentTokenPool; + route[0].ammAdapter = IPlatform(_platform).ammAdapter(keccak256(bytes(ammAdapterId()))).proxy; + route[0].tokenIn = uToken; + route[0].tokenOut = paymentToken; + ISwapper(swapper).swapWithRoute( + route, GRMFLib.balance(uToken), LPStrategyLib.SWAP_ASSETS_PRICE_IMPACT_TOLERANCE + ); + + // Pay off our loan + uint pairDebt = paymentTokenAmount + fee1; + IERC20(paymentToken).safeTransfer(flashPool, pairDebt); + + $.flashOn = false; + } + + /// @dev Temporary actions + function upgradeStorageToVersion2( + address paymentToken, + address flashPool, + address oPool, + address uToPaymentTokenPool, + address quoter + ) external onlyOperator { + GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); + + if ($.paymentToken != address(0)) { + revert AlreadyExist(); + } + + // CASH + $.paymentToken = paymentToken; + + // USDCe-CASH 0.01% + $.flashPool = flashPool; + + // RETRO-oRETRO 1% + $.oPool = oPool; + + // CASH-RETRO 1% + $.uToPaymentTokenPool = uToPaymentTokenPool; + + // UniswapV3 Quoter + $.quoter = quoter; + + address oToken = _getFarmingStrategyBaseStorage()._rewardAssets[0]; + address uToken = GRMFLib.getOtherTokenFromPool(oPool, oToken); + address swapper = IPlatform(platform()).swapper(); + + IERC20(paymentToken).forceApprove(oToken, type(uint).max); + IERC20(uToken).forceApprove(swapper, type(uint).max); + IERC20(paymentToken).forceApprove(swapper, type(uint).max); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ @@ -200,19 +295,9 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @inheritdoc StrategyBase - function _depositAssets(uint[] memory amounts, bool claimRevenue) internal override returns (uint value) { - GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); - FarmingStrategyBaseStorage storage _$ = _getFarmingStrategyBaseStorage(); + function _depositAssets(uint[] memory amounts, bool) internal override returns (uint value) { + GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); StrategyBaseStorage storage __$ = _getStrategyBaseStorage(); - if (claimRevenue) { - (,,, uint[] memory rewardAmounts) = _claimRevenue(); - uint len = rewardAmounts.length; - // nosemgrep - for (uint i; i < len; ++i) { - // nosemgrep - _$._rewardsOnBalance[i] += rewardAmounts[i]; - } - } //slither-disable-next-line uninitialized-local uint[4] memory minIn; value = $.uniProxy.deposit(amounts[0], amounts[1], address(this), __$._underlying, minIn); @@ -247,7 +332,6 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { /// @inheritdoc StrategyBase function _claimRevenue() internal - view override returns ( address[] memory __assets, @@ -256,16 +340,8 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { uint[] memory __rewardAmounts ) { - StrategyBaseStorage storage __$__ = _getStrategyBaseStorage(); - FarmingStrategyBaseStorage storage _$_ = _getFarmingStrategyBaseStorage(); - __assets = __$__._assets; - __rewardAssets = _$_._rewardAssets; - __amounts = new uint[](2); - uint rwLen = __rewardAssets.length; - __rewardAmounts = new uint[](rwLen); - for (uint i; i < rwLen; ++i) { - __rewardAmounts[i] = StrategyLib.balance(__rewardAssets[i]); - } + return + GRMFLib.claimRevenue(_getStrategyBaseStorage(), _getFarmingStrategyBaseStorage(), _getGammaRetroStorage()); } /// @inheritdoc StrategyBase @@ -289,7 +365,7 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { returns (uint[] memory amountsConsumed, uint value) { // alternative calculation: beefy-contracts/contracts/BIFI/strategies/Gamma/StrategyQuickGamma.sol - GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); + GRMFLib.GammaRetroFarmStrategyStorage storage $ = _getGammaRetroStorage(); StrategyBaseStorage storage _$ = _getStrategyBaseStorage(); amountsConsumed = new uint[](2); address[] memory _assets = assets(); @@ -383,7 +459,7 @@ contract GammaRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { ); } - function _getGammaRetroStorage() private pure returns (GammaRetroFarmStrategyStorage storage $) { + function _getGammaRetroStorage() private pure returns (GRMFLib.GammaRetroFarmStrategyStorage storage $) { //slither-disable-next-line assembly assembly { $.slot := GAMMARETROFARMSTRATEGY_STORAGE_LOCATION diff --git a/src/strategies/IchiRetroMerklFarmStrategy.sol b/src/strategies/IchiRetroMerklFarmStrategy.sol index 04b129ef..e06de42c 100644 --- a/src/strategies/IchiRetroMerklFarmStrategy.sol +++ b/src/strategies/IchiRetroMerklFarmStrategy.sol @@ -118,7 +118,6 @@ contract IchiRetroMerklFarmStrategy is LPStrategyBase, FarmingStrategyBase { ISwapper(swapper).swapWithRoute( route, IRMFLib.balance(uToken), LPStrategyLib.SWAP_ASSETS_PRICE_IMPACT_TOLERANCE ); - // ISwapper(swapper).swap(uToken, paymentToken, IRMFLib.balance(uToken), LPStrategyLib.SWAP_ASSETS_PRICE_IMPACT_TOLERANCE); // Pay off our loan uint pairDebt = paymentTokenAmount + fee1; diff --git a/src/strategies/libs/GRMFLib.sol b/src/strategies/libs/GRMFLib.sol new file mode 100644 index 00000000..659f8c95 --- /dev/null +++ b/src/strategies/libs/GRMFLib.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.23; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "../../interfaces/IStrategy.sol"; +import "../../interfaces/IFarmingStrategy.sol"; +import "../../integrations/gamma/IUniProxy.sol"; +import "../../integrations/uniswapv3/IUniswapV3Pool.sol"; +import "../../integrations/uniswapv3/IQuoter.sol"; +import "../../integrations/retro/IOToken.sol"; + +/// @title Library for GRMF strategy code splitting +library GRMFLib { + /// @custom:storage-location erc7201:stability.GammaRetroFarmStrategy + struct GammaRetroFarmStrategyStorage { + IUniProxy uniProxy; + address paymentToken; + address flashPool; + address oPool; + address uToPaymentTokenPool; + address quoter; + bool flashOn; + } + + function claimRevenue( + IStrategy.StrategyBaseStorage storage __$__, + IFarmingStrategy.FarmingStrategyBaseStorage storage _$_, + GammaRetroFarmStrategyStorage storage $ + ) + external + returns ( + address[] memory __assets, + uint[] memory __amounts, + address[] memory __rewardAssets, + uint[] memory __rewardAmounts + ) + { + __assets = __$__._assets; + __amounts = new uint[](2); + __rewardAssets = _$_._rewardAssets; + uint rwLen = __rewardAssets.length; + __rewardAmounts = new uint[](rwLen); + + // should we swap or flash excercise + address oToken = __rewardAssets[0]; + uint oTokenAmount = balance(oToken); + address oPool = $.oPool; + + if (oPool == address(0)) { + revert("Init upgraded strategy first!"); + } + + if (oTokenAmount > 0) { + address uToken = getOtherTokenFromPool(oPool, oToken); + bool needSwap = _shouldWeSwap(oToken, uToken, oTokenAmount, oPool, $.quoter); + + if (!needSwap) { + // Get payment token amount needed to exercise oTokens. + uint amountNeeded = IOToken(oToken).getDiscountedPrice(oTokenAmount); + + // Enter flash loan. + $.flashOn = true; + IUniswapV3Pool($.flashPool).flash(address(this), 0, amountNeeded, ""); + __rewardAssets[0] = $.paymentToken; + } + } + + for (uint i; i < rwLen; ++i) { + __rewardAmounts[i] = balance(__rewardAssets[i]); + } + } + + function _shouldWeSwap( + address oToken, + address uToken, + uint amount, + address pool, + address quoter + ) internal returns (bool should) { + // Whats the amount of underlying we get for flashSwapping. + uint discount = IOToken(oToken).discount(); + uint flashAmount = amount * (100 - discount) / 100; + + // How much we get for just swapping through LP. + uint24 fee = IUniswapV3Pool(pool).fee(); + uint swapAmount = IQuoter(quoter).quoteExactInputSingle(oToken, uToken, fee, amount, 0); + + if (swapAmount > flashAmount) { + should = true; + } + } + + function getOtherTokenFromPool(address pool, address token) public view returns (address) { + address token0 = IUniswapV3Pool(pool).token0(); + address token1 = IUniswapV3Pool(pool).token1(); + return token == token0 ? token1 : token0; + } + + function balance(address token) public view returns (uint) { + return IERC20(token).balanceOf(address(this)); + } +} diff --git a/src/strategies/libs/IRMFLib.sol b/src/strategies/libs/IRMFLib.sol index 5bb76e3e..a20b56c5 100644 --- a/src/strategies/libs/IRMFLib.sol +++ b/src/strategies/libs/IRMFLib.sol @@ -128,21 +128,25 @@ library IRMFLib { address oToken = __rewardAssets[0]; uint oTokenAmount = balance(oToken); address oPool = $.oPool; + if (oPool == address(0)) { revert("Init upgraded strategy first!"); } - address uToken = getOtherTokenFromPool(oPool, oToken); - bool needSwap = _shouldWeSwap(oToken, uToken, oTokenAmount, oPool, $.quoter); + if (oTokenAmount > 0) { + address uToken = getOtherTokenFromPool(oPool, oToken); + + bool needSwap = _shouldWeSwap(oToken, uToken, oTokenAmount, oPool, $.quoter); - if (!needSwap) { - // Get payment token amount needed to exercise oTokens. - uint amountNeeded = IOToken(oToken).getDiscountedPrice(oTokenAmount); + if (!needSwap) { + // Get payment token amount needed to exercise oTokens. + uint amountNeeded = IOToken(oToken).getDiscountedPrice(oTokenAmount); - // Enter flash loan. - $.flashOn = true; - IUniswapV3Pool($.flashPool).flash(address(this), 0, amountNeeded, ""); - __rewardAssets[0] = $.paymentToken; + // Enter flash loan. + $.flashOn = true; + IUniswapV3Pool($.flashPool).flash(address(this), 0, amountNeeded, ""); + __rewardAssets[0] = $.paymentToken; + } } for (uint i; i < rwLen; ++i) { diff --git a/test/strategies/GRMF.Polygon.t.sol b/test/strategies/GRMF.Polygon.t.sol index a67c1685..b3f2b16c 100644 --- a/test/strategies/GRMF.Polygon.t.sol +++ b/test/strategies/GRMF.Polygon.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.23; import "../base/chains/PolygonSetup.sol"; import "../base/UniversalTest.sol"; +import "../../src/strategies/GammaRetroMerklFarmStrategy.sol"; contract GammaRetroMerklFarmStrategyTest is PolygonSetup, UniversalTest { function testGRMF() public universalTest { @@ -24,5 +25,13 @@ contract GammaRetroMerklFarmStrategyTest is PolygonSetup, UniversalTest { function _preHardWork() internal override { deal(PolygonLib.TOKEN_oRETRO, currentStrategy, 10e18); + + // cover flash swap callback reverts + vm.expectRevert(GammaRetroMerklFarmStrategy.NotFlashPool.selector); + GammaRetroMerklFarmStrategy(currentStrategy).uniswapV3FlashCallback(0,0,""); + + vm.expectRevert(GammaRetroMerklFarmStrategy.PairReentered.selector); + vm.prank(PolygonLib.POOL_RETRO_USDCe_CASH_100); + GammaRetroMerklFarmStrategy(currentStrategy).uniswapV3FlashCallback(0,0,""); } } diff --git a/test/strategies/IRMF.Polygon.t.sol b/test/strategies/IRMF.Polygon.t.sol index 69834d2e..427c2508 100644 --- a/test/strategies/IRMF.Polygon.t.sol +++ b/test/strategies/IRMF.Polygon.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.23; import "../base/UniversalTest.sol"; import "../base/chains/PolygonSetup.sol"; +import "../../src/strategies/IchiRetroMerklFarmStrategy.sol"; contract IchiRetroMerklFarmStrategyTest is PolygonSetup, UniversalTest { function testIchiRetroMerklFarmStrategy() public universalTest { @@ -21,5 +22,13 @@ contract IchiRetroMerklFarmStrategyTest is PolygonSetup, UniversalTest { function _preHardWork() internal override { deal(PolygonLib.TOKEN_oRETRO, currentStrategy, 10e18); + + // cover flash swap callback reverts + vm.expectRevert(IchiRetroMerklFarmStrategy.NotFlashPool.selector); + IchiRetroMerklFarmStrategy(currentStrategy).uniswapV3FlashCallback(0,0,""); + + vm.expectRevert(IchiRetroMerklFarmStrategy.PairReentered.selector); + vm.prank(PolygonLib.POOL_RETRO_USDCe_CASH_100); + IchiRetroMerklFarmStrategy(currentStrategy).uniswapV3FlashCallback(0,0,""); } }