From 38834515bc3955abab009245ab7888c555443d77 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Fri, 9 Aug 2024 13:34:30 +0200 Subject: [PATCH 1/3] feat: recover tokens from strategy --- contracts/BaseStrategy.sol | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/contracts/BaseStrategy.sol b/contracts/BaseStrategy.sol index 49ad667..9802023 100644 --- a/contracts/BaseStrategy.sol +++ b/contracts/BaseStrategy.sol @@ -576,9 +576,25 @@ abstract contract BaseStrategy is ERC4626, AccessControl { } /*////////////////////////////////////////////////////////////// - SWAP LOGIC + KEEPER LOGIC //////////////////////////////////////////////////////////////*/ + /** + * @notice Recover tokens from the strategy + * @param tokens array of tokens to recover + * @param receiver address to receive the tokens + * @custom:requires KEEPER_ROLE + */ + function recoverTokens(address[] calldata tokens, address receiver) public onlyRole(KEEPER_ROLE) { + uint256 length = tokens.length; + for (uint256 i; i < length; ++i) { + address token = tokens[i]; + if (token != asset() && token != STRATEGY_ASSET) { + IERC20(token).safeTransfer(receiver, IERC20(token).balanceOf(address(this))); + } + } + } + /** * @notice Swap tokens using the router/aggregator + vest the profit * @param tokens array of tokens to swap From 92e990d323627d72584082685762b3f8de541508 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Fri, 9 Aug 2024 13:52:37 +0200 Subject: [PATCH 2/3] feat: check for receiver --- contracts/BaseStrategy.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/BaseStrategy.sol b/contracts/BaseStrategy.sol index 9802023..dafd445 100644 --- a/contracts/BaseStrategy.sol +++ b/contracts/BaseStrategy.sol @@ -586,6 +586,10 @@ abstract contract BaseStrategy is ERC4626, AccessControl { * @custom:requires KEEPER_ROLE */ function recoverTokens(address[] calldata tokens, address receiver) public onlyRole(KEEPER_ROLE) { + if (receiver == address(0)) { + revert ZeroAddress(); + } + uint256 length = tokens.length; for (uint256 i; i < length; ++i) { address token = tokens[i]; From dc4d2e3b4e3a7a22aaa7d03a026af725e5944984 Mon Sep 17 00:00:00 2001 From: 0xtekgrinder <0xtekgrinder@protonmail.com> Date: Fri, 9 Aug 2024 13:52:46 +0200 Subject: [PATCH 3/3] tests: add RecoverTokens tests --- test/unit/RecoverTokens.t.sol | 61 +++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 test/unit/RecoverTokens.t.sol diff --git a/test/unit/RecoverTokens.t.sol b/test/unit/RecoverTokens.t.sol new file mode 100644 index 0000000..69a90c5 --- /dev/null +++ b/test/unit/RecoverTokens.t.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity 0.8.26; + +import "../ERC4626StrategyTest.t.sol"; + +contract RecoverTokensTest is ERC4626StrategyTest { + function test_recoverTokens_Success() public { + deal(USDC, address(strategy), 10e18); + + address[] memory tokens = new address[](1); + tokens[0] = USDC; + + vm.prank(keeper); + strategy.recoverTokens(tokens, alice); + + assertEq(IERC20(USDC).balanceOf(alice), 10e18); + } + + function test_recoverTokens_IgnoreAsset() public { + deal(USDC, address(strategy), 10e18); + deal(asset, address(strategy), 10e18); + + address[] memory tokens = new address[](2); + tokens[0] = USDC; + tokens[1] = asset; + + vm.prank(keeper); + strategy.recoverTokens(tokens, alice); + + assertEq(IERC20(USDC).balanceOf(alice), 10e18); + assertEq(IERC20(asset).balanceOf(alice), 0); + assertEq(IERC20(asset).balanceOf(address(strategy)), 10e18); + } + + function test_recoverTokens_IgnoreStrategyAsset() public { + deal(USDC, address(strategy), 10e18); + deal(strategyAsset, address(strategy), 10e18); + + address[] memory tokens = new address[](2); + tokens[0] = USDC; + tokens[1] = strategyAsset; + + vm.prank(keeper); + strategy.recoverTokens(tokens, alice); + + assertEq(IERC20(USDC).balanceOf(alice), 10e18); + assertEq(IERC20(strategyAsset).balanceOf(alice), 0); + assertEq(IERC20(strategyAsset).balanceOf(address(strategy)), 10e18); + } + + function test_recoverTokens_ZeroReceiver() public { + deal(USDC, address(strategy), 10e18); + + address[] memory tokens = new address[](1); + tokens[0] = USDC; + + vm.expectRevert(ZeroAddress.selector); + vm.prank(keeper); + strategy.recoverTokens(tokens, address(0)); + } +}