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

feat: 302 #27

Merged
merged 8 commits into from
Mar 6, 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
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Setup node.js
uses: actions/setup-node@v1
with:
node-version: '16.x'
node-version: 18

- name: Set yarn cache directory path
id: yarn-cache-dir-path
Expand Down
11 changes: 5 additions & 6 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
branch = v1.5.3
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
branch = v4.8.2
[submodule "lib/tokenized-strategy"]
path = lib/tokenized-strategy
url = https://github.com/yearn/tokenized-strategy
branch = v3.0.1
release = v3.0.2
[submodule "lib/tokenized-strategy-periphery"]
path = lib/tokenized-strategy-periphery
url = https://github.com/yearn/tokenized-strategy-periphery
branch = master
release = v3.0.2
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This repo will allow you to write, test and deploy V3 "Tokenized Strategies" using [Foundry](https://book.getfoundry.sh/).

You will only need to override the three functions in Strategy.sol of `_deployFunds`, `_freeFunds` and `_harvestAndReport`. With the option to also override `_tend`, `_tendTrigger`, `availableDepositLimit`, `availableWithdrawLimit` and `_emegencyWithdraw` if desired.
You will only need to override the three functions in Strategy.sol of `_deployFunds`, `_freeFunds` and `_harvestAndReport`. With the option to also override `_tend`, `_tendTrigger`, `availableDepositLimit`, `availableWithdrawLimit` and `_emergencyWithdraw` if desired.

For a more complete overview of how the Tokenized Strategies work please visit the [TokenizedStrategy Repo](https://github.com/yearn/tokenized-strategy).

Expand All @@ -13,10 +13,10 @@ For a more complete overview of how the Tokenized Strategies work please visit t
First you will need to install [Foundry](https://book.getfoundry.sh/getting-started/installation).
NOTE: If you are on a windows machine it is recommended to use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install)

### Fork this repository
### Clone this repository

```sh
git clone --recursive https://github.com/user/tokenized-strategy-foundry-mix
git clone --recursive https://github.com/yearn/tokenized-strategy-foundry-mix

cd tokenized-strategy-foundry-mix

Expand Down Expand Up @@ -52,7 +52,7 @@ For a complete guide to creating a Tokenized Strategy please visit: https://docs

## Testing

Due to the nature of the BaseStrategy utilizing an external contract for the majority of its logic, the default interface for any tokenized strategy will not allow proper testing of all functions. Testing of your Strategy should utilize the pre-built [IStrategyInterface](https://github.com/Schlagonia/tokenized-strategy-foundry-mix/blob/master/src/interfaces/IStrategyInterface.sol) to cast any deployed strategy through for testing, as seen in the Setup example. You can add any external functions that you add for your specific strategy to this interface to be able to test all functions with one variable.
Due to the nature of the BaseStrategy utilizing an external contract for the majority of its logic, the default interface for any tokenized strategy will not allow proper testing of all functions. Testing of your Strategy should utilize the pre-built [IStrategyInterface](https://github.com/yearn/tokenized-strategy-foundry-mix/blob/master/src/interfaces/IStrategyInterface.sol) to cast any deployed strategy through for testing, as seen in the Setup example. You can add any external functions that you add for your specific strategy to this interface to be able to test all functions with one variable.

Example:

Expand Down Expand Up @@ -106,8 +106,6 @@ Once the Strategy is fully deployed and verified, you will need to verify the To

This should add all of the external `TokenizedStrategy` functions to the contract interface on Etherscan.

See the ApeWorx [documentation](https://docs.apeworx.io/ape/stable/) and [GitHub](https://github.com/ApeWorX/ape) for more information.

## CI

This repo uses [GitHub Actions](.github/workflows) for CI. There are three workflows: lint, test and slither for static analysis.
Expand Down
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
4 changes: 0 additions & 4 deletions remappings.txt

This file was deleted.

18 changes: 8 additions & 10 deletions src/Strategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,26 @@
//////////////////////////////////////////////////////////////*/

/**
* @dev Should deploy up to '_amount' of 'asset' in the yield source.
* @dev Can deploy up to '_amount' of 'asset' in the yield source.
*
* This function is called at the end of a {deposit} or {mint}
* call. Meaning that unless a whitelist is implemented it will
* be entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* @param _amount The amount of 'asset' that the strategy should attempt
* @param _amount The amount of 'asset' that the strategy can attempt
* to deposit in the yield source.
*/
function _deployFunds(uint256 _amount) internal override {

Check warning on line 46 in src/Strategy.sol

View workflow job for this annotation

GitHub Actions / solidity

Code contains empty blocks
// TODO: implement deposit logic EX:
//
// lendingPool.deposit(address(asset), _amount ,0);
}

/**
* @dev Will attempt to free the '_amount' of 'asset'.
* @dev Should attempt to free the '_amount' of 'asset'.
*
* The amount of 'asset' that is already loose has already
* NOTE: The amount of 'asset' that is already loose has already
* been accounted for.
*
* This function is called during {withdraw} and {redeem} calls.
Expand All @@ -70,7 +70,7 @@
*
* @param _amount, The amount of 'asset' to be freed.
*/
function _freeFunds(uint256 _amount) internal override {

Check warning on line 73 in src/Strategy.sol

View workflow job for this annotation

GitHub Actions / solidity

Code contains empty blocks
// TODO: implement withdraw logic EX:
//
// lendingPool.withdraw(address(asset), _amount);
Expand Down Expand Up @@ -134,9 +134,7 @@
* sandwiched can use the tend when a certain threshold
* of idle to totalAssets has been reached.
*
* The TokenizedStrategy contract will do all needed debt and idle updates
* after this has finished and will have no effect on PPS of the strategy
* till report() is called.
* This will have no effect on PPS of the strategy till report() is called.
*
* @param _totalIdle The current amount of idle funds that are available to deploy.
*
Expand Down Expand Up @@ -191,10 +189,10 @@
*
* This function will be called before any withdraw or redeem to enforce
* any limits desired by the strategist. This can be used for illiquid
* or sandwichable strategies. It should never be lower than `totalIdle`.
* or sandwichable strategies.
*
* EX:
* return TokenIzedStrategy.totalIdle();
* return asset.balanceOf(address(this));;
*
* This does not need to take into account the `_owner`'s share balance
* or conversion rates from shares to assets.
Expand All @@ -208,7 +206,7 @@
TODO: If desired Implement withdraw limit logic and any needed state variables.

EX:
return TokenizedStrategy.totalIdle();
return asset.balanceOf(address(this));
}
*/

Expand Down
9 changes: 2 additions & 7 deletions src/test/FunctionSignature.t.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "forge-std/console.sol";

Check warning on line 4 in src/test/FunctionSignature.t.sol

View workflow job for this annotation

GitHub Actions / solidity

global import of path forge-std/console.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import {Setup, ERC20, IStrategyInterface} from "./utils/Setup.sol";

Check warning on line 5 in src/test/FunctionSignature.t.sol

View workflow job for this annotation

GitHub Actions / solidity

imported name IStrategyInterface is not used

contract FunctionSignatureTest is Setup {
function setUp() public virtual override {
Expand All @@ -12,10 +12,10 @@
// This test should not be overridden and checks that
// no function signature collisions occurred from the custom functions.
// Does not check functions that are strategy dependant and will be checked in other tests
function test_functionCollisions() public {

Check warning on line 15 in src/test/FunctionSignature.t.sol

View workflow job for this annotation

GitHub Actions / solidity

Function body contains 76 lines but allowed no more than 50 lines

Check warning on line 15 in src/test/FunctionSignature.t.sol

View workflow job for this annotation

GitHub Actions / solidity

Function name must be in mixedCase
uint256 wad = 1e18;
vm.expectRevert("initialized");
strategy.init(
strategy.initialize(
address(asset),
"name",
management,
Expand All @@ -34,11 +34,8 @@
assertEq(strategy.totalSupply(), 0, "total supply");
assertEq(strategy.unlockedShares(), 0, "unlocked shares");
assertEq(strategy.asset(), address(asset), "asset");
assertEq(strategy.apiVersion(), "3.0.1", "api");
assertEq(strategy.totalIdle(), 0, "idle");
assertEq(strategy.totalDebt(), 0, "debt");
assertEq(strategy.apiVersion(), "3.0.2", "api");
assertEq(strategy.MAX_FEE(), 5_000, "max fee");
assertEq(strategy.MIN_FEE(), 500, "min fee");
assertEq(strategy.fullProfitUnlockDate(), 0, "unlock date");
assertEq(strategy.profitUnlockingRate(), 0, "unlock rate");
assertGt(strategy.lastReport(), 0, "last report");
Expand Down Expand Up @@ -71,8 +68,6 @@

// Assure checks are being used
vm.startPrank(strategy.management());
vm.expectRevert("MIN FEE");
strategy.setPerformanceFee(uint16(0));
vm.expectRevert("Cannot be self");
strategy.setPerformanceFeeRecipient(address(strategy));
vm.expectRevert("too long");
Expand Down
9 changes: 0 additions & 9 deletions src/test/Operation.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import "forge-std/console.sol";

Check warning on line 4 in src/test/Operation.t.sol

View workflow job for this annotation

GitHub Actions / solidity

global import of path forge-std/console.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import {Setup, ERC20, IStrategyInterface} from "./utils/Setup.sol";

contract OperationTest is Setup {
Expand All @@ -25,10 +25,7 @@
// Deposit into strategy
mintAndDepositIntoStrategy(strategy, user, _amount);

// TODO: Implement logic so totalDebt is _amount and totalIdle = 0.
assertEq(strategy.totalAssets(), _amount, "!totalAssets");
assertEq(strategy.totalDebt(), 0, "!totalDebt");
assertEq(strategy.totalIdle(), _amount, "!totalIdle");

// Earn Interest
skip(1 days);
Expand Down Expand Up @@ -66,10 +63,7 @@
// Deposit into strategy
mintAndDepositIntoStrategy(strategy, user, _amount);

// TODO: Implement logic so totalDebt is _amount and totalIdle = 0.
assertEq(strategy.totalAssets(), _amount, "!totalAssets");
assertEq(strategy.totalDebt(), 0, "!totalDebt");
assertEq(strategy.totalIdle(), _amount, "!totalIdle");

// Earn Interest
skip(1 days);
Expand Down Expand Up @@ -114,10 +108,7 @@
// Deposit into strategy
mintAndDepositIntoStrategy(strategy, user, _amount);

// TODO: Implement logic so totalDebt is _amount and totalIdle = 0.
assertEq(strategy.totalAssets(), _amount, "!totalAssets");
assertEq(strategy.totalDebt(), 0, "!totalDebt");
assertEq(strategy.totalIdle(), _amount, "!totalIdle");

// Earn Interest
skip(1 days);
Expand Down
6 changes: 0 additions & 6 deletions src/test/Shutdown.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ contract ShutdownTest is Setup {
// Deposit into strategy
mintAndDepositIntoStrategy(strategy, user, _amount);

// TODO: Implement logic so totalDebt is _amount and totalIdle = 0.
assertEq(strategy.totalAssets(), _amount, "!totalAssets");
assertEq(strategy.totalDebt(), 0, "!totalDebt");
assertEq(strategy.totalIdle(), _amount, "!totalIdle");

// Earn Interest
skip(1 days);
Expand All @@ -26,10 +23,7 @@ contract ShutdownTest is Setup {
vm.prank(management);
strategy.shutdownStrategy();

// TODO: Implement logic so totalDebt is _amount and totalIdle = 0.
assertEq(strategy.totalAssets(), _amount, "!totalAssets");
assertEq(strategy.totalDebt(), 0, "!totalDebt");
assertEq(strategy.totalIdle(), _amount, "!totalIdle");

// Make sure we can still withdraw the full amount
uint256 balanceBefore = asset.balanceOf(user);
Expand Down
12 changes: 9 additions & 3 deletions src/test/utils/Setup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,15 @@ contract Setup is ExtendedTest, IEvents {
uint256 _totalDebt,
uint256 _totalIdle
) public {
assertEq(_strategy.totalAssets(), _totalAssets, "!totalAssets");
assertEq(_strategy.totalDebt(), _totalDebt, "!totalDebt");
assertEq(_strategy.totalIdle(), _totalIdle, "!totalIdle");
uint256 _assets = _strategy.totalAssets();
uint256 _balance = ERC20(_strategy.asset()).balanceOf(
address(_strategy)
);
uint256 _idle = _balance > _assets ? _assets : _balance;
uint256 _debt = _assets - _idle;
assertEq(_assets, _totalAssets, "!totalAssets");
assertEq(_debt, _totalDebt, "!totalDebt");
assertEq(_idle, _totalIdle, "!totalIdle");
assertEq(_totalAssets, _totalDebt + _totalIdle, "!Added");
}

Expand Down
Loading