diff --git a/.gitmodules b/.gitmodules index c3327f0f..48bdd05f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,13 +2,9 @@ path = lib/forge-std url = https://github.com/foundry-rs/forge-std branch = master -[submodule "lib/openzeppelin-contracts"] - path = lib/openzeppelin-contracts - url = https://github.com/OpenZeppelin/openzeppelin-contracts - branch = v4.9.5 -[submodule "lib/tokenized-strategy"] - path = lib/tokenized-strategy - url = https://github.com/yearn/tokenized-strategy +[submodule "lib/octant-v2-core"] + path = lib/octant-v2-core + url = https://github.com/golemfoundation/octant-v2-core [submodule "lib/tokenized-strategy-periphery"] path = lib/tokenized-strategy-periphery - url = https://github.com/yearn/tokenized-strategy-periphery + url = https://github.com/golemfoundation/tokenized-strategy-periphery diff --git a/.vscode/settings.json b/.vscode/settings.json index eb095a44..e4a38ecc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,16 @@ "forge-std/=lib/forge-std/src/", "@tokenized-strategy/=./lib/tokenized-strategy/src/", "@periphery/=./lib/tokenized-strategy-periphery/src/" + ], + "wake.compiler.solc.remappings": [ + "@gnosis.pm/safe-contracts/=lib/octant-v2-core/lib/safe-smart-account", + "@openzeppelin/contracts-upgradeable/=lib/octant-v2-core/lib/openzeppelin-contracts-upgradeable/contracts/", + "@openzeppelin/contracts/=lib/octant-v2-core/lib/openzeppelin-contracts/contracts/", + "forge-std/=lib/forge-std/src/", + "octant-v2-core/=lib/octant-v2-core/", + "openzeppelin-contracts-upgradeable/=lib/octant-v2-core/lib/openzeppelin-contracts-upgradeable/", + "openzeppelin-contracts/=lib/octant-v2-core/lib/openzeppelin-contracts/", + "tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/", + "zodiac/=lib/octant-v2-core/lib/zodiac/contracts/" ] } diff --git a/foundry.toml b/foundry.toml index 262de881..64c6aae1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,14 +1,25 @@ [profile.default] -src = 'src' -out = 'out' -libs = ['lib'] -solc = "0.8.23" - -remappings = [ - "@openzeppelin/=lib/openzeppelin-contracts/", - "forge-std/=lib/forge-std/src/", - "@tokenized-strategy/=lib/tokenized-strategy/src/", - "@periphery/=lib/tokenized-strategy-periphery/src/", -] - -# See more config options https://github.com/foundry-rs/foundry/tree/master/config \ No newline at end of file +src = "src" +out = "out" +libs = ["node_modules", "lib"] +evm_version = "cancun" +optimizer = true +optimizer_runs = 10_000_000 +solc = "0.8.25" +verbosity = 3 + +[profile.ci] +fuzz = { runs = 5000 } +invariant = { runs = 1000 } +verbosity = 4 + +[profile.local] + fuzz = { runs = 50 } + invariant = { runs = 10 } + # Speed up compilation and tests during development. + optimizer = true + +[rpc_endpoints] +mainnet = "${ETHEREUM_NODE_MAINNET}" + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/lib/octant-v2-core b/lib/octant-v2-core new file mode 160000 index 00000000..e7e7c5fc --- /dev/null +++ b/lib/octant-v2-core @@ -0,0 +1 @@ +Subproject commit e7e7c5fc711523976ad3759294ebf092b267c28f diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts deleted file mode 160000 index bd325d56..00000000 --- a/lib/openzeppelin-contracts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bd325d56b4c62c9c5c1aff048c37c6bb18ac0290 diff --git a/lib/tokenized-strategy b/lib/tokenized-strategy deleted file mode 160000 index 82806289..00000000 --- a/lib/tokenized-strategy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 82806289f967590c4efbf6bc3d237e4e7f0a0966 diff --git a/lib/tokenized-strategy-periphery b/lib/tokenized-strategy-periphery index fc802464..d732e918 160000 --- a/lib/tokenized-strategy-periphery +++ b/lib/tokenized-strategy-periphery @@ -1 +1 @@ -Subproject commit fc802464cb8ec6dacea20d8309dc7f82a2739710 +Subproject commit d732e918b208a9b2c33c71ea0ea9e8eb880cfc6e diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 00000000..1d103976 --- /dev/null +++ b/remappings.txt @@ -0,0 +1,9 @@ +@gnosis.pm/safe-contracts/=lib/octant-v2-core/lib/safe-smart-account +@openzeppelin/contracts-upgradeable/=lib/octant-v2-core/lib/openzeppelin-contracts-upgradeable/contracts/ +@openzeppelin/contracts/=lib/octant-v2-core/lib/openzeppelin-contracts/contracts/ +forge-std/=lib/forge-std/src/ +octant-v2-core/=lib/octant-v2-core/ +openzeppelin-contracts-upgradeable/=lib/octant-v2-core/lib/openzeppelin-contracts-upgradeable/ +openzeppelin-contracts/=lib/octant-v2-core/lib/openzeppelin-contracts/ +tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/ +zodiac/=lib/octant-v2-core/lib/zodiac/contracts/ \ No newline at end of file diff --git a/src/Strategy.sol b/src/Strategy.sol index def08717..c8095fad 100644 --- a/src/Strategy.sol +++ b/src/Strategy.sol @@ -1,8 +1,10 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; -import {BaseStrategy, ERC20} from "@tokenized-strategy/BaseStrategy.sol"; +import {BaseStrategy, ERC20} from "octant-v2-core/src/dragons/BaseStrategy.sol"; +import {Module} from "zodiac/core/Module.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {IStrategyInterface} from "./interfaces/IStrategyInterface.sol"; // Import interfaces for many popular DeFi projects, or add your own! //import "../interfaces//.sol"; @@ -20,13 +22,51 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol // NOTE: To implement permissioned functions you can use the onlyManagement, onlyEmergencyAuthorized and onlyKeepers modifiers -contract Strategy is BaseStrategy { +contract Strategy is Module, BaseStrategy { using SafeERC20 for ERC20; - constructor( - address _asset, - string memory _name - ) BaseStrategy(_asset, _name) {} + address public yieldSource; + bool public trigger; + bool public managed; + bool public kept; + bool public emergentizated; + + /// @dev Initialize function, will be triggered when a new proxy is deployed + /// @dev owner of this module will the safe multisig that calls setUp function + /// @param initializeParams Parameters of initialization encoded + function setUp(bytes memory initializeParams) public override initializer { + (address _owner, bytes memory data) = abi.decode(initializeParams, (address, bytes)); + + ( + address _tokenizedStrategyImplementation, + address _asset, + address _yieldSource, + address _management, + address _keeper, + address _dragonRouter, + uint256 _maxReportDelay, + string memory _name + ) = abi.decode(data, (address, address, address, address, address, address, uint256, string)); + + __Ownable_init(msg.sender); + __BaseStrategy_init( + _tokenizedStrategyImplementation, + _asset, + _owner, + _management, + _keeper, + _dragonRouter, + _maxReportDelay, + _name + ); + + yieldSource = _yieldSource; + if (_asset != ETH) ERC20(_asset).approve(_yieldSource, type(uint256).max); + + setAvatar(_owner); + setTarget(_owner); + transferOwnership(_owner); + } /*////////////////////////////////////////////////////////////// NEEDED TO BE OVERRIDDEN BY STRATEGIST @@ -98,11 +138,7 @@ contract Strategy is BaseStrategy { * @return _totalAssets A trusted and accurate account for the total * amount of 'asset' the strategy currently holds including idle funds. */ - function _harvestAndReport() - internal - override - returns (uint256 _totalAssets) - { + function _harvestAndReport() internal override returns (uint256 _totalAssets) { // TODO: Implement harvesting logic and accurate accounting EX: // // if(!TokenizedStrategy.isShutdown()) { @@ -135,9 +171,7 @@ contract Strategy is BaseStrategy { * @param . The address that is withdrawing from the strategy. * @return . The available amount that can be withdrawn in terms of `asset` */ - function availableWithdrawLimit( - address /*_owner*/ - ) public view override returns (uint256) { + function availableWithdrawLimit(address /*_owner*/ ) public view override returns (uint256) { // NOTE: Withdraw limitations such as liquidity constraints should be accounted for HERE // rather than _freeFunds in order to not count them as losses on withdraws. @@ -171,16 +205,16 @@ contract Strategy is BaseStrategy { * @param . The address that is depositing into the strategy. * @return . The available amount the `_owner` can deposit in terms of `asset` * - function availableDepositLimit( - address _owner - ) public view override returns (uint256) { - TODO: If desired Implement deposit limit logic and any needed state variables . - - EX: - uint256 totalAssets = TokenizedStrategy.totalAssets(); - return totalAssets >= depositLimit ? 0 : depositLimit - totalAssets; - } - */ + * function availableDepositLimit( + * address _owner + * ) public view override returns (uint256) { + * TODO: If desired Implement deposit limit logic and any needed state variables . + * + * EX: + * uint256 totalAssets = TokenizedStrategy.totalAssets(); + * return totalAssets >= depositLimit ? 0 : depositLimit - totalAssets; + * } + */ /** * @dev Optional function for strategist to override that can @@ -203,8 +237,8 @@ contract Strategy is BaseStrategy { * * @param _totalIdle The current amount of idle funds that are available to deploy. * - function _tend(uint256 _totalIdle) internal override {} - */ + * function _tend(uint256 _totalIdle) internal override {} + */ /** * @dev Optional trigger to override if tend() will be used by the strategy. @@ -212,8 +246,8 @@ contract Strategy is BaseStrategy { * * @return . Should return true if tend() should be called by keeper or false if not. * - function _tendTrigger() internal view override returns (bool) {} - */ + * function _tendTrigger() internal view override returns (bool) {} + */ /** * @dev Optional function for a strategist to override that will @@ -236,13 +270,12 @@ contract Strategy is BaseStrategy { * * @param _amount The amount of asset to attempt to free. * - function _emergencyWithdraw(uint256 _amount) internal override { - TODO: If desired implement simple logic to free deployed funds. - - EX: - _amount = min(_amount, aToken.balanceOf(address(this))); - _freeFunds(_amount); - } - - */ + * function _emergencyWithdraw(uint256 _amount) internal override { + * TODO: If desired implement simple logic to free deployed funds. + * + * EX: + * _amount = min(_amount, aToken.balanceOf(address(this))); + * _freeFunds(_amount); + * } + */ } diff --git a/src/StrategyFactory.sol b/src/StrategyFactory.sol index db02a734..ebc5d4e8 100644 --- a/src/StrategyFactory.sol +++ b/src/StrategyFactory.sol @@ -1,76 +1,61 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.18; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "octant-v2-core/src/dragons/DragonTokenizedStrategy.sol"; +import "forge-std/console2.sol"; import {Strategy, ERC20} from "./Strategy.sol"; import {IStrategyInterface} from "./interfaces/IStrategyInterface.sol"; contract StrategyFactory { - event NewStrategy(address indexed strategy, address indexed asset); + error InvalidStrategyId(); - address public immutable emergencyAdmin; + event NewStrategy(address indexed strategy, uint256 indexed strategyId); - address public management; - address public performanceFeeRecipient; - address public keeper; + /// @notice Track the deployments. strategyId => strategy address + mapping(uint256 => address) public deployments; - /// @notice Track the deployments. asset => pool => strategy - mapping(address => address) public deployments; + address public strategyImplementation; + address public dragonTokenizedStrategyImplementation; + uint256 public strategyId; - constructor( - address _management, - address _performanceFeeRecipient, - address _keeper, - address _emergencyAdmin - ) { - management = _management; - performanceFeeRecipient = _performanceFeeRecipient; - keeper = _keeper; - emergencyAdmin = _emergencyAdmin; + constructor() { + strategyId = 0; + strategyImplementation = address(new Strategy()); + dragonTokenizedStrategyImplementation = address(new DragonTokenizedStrategy()); } /** - * @notice Deploy a new Strategy. - * @param _asset The underlying asset for the strategy to use. - * @return . The address of the new strategy. + * @notice Deploy a new Strategy + * @param initializeParams The encoded parameters to initialize the strategy with + * @return address The address of the newly deployed strategy */ - function newStrategy( - address _asset, - string calldata _name - ) external virtual returns (address) { - // tokenized strategies available setters. - IStrategyInterface _newStrategy = IStrategyInterface( - address(new Strategy(_asset, _name)) + function newStrategy(bytes memory initializeParams) external virtual returns (address) { + ERC1967Proxy _newStrategy = new ERC1967Proxy( + strategyImplementation, + abi.encodeWithSelector(Strategy(payable(address(0))).setUp.selector, initializeParams) ); - _newStrategy.setPerformanceFeeRecipient(performanceFeeRecipient); + uint256 currentId = strategyId; + deployments[currentId] = address(_newStrategy); - _newStrategy.setKeeper(keeper); + emit NewStrategy(address(_newStrategy), currentId); - _newStrategy.setPendingManagement(management); + unchecked { + strategyId = currentId + 1; + } - _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; + /** + * @notice Retrieve a deployed strategy address by ID + * @param _strategyId The ID of the strategy to look up + * @return address The strategy contract address + */ + function getStrategy(uint256 _strategyId) external view returns (address) { + address strategy = deployments[_strategyId]; + if (strategy == address(0)) revert InvalidStrategyId(); + return strategy; } } diff --git a/src/interfaces/IStrategyInterface.sol b/src/interfaces/IStrategyInterface.sol index 917e2d0c..83054655 100644 --- a/src/interfaces/IStrategyInterface.sol +++ b/src/interfaces/IStrategyInterface.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; -import {IStrategy} from "@tokenized-strategy/interfaces/IStrategy.sol"; +import {IStrategy} from "octant-v2-core/src/interfaces/IStrategy.sol"; interface IStrategyInterface is IStrategy { - //TODO: Add your specific implementation interface in here. + function setUp(bytes memory initializeParams) external; } diff --git a/src/periphery/StrategyAprOracle.sol b/src/periphery/StrategyAprOracle.sol index 1c0bfb8f..d75f5f2d 100644 --- a/src/periphery/StrategyAprOracle.sol +++ b/src/periphery/StrategyAprOracle.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.18; -import {AprOracleBase} from "@periphery/AprOracle/AprOracleBase.sol"; +import {AprOracleBase} from "tokenized-strategy-periphery/src/AprOracle/AprOracleBase.sol"; contract StrategyAprOracle is AprOracleBase { constructor() AprOracleBase("Strategy Apr Oracle Example", msg.sender) {} @@ -25,10 +25,7 @@ contract StrategyAprOracle is AprOracleBase { * @param _delta The difference in debt. * @return . The expected apr for the strategy represented as 1e18. */ - function aprAfterDebtChange( - address _strategy, - int256 _delta - ) external view override returns (uint256) { + function aprAfterDebtChange(address _strategy, int256 _delta) external view override returns (uint256) { // TODO: Implement any necessary logic to return the most accurate // APR estimation for the strategy. return 1e17; diff --git a/src/test/FunctionSignature.t.sol b/src/test/FunctionSignature.t.sol index 6df721b4..fafc5e42 100644 --- a/src/test/FunctionSignature.t.sol +++ b/src/test/FunctionSignature.t.sol @@ -14,14 +14,8 @@ contract FunctionSignatureTest is Setup { // Does not check functions that are strategy dependant and will be checked in other tests function test_functionCollisions() public { uint256 wad = 1e18; - vm.expectRevert("initialized"); - strategy.initialize( - address(asset), - "name", - management, - performanceFeeRecipient, - keeper - ); + // vm.expectRevert("initialized"); + // strategy.initialize(address(asset), yieldSource); // Check view functions assertEq(strategy.convertToAssets(wad), wad, "convert to assets"); @@ -32,20 +26,15 @@ contract FunctionSignatureTest is Setup { assertEq(strategy.previewRedeem(wad), wad, "preview redeem"); assertEq(strategy.totalAssets(), 0, "total assets"); assertEq(strategy.totalSupply(), 0, "total supply"); - assertEq(strategy.unlockedShares(), 0, "unlocked shares"); + assertEq(strategy.unlockedShares(user), 0, "unlocked shares"); assertEq(strategy.asset(), address(asset), "asset"); - assertEq(strategy.apiVersion(), "3.0.4", "api"); - assertEq(strategy.MAX_FEE(), 5_000, "max fee"); - assertEq(strategy.fullProfitUnlockDate(), 0, "unlock date"); - assertEq(strategy.profitUnlockingRate(), 0, "unlock rate"); + assertEq(strategy.apiVersion(), "1.0.0", "api"); + // assertEq(strategy.fullProfitUnlockDate(), 0, "unlock date"); + // assertEq(strategy.profitUnlockingRate(), 0, "unlock rate"); assertGt(strategy.lastReport(), 0, "last report"); assertEq(strategy.pricePerShare(), 10 ** asset.decimals(), "pps"); assertTrue(!strategy.isShutdown()); - assertEq( - strategy.symbol(), - string(abi.encodePacked("ys", asset.symbol())), - "symbol" - ); + assertEq(strategy.symbol(), string(abi.encodePacked("dgn", asset.symbol())), "symbol"); assertEq(strategy.decimals(), asset.decimals(), "decimals"); // Assure modifiers are working @@ -58,20 +47,20 @@ contract FunctionSignatureTest is Setup { strategy.setKeeper(user); vm.expectRevert("!management"); strategy.setEmergencyAdmin(user); - vm.expectRevert("!management"); - strategy.setPerformanceFee(uint16(2_000)); - vm.expectRevert("!management"); - strategy.setPerformanceFeeRecipient(user); - vm.expectRevert("!management"); - strategy.setProfitMaxUnlockTime(1); + // vm.expectRevert("!management"); + // strategy.setPerformanceFee(uint16(2_000)); + // vm.expectRevert("!management"); + // strategy.setPerformanceFeeRecipient(user); + // vm.expectRevert("!management"); + // strategy.setProfitMaxUnlockTime(1); vm.stopPrank(); // Assure checks are being used vm.startPrank(strategy.management()); - vm.expectRevert("Cannot be self"); - strategy.setPerformanceFeeRecipient(address(strategy)); - vm.expectRevert("too long"); - strategy.setProfitMaxUnlockTime(type(uint256).max); + // vm.expectRevert("Cannot be self"); + // strategy.setPerformanceFeeRecipient(address(strategy)); + // vm.expectRevert("too long"); + // strategy.setProfitMaxUnlockTime(type(uint256).max); vm.stopPrank(); // Mint some shares to the user diff --git a/src/test/Operation.t.sol b/src/test/Operation.t.sol index 9a3f25be..d1b01df4 100644 --- a/src/test/Operation.t.sol +++ b/src/test/Operation.t.sol @@ -14,8 +14,9 @@ contract OperationTest is Setup { assertTrue(address(0) != address(strategy)); assertEq(strategy.asset(), address(asset)); assertEq(strategy.management(), management); - assertEq(strategy.performanceFeeRecipient(), performanceFeeRecipient); assertEq(strategy.keeper(), keeper); + assertEq(strategy.dragonRouter(), dragonRouter); + assertEq(strategy.name(), "Tokenized Strategy"); // TODO: add additional check on strat params } @@ -38,7 +39,7 @@ contract OperationTest is Setup { assertGe(profit, 0, "!profit"); assertEq(loss, 0, "!loss"); - skip(strategy.profitMaxUnlockTime()); + // skip(strategy.profitMaxUnlockTime()); uint256 balanceBefore = asset.balanceOf(user); @@ -46,17 +47,10 @@ contract OperationTest is Setup { vm.prank(user); strategy.redeem(_amount, user, user); - assertGe( - asset.balanceOf(user), - balanceBefore + _amount, - "!final balance" - ); + assertGe(asset.balanceOf(user), balanceBefore + _amount, "!final balance"); } - function test_profitableReport( - uint256 _amount, - uint16 _profitFactor - ) public { + function test_profitableReport(uint256 _amount, uint16 _profitFactor) public { vm.assume(_amount > minFuzzAmount && _amount < maxFuzzAmount); _profitFactor = uint16(bound(uint256(_profitFactor), 10, MAX_BPS)); @@ -80,7 +74,7 @@ contract OperationTest is Setup { assertGe(profit, toAirdrop, "!profit"); assertEq(loss, 0, "!loss"); - skip(strategy.profitMaxUnlockTime()); + // skip(strategy.profitMaxUnlockTime()); uint256 balanceBefore = asset.balanceOf(user); @@ -88,112 +82,93 @@ contract OperationTest is Setup { vm.prank(user); strategy.redeem(_amount, user, user); - assertGe( - asset.balanceOf(user), - balanceBefore + _amount, - "!final balance" - ); + assertGe(asset.balanceOf(user), balanceBefore + _amount, "!final balance"); } - function test_profitableReport_withFees( - uint256 _amount, - uint16 _profitFactor - ) public { - vm.assume(_amount > minFuzzAmount && _amount < maxFuzzAmount); - _profitFactor = uint16(bound(uint256(_profitFactor), 10, MAX_BPS)); + // function test_profitableReport_withFees(uint256 _amount, uint16 _profitFactor) public { + // vm.assume(_amount > minFuzzAmount && _amount < maxFuzzAmount); + // _profitFactor = uint16(bound(uint256(_profitFactor), 10, MAX_BPS)); - // Set protocol fee to 0 and perf fee to 10% - setFees(0, 1_000); + // // Set protocol fee to 0 and perf fee to 10% + // setFees(0, 1_000); - // Deposit into strategy - mintAndDepositIntoStrategy(strategy, user, _amount); + // // Deposit into strategy + // mintAndDepositIntoStrategy(strategy, user, _amount); - assertEq(strategy.totalAssets(), _amount, "!totalAssets"); + // assertEq(strategy.totalAssets(), _amount, "!totalAssets"); - // Earn Interest - skip(1 days); + // // Earn Interest + // skip(1 days); - // TODO: implement logic to simulate earning interest. - uint256 toAirdrop = (_amount * _profitFactor) / MAX_BPS; - airdrop(asset, address(strategy), toAirdrop); + // // TODO: implement logic to simulate earning interest. + // uint256 toAirdrop = (_amount * _profitFactor) / MAX_BPS; + // airdrop(asset, address(strategy), toAirdrop); - // Report profit - vm.prank(keeper); - (uint256 profit, uint256 loss) = strategy.report(); + // // Report profit + // vm.prank(keeper); + // (uint256 profit, uint256 loss) = strategy.report(); - // Check return Values - assertGe(profit, toAirdrop, "!profit"); - assertEq(loss, 0, "!loss"); + // // Check return Values + // assertGe(profit, toAirdrop, "!profit"); + // assertEq(loss, 0, "!loss"); - skip(strategy.profitMaxUnlockTime()); + // skip(strategy.profitMaxUnlockTime()); - // Get the expected fee - uint256 expectedShares = (profit * 1_000) / MAX_BPS; + // // Get the expected fee + // uint256 expectedShares = (profit * 1_000) / MAX_BPS; - assertEq(strategy.balanceOf(performanceFeeRecipient), expectedShares); + // assertEq(strategy.balanceOf(performanceFeeRecipient), expectedShares); - uint256 balanceBefore = asset.balanceOf(user); + // uint256 balanceBefore = asset.balanceOf(user); - // Withdraw all funds - vm.prank(user); - strategy.redeem(_amount, user, user); + // // Withdraw all funds + // vm.prank(user); + // strategy.redeem(_amount, user, user); - assertGe( - asset.balanceOf(user), - balanceBefore + _amount, - "!final balance" - ); - - vm.prank(performanceFeeRecipient); - strategy.redeem( - expectedShares, - performanceFeeRecipient, - performanceFeeRecipient - ); - - checkStrategyTotals(strategy, 0, 0, 0); - - assertGe( - asset.balanceOf(performanceFeeRecipient), - expectedShares, - "!perf fee out" - ); - } + // assertGe(asset.balanceOf(user), balanceBefore + _amount, "!final balance"); + + // vm.prank(performanceFeeRecipient); + // strategy.redeem(expectedShares, performanceFeeRecipient, performanceFeeRecipient); + + // checkStrategyTotals(strategy, 0, 0, 0); + + // assertGe(asset.balanceOf(performanceFeeRecipient), expectedShares, "!perf fee out"); + // } function test_tendTrigger(uint256 _amount) public { vm.assume(_amount > minFuzzAmount && _amount < maxFuzzAmount); - (bool trigger, ) = strategy.tendTrigger(); + (bool trigger,) = strategy.tendTrigger(); assertTrue(!trigger); // Deposit into strategy mintAndDepositIntoStrategy(strategy, user, _amount); - (trigger, ) = strategy.tendTrigger(); + (trigger,) = strategy.tendTrigger(); assertTrue(!trigger); // Skip some time skip(1 days); - (trigger, ) = strategy.tendTrigger(); + (trigger,) = strategy.tendTrigger(); assertTrue(!trigger); vm.prank(keeper); strategy.report(); - (trigger, ) = strategy.tendTrigger(); + (trigger,) = strategy.tendTrigger(); assertTrue(!trigger); // Unlock Profits - skip(strategy.profitMaxUnlockTime()); + // skip(strategy.profitMaxUnlockTime()); - (trigger, ) = strategy.tendTrigger(); + (trigger,) = strategy.tendTrigger(); assertTrue(!trigger); vm.prank(user); strategy.redeem(_amount, user, user); - (trigger, ) = strategy.tendTrigger(); + (trigger,) = strategy.tendTrigger(); assertTrue(!trigger); } } diff --git a/src/test/Oracle.t.sol b/src/test/Oracle.t.sol index f7c349c4..63d922b8 100644 --- a/src/test/Oracle.t.sol +++ b/src/test/Oracle.t.sol @@ -25,27 +25,27 @@ contract OracleTest is Setup { // TODO: Uncomment to test the apr goes up and down based on debt changes /** - uint256 negativeDebtChangeApr = oracle.aprAfterDebtChange(_strategy, -int256(_delta)); - - // The apr should go up if deposits go down - assertLt(currentApr, negativeDebtChangeApr, "negative change"); - - uint256 positiveDebtChangeApr = oracle.aprAfterDebtChange(_strategy, int256(_delta)); - - assertGt(currentApr, positiveDebtChangeApr, "positive change"); - */ + * uint256 negativeDebtChangeApr = oracle.aprAfterDebtChange(_strategy, -int256(_delta)); + * + * // The apr should go up if deposits go down + * assertLt(currentApr, negativeDebtChangeApr, "negative change"); + * + * uint256 positiveDebtChangeApr = oracle.aprAfterDebtChange(_strategy, int256(_delta)); + * + * assertGt(currentApr, positiveDebtChangeApr, "positive change"); + */ // TODO: Uncomment if there are setter functions to test. /** - vm.expectRevert("!governance"); - vm.prank(user); - oracle.setterFunction(setterVariable); - - vm.prank(management); - oracle.setterFunction(setterVariable); - - assertEq(oracle.setterVariable(), setterVariable); - */ + * vm.expectRevert("!governance"); + * vm.prank(user); + * oracle.setterFunction(setterVariable); + * + * vm.prank(management); + * oracle.setterFunction(setterVariable); + * + * assertEq(oracle.setterVariable(), setterVariable); + */ } function test_oracle(uint256 _amount, uint16 _percentChange) public { diff --git a/src/test/Shutdown.t.sol b/src/test/Shutdown.t.sol index 5e498b9f..851d89cd 100644 --- a/src/test/Shutdown.t.sol +++ b/src/test/Shutdown.t.sol @@ -32,11 +32,7 @@ contract ShutdownTest is Setup { vm.prank(user); strategy.redeem(_amount, user, user); - assertGe( - asset.balanceOf(user), - balanceBefore + _amount, - "!final balance" - ); + assertGe(asset.balanceOf(user), balanceBefore + _amount, "!final balance"); } function test_emergencyWithdraw_maxUint(uint256 _amount) public { @@ -67,11 +63,7 @@ contract ShutdownTest is Setup { vm.prank(user); strategy.redeem(_amount, user, user); - assertGe( - asset.balanceOf(user), - balanceBefore + _amount, - "!final balance" - ); + assertGe(asset.balanceOf(user), balanceBefore + _amount, "!final balance"); } // TODO: Add tests for any emergency function added. diff --git a/src/test/utils/ExtendedTest.sol b/src/test/utils/ExtendedTest.sol index 4dfd4f1f..a9314bcf 100644 --- a/src/test/utils/ExtendedTest.sol +++ b/src/test/utils/ExtendedTest.sol @@ -18,11 +18,7 @@ contract ExtendedTest is Test { // @a actual value // @b expected value // solhint-disable-next-line - function assertRelApproxEq( - uint256 a, - uint256 b, - uint256 maxPercentDelta - ) internal virtual { + function assertRelApproxEq(uint256 a, uint256 b, uint256 maxPercentDelta) internal virtual { uint256 delta = a > b ? a - b : b - a; uint256 maxRelDelta = b / maxPercentDelta; @@ -38,11 +34,7 @@ contract ExtendedTest is Test { // Can be removed once https://github.com/dapphub/ds-test/pull/25 is merged and we update submodules, but useful for now // solhint-disable-next-line - function assertApproxEq( - uint256 a, - uint256 b, - uint256 margin_of_error - ) internal { + function assertApproxEq(uint256 a, uint256 b, uint256 margin_of_error) internal { if (a > b) { if (a - b > margin_of_error) { emit log("Error a not equal to b"); @@ -61,12 +53,7 @@ contract ExtendedTest is Test { } // solhint-disable-next-line - function assertApproxEq( - uint256 a, - uint256 b, - uint256 margin_of_error, - string memory err - ) internal { + function assertApproxEq(uint256 a, uint256 b, uint256 margin_of_error, string memory err) internal { if (a > b) { if (a - b > margin_of_error) { emit log_named_string("Error", err); diff --git a/src/test/utils/Setup.sol b/src/test/utils/Setup.sol index 42042f1d..b5ccbb3a 100644 --- a/src/test/utils/Setup.sol +++ b/src/test/utils/Setup.sol @@ -9,7 +9,7 @@ import {StrategyFactory} from "../../StrategyFactory.sol"; import {IStrategyInterface} from "../../interfaces/IStrategyInterface.sol"; // Inherit the events so they can be checked if desired. -import {IEvents} from "@tokenized-strategy/interfaces/IEvents.sol"; +import {IEvents} from "octant-v2-core/src/interfaces/IEvents.sol"; interface IFactory { function governance() external view returns (address); @@ -29,11 +29,16 @@ contract Setup is ExtendedTest, IEvents { mapping(string => address) public tokenAddrs; // Addresses for different roles we will use repeatedly. - address public user = address(10); - address public keeper = address(4); - address public management = address(1); - address public performanceFeeRecipient = address(3); + address public user = address(1); + address public keeper = address(2); + address public management = address(3); + address public dragonRouter = address(4); address public emergencyAdmin = address(5); + address public yieldSource = address(6); + address public deployer = address(7); + address public owner = address(8); + address public tokenizedStrategyImplementation; + address public dragonTokenizedStrategyImplementation; // Address of the real deployed Factory address public factory; @@ -41,6 +46,7 @@ contract Setup is ExtendedTest, IEvents { // Integer variables that will be used repeatedly. uint256 public decimals; uint256 public MAX_BPS = 10_000; + uint256 public maxReportDelay = 1 days; // Fuzz from $0.01 of 1e6 stable coins up to 1 trillion of a 1e18 coin uint256 public maxFuzzAmount = 1e30; @@ -58,17 +64,14 @@ contract Setup is ExtendedTest, IEvents { // Set decimals decimals = asset.decimals(); - strategyFactory = new StrategyFactory( - management, - performanceFeeRecipient, - keeper, - emergencyAdmin - ); - + strategyFactory = new StrategyFactory(); + tokenizedStrategyImplementation = address(strategyFactory.strategyImplementation()); + dragonTokenizedStrategyImplementation = address(strategyFactory.dragonTokenizedStrategyImplementation()); // Deploy strategy and set variables strategy = IStrategyInterface(setUpStrategy()); - factory = strategy.FACTORY(); + // factory = strategy.FACTORY(); + factory = address(strategyFactory); // label all the used addresses for traces vm.label(keeper, "keeper"); @@ -76,31 +79,31 @@ contract Setup is ExtendedTest, IEvents { vm.label(address(asset), "asset"); vm.label(management, "management"); vm.label(address(strategy), "strategy"); - vm.label(performanceFeeRecipient, "performanceFeeRecipient"); + vm.label(dragonRouter, "dragonRouter"); } function setUpStrategy() public returns (address) { - // we save the strategy as a IStrategyInterface to give it the needed interface - IStrategyInterface _strategy = IStrategyInterface( - address( - strategyFactory.newStrategy( - address(asset), - "Tokenized Strategy" - ) + // Encode initialization parameters + bytes memory initParams = abi.encode( + owner, + abi.encode( + dragonTokenizedStrategyImplementation, + address(asset), + yieldSource, + management, + keeper, + dragonRouter, + maxReportDelay, + "Tokenized Strategy" ) ); - - vm.prank(management); - _strategy.acceptManagement(); + // we save the strategy as a IStrategyInterface to give it the needed interface + IStrategyInterface _strategy = IStrategyInterface(address(strategyFactory.newStrategy(initParams))); return address(_strategy); } - function depositIntoStrategy( - IStrategyInterface _strategy, - address _user, - uint256 _amount - ) public { + function depositIntoStrategy(IStrategyInterface _strategy, address _user, uint256 _amount) public { vm.prank(_user); asset.approve(address(_strategy), _amount); @@ -108,11 +111,7 @@ contract Setup is ExtendedTest, IEvents { _strategy.deposit(_amount, _user); } - function mintAndDepositIntoStrategy( - IStrategyInterface _strategy, - address _user, - uint256 _amount - ) public { + function mintAndDepositIntoStrategy(IStrategyInterface _strategy, address _user, uint256 _amount) public { airdrop(asset, _user, _amount); depositIntoStrategy(_strategy, _user, _amount); } @@ -125,9 +124,7 @@ contract Setup is ExtendedTest, IEvents { uint256 _totalIdle ) public { uint256 _assets = _strategy.totalAssets(); - uint256 _balance = ERC20(_strategy.asset()).balanceOf( - address(_strategy) - ); + uint256 _balance = ERC20(_strategy.asset()).balanceOf(address(_strategy)); uint256 _idle = _balance > _assets ? _assets : _balance; uint256 _debt = _assets - _idle; assertEq(_assets, _totalAssets, "!totalAssets"); @@ -141,19 +138,19 @@ contract Setup is ExtendedTest, IEvents { deal(address(_asset), _to, balanceBefore + _amount); } - function setFees(uint16 _protocolFee, uint16 _performanceFee) public { - address gov = IFactory(factory).governance(); + // function setFees(uint16 _protocolFee, uint16 _performanceFee) public { + // address gov = IFactory(factory).governance(); - // Need to make sure there is a protocol fee recipient to set the fee. - vm.prank(gov); - IFactory(factory).set_protocol_fee_recipient(gov); + // // Need to make sure there is a protocol fee recipient to set the fee. + // vm.prank(gov); + // IFactory(factory).set_protocol_fee_recipient(gov); - vm.prank(gov); - IFactory(factory).set_protocol_fee_bps(_protocolFee); + // vm.prank(gov); + // IFactory(factory).set_protocol_fee_bps(_protocolFee); - vm.prank(management); - strategy.setPerformanceFee(_performanceFee); - } + // vm.prank(management); + // strategy.setPerformanceFee(_performanceFee); + // } function _setTokenAddrs() internal { tokenAddrs["WBTC"] = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599;