Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build: update to 303 #40

Merged
merged 4 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
branch = v3.0.3
[submodule "lib/tokenized-strategy-periphery"]
path = lib/tokenized-strategy-periphery
url = https://github.com/yearn/tokenized-strategy-periphery
branch = master
80 changes: 80 additions & 0 deletions src/StrategyFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;

import {Strategy, ERC20} from "./Strategy.sol";

Check warning on line 4 in src/StrategyFactory.sol

View workflow job for this annotation

GitHub Actions / solidity

imported name ERC20 is not used
import {IStrategyInterface} from "./interfaces/IStrategyInterface.sol";

contract StrategyFactory {
event NewStrategy(address indexed strategy, address indexed asset);

address public immutable emergencyAdmin;

Check warning on line 10 in src/StrategyFactory.sol

View workflow job for this annotation

GitHub Actions / solidity

Immutable variables name are set to be in capitalized SNAKE_CASE

address public immutable lendingPool;

Check warning on line 12 in src/StrategyFactory.sol

View workflow job for this annotation

GitHub Actions / solidity

Immutable variables name are set to be in capitalized SNAKE_CASE
address public immutable router;

Check warning on line 13 in src/StrategyFactory.sol

View workflow job for this annotation

GitHub Actions / solidity

Immutable variables name are set to be in capitalized SNAKE_CASE
address public immutable base;

Check warning on line 14 in src/StrategyFactory.sol

View workflow job for this annotation

GitHub Actions / solidity

Immutable variables name are set to be in capitalized SNAKE_CASE

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;
}
}
2 changes: 1 addition & 1 deletion src/test/FunctionSignature.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
37 changes: 36 additions & 1 deletion src/test/Shutdown.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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.
}
25 changes: 17 additions & 8 deletions src/test/utils/Setup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -23,13 +24,16 @@ 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.
address public user = address(10);
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;
Expand All @@ -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());

Expand All @@ -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();

Expand Down
Loading