Skip to content

Commit

Permalink
feat: update test
Browse files Browse the repository at this point in the history
  • Loading branch information
joelkrusala committed Jan 6, 2025
1 parent 3c8bcb4 commit abd9611
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 88 deletions.
6 changes: 0 additions & 6 deletions src/Strategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ contract Strategy is Module, BaseStrategy {
transferOwnership(_owner);
}

function initialize(address _asset, address _yieldSource) public {
require(yieldSource == address(0));
yieldSource = _yieldSource;
ERC20(_asset).approve(_yieldSource, type(uint256).max);
}

/*//////////////////////////////////////////////////////////////
NEEDED TO BE OVERRIDDEN BY STRATEGIST
//////////////////////////////////////////////////////////////*/
Expand Down
15 changes: 12 additions & 3 deletions src/StrategyFactory.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// 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";

Expand All @@ -12,20 +15,26 @@ contract StrategyFactory {
/// @notice Track the deployments. strategyId => strategy address
mapping(uint256 => address) public deployments;

address public strategyImplementation;
address public dragonTokenizedStrategyImplementation;
uint256 public strategyId;

constructor() {
strategyId = 0;
strategyImplementation = address(new Strategy());
dragonTokenizedStrategyImplementation = address(new DragonTokenizedStrategy());
}

/**
* @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(bytes calldata initializeParams) external virtual returns (address) {
IStrategyInterface _newStrategy = IStrategyInterface(address(new Strategy()));
_newStrategy.setUp(initializeParams);
function newStrategy(bytes memory initializeParams) external virtual returns (address) {
ERC1967Proxy _newStrategy = new ERC1967Proxy(
strategyImplementation,
abi.encodeWithSelector(Strategy(payable(address(0))).setUp.selector, initializeParams)
);

uint256 currentId = strategyId;
deployments[currentId] = address(_newStrategy);
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IStrategyInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ pragma solidity ^0.8.18;
import {IStrategy} from "octant-v2-core/src/interfaces/IStrategy.sol";

interface IStrategyInterface is IStrategy {
function setUp(bytes calldata initializeParams) external;
function setUp(bytes memory initializeParams) external;
}
35 changes: 17 additions & 18 deletions src/test/FunctionSignature.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +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");
Expand All @@ -26,16 +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
Expand All @@ -48,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
Expand Down
75 changes: 38 additions & 37 deletions src/test/Operation.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

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

Expand Down Expand Up @@ -73,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);

Expand All @@ -84,55 +85,55 @@ contract OperationTest is Setup {
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");
// assertGe(asset.balanceOf(user), balanceBefore + _amount, "!final balance");

vm.prank(performanceFeeRecipient);
strategy.redeem(expectedShares, performanceFeeRecipient, performanceFeeRecipient);
// vm.prank(performanceFeeRecipient);
// strategy.redeem(expectedShares, performanceFeeRecipient, performanceFeeRecipient);

checkStrategyTotals(strategy, 0, 0, 0);
// checkStrategyTotals(strategy, 0, 0, 0);

assertGe(asset.balanceOf(performanceFeeRecipient), expectedShares, "!perf fee out");
}
// assertGe(asset.balanceOf(performanceFeeRecipient), expectedShares, "!perf fee out");
// }

function test_tendTrigger(uint256 _amount) public {
vm.assume(_amount > minFuzzAmount && _amount < maxFuzzAmount);
Expand All @@ -159,7 +160,7 @@ contract OperationTest is Setup {
assertTrue(!trigger);

// Unlock Profits
skip(strategy.profitMaxUnlockTime());
// skip(strategy.profitMaxUnlockTime());

(trigger,) = strategy.tendTrigger();
assertTrue(!trigger);
Expand Down
64 changes: 41 additions & 23 deletions src/test/utils/Setup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,24 @@ 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;

// 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;
Expand All @@ -58,29 +64,41 @@ 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");
vm.label(factory, "factory");
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) {
// Encode initialization parameters
bytes memory initParams = abi.encode(
owner,
abi.encode(
dragonTokenizedStrategyImplementation,
address(asset),
yieldSource,
management,
keeper,
dragonRouter,
maxReportDelay,
"Tokenized Strategy"
)
);
// we save the strategy as a IStrategyInterface to give it the needed interface
IStrategyInterface _strategy =
IStrategyInterface(address(strategyFactory.newStrategy(address(asset), "Tokenized Strategy")));

vm.prank(management);
_strategy.acceptManagement();
IStrategyInterface _strategy = IStrategyInterface(address(strategyFactory.newStrategy(initParams)));

return address(_strategy);
}
Expand Down Expand Up @@ -120,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;
Expand Down

0 comments on commit abd9611

Please sign in to comment.