From f8afbce8d62187c9c9970a6e2bea390cdeda00e8 Mon Sep 17 00:00:00 2001 From: Schlag <89420541+Schlagonia@users.noreply.github.com> Date: Tue, 24 Sep 2024 16:58:42 -0600 Subject: [PATCH] build: update to 303 (#40) * build: add factory * chore: install new version * chore: update periphery * fix: factory --- .gitmodules | 10 ++-- lib/tokenized-strategy | 2 +- lib/tokenized-strategy-periphery | 2 +- src/StrategyFactory.sol | 80 ++++++++++++++++++++++++++++++++ src/test/FunctionSignature.t.sol | 2 +- src/test/Shutdown.t.sol | 37 ++++++++++++++- src/test/utils/Setup.sol | 25 ++++++---- 7 files changed, 141 insertions(+), 17 deletions(-) create mode 100644 src/StrategyFactory.sol diff --git a/.gitmodules b/.gitmodules index 37f73502..63378a86 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,11 +6,11 @@ path = lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts branch = v4.9.5 -[submodule "lib/tokenized-strategy-periphery"] - path = lib/tokenized-strategy-periphery - url = https://github.com/yearn/tokenized-strategy-periphery - branch = master [submodule "lib/tokenized-strategy"] path = lib/tokenized-strategy url = https://github.com/yearn/tokenized-strategy - branch = v3.0.2-1 \ No newline at end of file + branch = v3.0.3 +[submodule "lib/tokenized-strategy-periphery"] + path = lib/tokenized-strategy-periphery + url = https://github.com/yearn/tokenized-strategy-periphery + branch = master \ No newline at end of file diff --git a/lib/tokenized-strategy b/lib/tokenized-strategy index 7bf18701..6e12ce08 160000 --- a/lib/tokenized-strategy +++ b/lib/tokenized-strategy @@ -1 +1 @@ -Subproject commit 7bf187015f5f7159276f80cd52204431ab1b3b8b +Subproject commit 6e12ce08584b49c7560227e94810d2f0e3dec8b2 diff --git a/lib/tokenized-strategy-periphery b/lib/tokenized-strategy-periphery index 6ce8d29b..03dfef66 160000 --- a/lib/tokenized-strategy-periphery +++ b/lib/tokenized-strategy-periphery @@ -1 +1 @@ -Subproject commit 6ce8d29b1e107a89754dd9f17337582734989b4d +Subproject commit 03dfef66bf046ddccee885bd634a928606601b35 diff --git a/src/StrategyFactory.sol b/src/StrategyFactory.sol new file mode 100644 index 00000000..79b78054 --- /dev/null +++ b/src/StrategyFactory.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.18; + +import {Strategy, ERC20} from "./Strategy.sol"; +import {IStrategyInterface} from "./interfaces/IStrategyInterface.sol"; + +contract StrategyFactory { + event NewStrategy(address indexed strategy, address indexed asset); + + address public immutable emergencyAdmin; + + address public immutable lendingPool; + address public immutable router; + address public immutable base; + + address public management; + address public performanceFeeRecipient; + address public keeper; + + /// @notice Track the deployments. asset => pool => strategy + mapping(address => address) public deployments; + + constructor( + address _management, + address _performanceFeeRecipient, + address _keeper, + address _emergencyAdmin + ) { + management = _management; + performanceFeeRecipient = _performanceFeeRecipient; + keeper = _keeper; + emergencyAdmin = _emergencyAdmin; + } + + /** + * @notice Deploy a new Strategy. + * @param _asset The underlying asset for the strategy to use. + * @return . The address of the new strategy. + */ + function newStrategy( + address _asset, + string calldata _name + ) external virtual returns (address) { + // tokenized strategies available setters. + IStrategyInterface _newStrategy = IStrategyInterface( + address(new Strategy(_asset, _name)) + ); + + _newStrategy.setPerformanceFeeRecipient(performanceFeeRecipient); + + _newStrategy.setKeeper(keeper); + + _newStrategy.setPendingManagement(management); + + _newStrategy.setEmergencyAdmin(emergencyAdmin); + + emit NewStrategy(address(_newStrategy), _asset); + + deployments[_asset] = address(_newStrategy); + return address(_newStrategy); + } + + function setAddresses( + address _management, + address _performanceFeeRecipient, + address _keeper + ) external { + require(msg.sender == management, "!management"); + management = _management; + performanceFeeRecipient = _performanceFeeRecipient; + keeper = _keeper; + } + + function isDeployedStrategy( + address _strategy + ) external view returns (bool) { + address _asset = IStrategyInterface(_strategy).asset(); + return deployments[_asset] == _strategy; + } +} diff --git a/src/test/FunctionSignature.t.sol b/src/test/FunctionSignature.t.sol index 0389a271..2d447353 100644 --- a/src/test/FunctionSignature.t.sol +++ b/src/test/FunctionSignature.t.sol @@ -34,7 +34,7 @@ contract FunctionSignatureTest is Setup { assertEq(strategy.totalSupply(), 0, "total supply"); assertEq(strategy.unlockedShares(), 0, "unlocked shares"); assertEq(strategy.asset(), address(asset), "asset"); - assertEq(strategy.apiVersion(), "3.0.2", "api"); + assertEq(strategy.apiVersion(), "3.0.3", "api"); assertEq(strategy.MAX_FEE(), 5_000, "max fee"); assertEq(strategy.fullProfitUnlockDate(), 0, "unlock date"); assertEq(strategy.profitUnlockingRate(), 0, "unlock rate"); diff --git a/src/test/Shutdown.t.sol b/src/test/Shutdown.t.sol index a9ce1e5e..5e498b9f 100644 --- a/src/test/Shutdown.t.sol +++ b/src/test/Shutdown.t.sol @@ -20,7 +20,7 @@ contract ShutdownTest is Setup { skip(1 days); // Shutdown the strategy - vm.prank(management); + vm.prank(emergencyAdmin); strategy.shutdownStrategy(); assertEq(strategy.totalAssets(), _amount, "!totalAssets"); @@ -39,5 +39,40 @@ contract ShutdownTest is Setup { ); } + function test_emergencyWithdraw_maxUint(uint256 _amount) public { + vm.assume(_amount > minFuzzAmount && _amount < maxFuzzAmount); + + // Deposit into strategy + mintAndDepositIntoStrategy(strategy, user, _amount); + + assertEq(strategy.totalAssets(), _amount, "!totalAssets"); + + // Earn Interest + skip(1 days); + + // Shutdown the strategy + vm.prank(emergencyAdmin); + strategy.shutdownStrategy(); + + assertEq(strategy.totalAssets(), _amount, "!totalAssets"); + + // should be able to pass uint 256 max and not revert. + vm.prank(emergencyAdmin); + strategy.emergencyWithdraw(type(uint256).max); + + // Make sure we can still withdraw the full amount + uint256 balanceBefore = asset.balanceOf(user); + + // Withdraw all funds + vm.prank(user); + strategy.redeem(_amount, user, user); + + assertGe( + asset.balanceOf(user), + balanceBefore + _amount, + "!final balance" + ); + } + // TODO: Add tests for any emergency function added. } diff --git a/src/test/utils/Setup.sol b/src/test/utils/Setup.sol index a24c4dc5..42042f1d 100644 --- a/src/test/utils/Setup.sol +++ b/src/test/utils/Setup.sol @@ -5,6 +5,7 @@ import "forge-std/console2.sol"; import {ExtendedTest} from "./ExtendedTest.sol"; import {Strategy, ERC20} from "../../Strategy.sol"; +import {StrategyFactory} from "../../StrategyFactory.sol"; import {IStrategyInterface} from "../../interfaces/IStrategyInterface.sol"; // Inherit the events so they can be checked if desired. @@ -23,6 +24,8 @@ contract Setup is ExtendedTest, IEvents { ERC20 public asset; IStrategyInterface public strategy; + StrategyFactory public strategyFactory; + mapping(string => address) public tokenAddrs; // Addresses for different roles we will use repeatedly. @@ -30,6 +33,7 @@ contract Setup is ExtendedTest, IEvents { address public keeper = address(4); address public management = address(1); address public performanceFeeRecipient = address(3); + address public emergencyAdmin = address(5); // Address of the real deployed Factory address public factory; @@ -54,6 +58,13 @@ contract Setup is ExtendedTest, IEvents { // Set decimals decimals = asset.decimals(); + strategyFactory = new StrategyFactory( + management, + performanceFeeRecipient, + keeper, + emergencyAdmin + ); + // Deploy strategy and set variables strategy = IStrategyInterface(setUpStrategy()); @@ -71,16 +82,14 @@ contract Setup is ExtendedTest, IEvents { function setUpStrategy() public returns (address) { // we save the strategy as a IStrategyInterface to give it the needed interface IStrategyInterface _strategy = IStrategyInterface( - address(new Strategy(address(asset), "Tokenized Strategy")) + address( + strategyFactory.newStrategy( + address(asset), + "Tokenized Strategy" + ) + ) ); - // set keeper - _strategy.setKeeper(keeper); - // set treasury - _strategy.setPerformanceFeeRecipient(performanceFeeRecipient); - // set management of the strategy - _strategy.setPendingManagement(management); - vm.prank(management); _strategy.acceptManagement();