Skip to content

Commit

Permalink
test: add tokenized strategy tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Schlagonia committed Oct 17, 2023
1 parent 45b5d6b commit cc77711
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 2 deletions.
5 changes: 5 additions & 0 deletions ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ dependencies:
- name: openzeppelin
github: OpenZeppelin/openzeppelin-contracts
version: 4.7.3
- name: tokenized-strategy
github: yearn/tokenized-strategy
branch: master
contracts_folder: src

solidity:
import_remapping:
- "@openzeppelin/contracts=openzeppelin/v4.7.3"
- "@tokenized-strategy=tokenized-strategy/master"

ethereum:
local:
Expand Down
5 changes: 5 additions & 0 deletions contracts/test/ERC4626BaseStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ abstract contract ERC4626BaseStrategy is ERC4626 {

address public vault;
uint8 private _decimals;
address public keeper;

constructor(
address _vault,
Expand Down Expand Up @@ -58,4 +59,8 @@ abstract contract ERC4626BaseStrategy is ERC4626 {
) internal virtual returns (uint256 amountFreed);

function sweep(address _token) external {}

function report() external virtual returns (uint256, uint256) {
return (0, 0);
}
}
81 changes: 81 additions & 0 deletions contracts/test/mocks/ERC4626/MockTokenizedStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;

import {TokenizedStrategy, ERC20} from "@tokenized-strategy/TokenizedStrategy.sol";

contract MockTokenizedStrategy is TokenizedStrategy {
uint256 public minDebt;
uint256 public maxDebt = type(uint256).max;

// Private variables and functions used in this mock.
bytes32 public constant BASE_STRATEGY_STORAGE =
bytes32(uint256(keccak256("yearn.base.strategy.storage")) - 1);

function strategyStorage() internal pure returns (StrategyData storage S) {
// Since STORAGE_SLOT is a constant, we have to put a variable
// on the stack to access it from an inline assembly block.
bytes32 slot = BASE_STRATEGY_STORAGE;
assembly {
S.slot := slot
}
}

constructor(
address _asset,
string memory _name,
address _management,
address _keeper
) {
// Cache storage pointer
StrategyData storage S = strategyStorage();

// Set the strategy's underlying asset
S.asset = ERC20(_asset);
// Set the Strategy Tokens name.
S.name = _name;
// Set decimals based off the `asset`.
S.decimals = ERC20(_asset).decimals();

// Default to an instant profit unlock period
S.profitMaxUnlockTime = 0;
// set to a 0% performance fee.
S.performanceFee = 0;
// Set last report to this block.
S.lastReport = uint128(block.timestamp);

// Set the default management address. Can't be 0.
require(_management != address(0), "ZERO ADDRESS");
S.management = _management;
S.performanceFeeRecipient = _management;
// Set the keeper address
S.keeper = _keeper;
}

function setMinDebt(uint256 _minDebt) external {
minDebt = _minDebt;
}

function setMaxDebt(uint256 _maxDebt) external {
maxDebt = _maxDebt;
}

function availableDepositLimit(address) public view returns (uint256) {
uint256 _totalAssets = strategyStorage().totalIdle;
uint256 _maxDebt = maxDebt;
return _maxDebt > _totalAssets ? _maxDebt - _totalAssets : 0;
}

function availableWithdrawLimit(
address /*_owner*/
) public view returns (uint256) {
return type(uint256).max;
}

function deployFunds(uint256 _amount) external {}

function freeFunds(uint256 _amount) external {}

function harvestAndReport() external returns (uint256) {
return strategyStorage().asset.balanceOf(address(this));
}
}
10 changes: 8 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,15 @@ def create_vault(

# create default liquid strategy with 0 fee
@pytest.fixture(scope="session")
def create_strategy(project, strategist):
def create_strategy(project, strategist, gov):
def create_strategy(vault):
return strategist.deploy(project.ERC4626LiquidStrategy, vault, vault.asset())
return strategist.deploy(
project.MockTokenizedStrategy,
vault.asset(),
"Mock Tokenized Strategy",
strategist,
gov,
)

yield create_strategy

Expand Down
3 changes: 3 additions & 0 deletions tests/unit/vault/test_profit_unlocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def create_and_check_profit(
# We create a virtual profit
initial_debt = vault.strategies(strategy).current_debt
asset.transfer(strategy, profit, sender=gov)
# Record profits at the strategy level.
strategy.report(sender=gov)
tx = vault.process_report(strategy, sender=gov)
event = list(tx.decode_logs(vault.StrategyReported))

Expand Down Expand Up @@ -1199,6 +1201,7 @@ def test_gain_fees_no_refunds_not_enough_buffer(
assert accountant.fees(strategy).performance_fee == second_performance_fee

asset.transfer(strategy, second_profit, sender=gov)
strategy.report(sender=gov)

price_per_share_before_2nd_profit = vault.pricePerShare() / 10 ** vault.decimals()
accountant_shares_before_2nd_profit = vault.balanceOf(accountant)
Expand Down
1 change: 1 addition & 0 deletions tests/unit/vault/test_protocol_fees.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def test__report_gain_with_protocol_fees__accountant_fees(

# Create a profit
airdrop_asset(gov, asset, strategy, profit)
strategy.report(sender=gov)

expected_accountant_fee = profit * performance_fee / MAX_BPS_ACCOUNTANT
expected_protocol_fee = expected_accountant_fee * protocol_fee / MAX_BPS_ACCOUNTANT
Expand Down
1 change: 1 addition & 0 deletions tests/unit/vault/test_role_base_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ def test_process_report__reporting_manager(
add_debt_to_strategy(gov, strategy, vault, 2)
# airdrop gain to strategy
airdrop_asset(gov, asset, strategy, 1)
strategy.report(sender=gov)

tx = vault.process_report(strategy.address, sender=bunny)

Expand Down
4 changes: 4 additions & 0 deletions tests/unit/vault/test_role_permissioned_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ def test_process_report__set_reporting_role_open(
assert event[0].status == RoleStatusChange.OPENED

asset.mint(new_strategy, fish_amount, sender=gov)
new_strategy.report(sender=gov)

tx = vault.process_report(new_strategy, sender=bunny)
event = list(tx.decode_logs(vault.StrategyReported))
assert len(event) == 1
Expand Down Expand Up @@ -267,6 +269,8 @@ def test_process_report__set_reporting_role_open_then_close__reverts(
assert event[0].status == RoleStatusChange.OPENED

asset.mint(new_strategy, fish_amount, sender=gov)
new_strategy.report(sender=gov)

tx = vault.process_report(new_strategy, sender=bunny)
event = list(tx.decode_logs(vault.StrategyReported))
assert len(event) == 1
Expand Down
1 change: 1 addition & 0 deletions tests/unit/vault/test_shares.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ def create_profit(
# We create a virtual profit
initial_debt = vault.strategies(strategy).current_debt
asset.transfer(strategy, profit, sender=gov)
strategy.report(sender=gov)
tx = vault.process_report(strategy, sender=gov)
event = list(tx.decode_logs(vault.StrategyReported))

Expand Down
10 changes: 10 additions & 0 deletions tests/unit/vault/test_strategy_accounting.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ def test_process_report__with_gain_and_zero_fees(
# add debt to strategy
add_debt_to_strategy(gov, strategy, vault, new_debt)
airdrop_asset(gov, asset, strategy, gain)
# record gain
strategy.report(sender=gov)

strategy_params = vault.strategies(strategy.address)
initial_debt = strategy_params.current_debt
Expand Down Expand Up @@ -87,6 +89,8 @@ def test_process_report__with_gain_and_zero_management_fees(
add_debt_to_strategy(gov, strategy, vault, new_debt)
# airdrop gain to strategy
airdrop_asset(gov, asset, strategy, gain)
# record gain
strategy.report(sender=gov)
# set up accountant
set_fees_for_strategy(gov, strategy, accountant, management_fee, performance_fee)

Expand Down Expand Up @@ -145,6 +149,8 @@ def test_process_report__with_gain_and_zero_performance_fees(
add_debt_to_strategy(gov, strategy, vault, new_debt)
# airdrop gain to strategy
airdrop_asset(gov, asset, strategy, gain)
# record gain
strategy.report(sender=gov)
# set up accountant
set_fees_for_strategy(gov, strategy, accountant, management_fee, performance_fee)

Expand Down Expand Up @@ -202,6 +208,8 @@ def test_process_report__with_gain_and_both_fees(
add_debt_to_strategy(gov, strategy, vault, new_debt)
# airdrop gain to strategy
airdrop_asset(gov, asset, strategy, gain)
# record gain
strategy.report(sender=gov)
# set up accountant
set_fees_for_strategy(gov, strategy, accountant, management_fee, performance_fee)

Expand Down Expand Up @@ -256,6 +264,8 @@ def test_process_report__with_fees_exceeding_fee_cap(
add_debt_to_strategy(gov, strategy, vault, new_debt)
# airdrop gain to strategy
airdrop_asset(gov, asset, strategy, gain)
# record gain
strategy.report(sender=gov)
# set up accountant
set_fees_for_strategy(gov, strategy, accountant, management_fee, performance_fee)

Expand Down
1 change: 1 addition & 0 deletions tests/unit/vault/test_strategy_withdraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -1994,6 +1994,7 @@ def test_withdraw__with_multiple_liquid_strategies_more_assets_than_debt__withdr

airdrop_asset(gov, asset, gov, fish_amount)
asset.transfer(first_strategy, profit, sender=gov)
first_strategy.report(sender=gov)

tx = vault.withdraw(
shares,
Expand Down

0 comments on commit cc77711

Please sign in to comment.