Skip to content

Commit

Permalink
test: yield manager
Browse files Browse the repository at this point in the history
  • Loading branch information
Schlagonia committed Dec 12, 2023
1 parent 4552258 commit 7856970
Show file tree
Hide file tree
Showing 7 changed files with 1,197 additions and 57 deletions.
6 changes: 6 additions & 0 deletions contracts/Mocks/MockOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;

import {AprOracle} from "@periphery/AprOracle/AprOracle.sol";

contract MockOracle is AprOracle {}
36 changes: 36 additions & 0 deletions contracts/Mocks/MockTokenizedStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.18;

import {MockTokenizedStrategy} from "@yearn-vaults/test/mocks/ERC4626/MockTokenizedStrategy.sol";

contract MockTokenized is MockTokenizedStrategy {
uint256 public apr;
uint256 public loss;

constructor(
address _asset,
string memory _name,
address _management,
address _keeper,
uint256 _apr
) MockTokenizedStrategy(_asset, _name, _management, _keeper) {
apr = _apr;
}

function aprAfterDebtChange(
address,
int256
) external view returns (uint256) {
return apr;
}

function setApr(uint256 _apr) external {
apr = _apr;
}

function realizeLoss(uint256 _amount) external {
strategyStorage().asset.transfer(msg.sender, _amount);
strategyStorage().totalIdle -= _amount;
strategyStorage().totalDebt += _amount;
}
}
138 changes: 125 additions & 13 deletions contracts/debtAllocators/YieldManager/StrategyManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,44 @@ pragma solidity 0.8.18;

import {IStrategy} from "@tokenized-strategy/interfaces/IStrategy.sol";

// Allow for an array of calls

/// @notice Holds the `management` role of a V3 strategy so that a
/// debt allocator can call both reports and change the profit unlock time.
contract StrategyManager {
/// @notice Emitted when a new strategy is added to the manager.
event StrategyAdded(
address indexed strategy,
address indexed owner,
address indexed debtManager
);

/// @notice Emitted when a strategy is removed.
event StrategyRemoved(address indexed strategy, address indexed newManager);

/// @notice holds info for a strategy that is managed.
struct StrategyInfo {
bool active;
address owner;
address debtManager;
}

/// @notice Only the `_strategy` specific owner can call.
modifier onlyStrategyOwner(address _strategy) {
_checkStrategyManager(_strategy);
_checkStrategyOwner(_strategy);
_;
}

/// @notice Only the `_strategy` owner of its debt manager can call.
modifier onlyStrategyOwnerOrDebtManager(address _strategy) {
_checkStrategyDebtManager(_strategy);
_;
}

/// @notice Checks if the msg sender is the governance.
function _checkStrategyManager(address _strategy) internal view virtual {
require(strategyInfo[_strategy].owner == msg.sender, "!governance");
/// @notice Checks if the msg sender is the owner of the strategy.
function _checkStrategyOwner(address _strategy) internal view virtual {
require(strategyInfo[_strategy].owner == msg.sender, "!owner");
}

/// @notice Checks if the msg sender is the debt manager or the strategy owner.
function _checkStrategyDebtManager(
address _strategy
) internal view virtual {
Expand All @@ -37,16 +51,27 @@ contract StrategyManager {
);
}

/// @notice strategy address => struct with info.
mapping(address => StrategyInfo) public strategyInfo;

/// @notice function selector => bool if a debt manager can call that.
mapping(bytes4 => bool) public allowedSelectors;

/**
* @notice Add any of the allowed selectors for a debt manager to call
* to the mapping.
*/
constructor(bytes4[] memory _allowedSelectors) {
for (uint256 i = 0; i < _allowedSelectors.length; ++i) {
allowedSelectors[_allowedSelectors[i]];
allowedSelectors[_allowedSelectors[i]] = true;
}
}

/**
* @notice Manage a new strategy, setting the debt manager and marking it as active.
* @param _strategy The address of the strategy.
* @param _debtManager The address of the debt manager.
*/
function manageNewStrategy(
address _strategy,
address _debtManager
Expand All @@ -64,28 +89,75 @@ contract StrategyManager {
owner: currentManager,
debtManager: _debtManager
});

emit StrategyAdded(_strategy, currentManager, _debtManager);
}

/**
* @notice Updates the owner of a strategy.
* @param _strategy The address of the strategy.
* @param _newOwner The address of the new owner.
*/
function updateStrategyOwner(
address _strategy,
address _newOwner
) external onlyStrategyOwner(_strategy) {
require(_newOwner != address(0), "ZERO ADDRESS");
require(
_newOwner != address(0) &&
_newOwner != address(this) &&
_newOwner != _strategy,
"bad address"
);
strategyInfo[_strategy].owner = _newOwner;
}

// This gets rid of the benefits of two step transfers.
/**
* @notice Updates the debt manager of a strategy.
* @param _strategy The address of the strategy.
* @param _newDebtManager The address of the new owner.
*/
function updateDebtManager(
address _strategy,
address _newDebtManager
) external onlyStrategyOwner(_strategy) {
strategyInfo[_strategy].debtManager = _newDebtManager;
}

/**
* @notice Removes the management of a strategy, transferring it to the `owner`.
* @param _strategy The address of the strategy.
*/
function removeManagement(address _strategy) external {
removeManagement(_strategy, msg.sender);
}

/**
* @notice Removes the management of a strategy, transferring it to a new manager.
* @param _strategy The address of the strategy.
* @param _newManager The address of the new manager.
*/
function removeManagement(
address _strategy,
address _newManager
) external onlyStrategyOwner(_strategy) {
require(strategyInfo[_strategy].active, "not active");
) public onlyStrategyOwner(_strategy) {
require(
_newManager != address(0) &&
_newManager != address(this) &&
_newManager != _strategy,
"bad address"
);

delete strategyInfo[_strategy];

IStrategy(_strategy).setPendingManagement(_newManager);

emit StrategyRemoved(_strategy, _newManager);
}

/**
* @notice Reports full profit for a strategy.
* @param _strategy The address of the strategy.
*/
function reportFullProfit(
address _strategy
) external onlyStrategyOwnerOrDebtManager(_strategy) {
Expand All @@ -106,6 +178,12 @@ contract StrategyManager {
}
}

/**
* @notice Forwards multiple calls to a strategy.
* @param _strategy The address of the strategy.
* @param _calldataArray An array of calldata for each call.
* @return _returnData An array of return data from each call.
*/
function forwardCalls(
address _strategy,
bytes[] memory _calldataArray
Expand All @@ -117,21 +195,27 @@ contract StrategyManager {
}
}

/**
* @notice Forwards a single call to a strategy.
* @param _strategy The address of the strategy.
* @param _calldata The calldata for the call.
* @return _returnData The return data from the call.
*/
function forwardCall(
address _strategy,
bytes memory _calldata
) public returns (bytes memory) {
bytes4 selector;

assembly {
// Copy the first 4 bytes of the memory array to the result variable
// Copy the first 4 bytes of the memory array to the selector variable
selector := mload(add(_calldata, 32))
}

if (allowedSelectors[selector]) {
_checkStrategyDebtManager(_strategy);
} else {
_checkStrategyManager(_strategy);
_checkStrategyOwner(_strategy);
}

(bool success, bytes memory result) = _strategy.call(_calldata);
Expand All @@ -149,4 +233,32 @@ contract StrategyManager {
// Return the result.
return result;
}

/**
* @notice Calls any target contract as the manager of the strategy.
* @param _strategy The address of the strategy.
* @param _target The address of the target contract.
* @param _calldata The calldata for the call.
* @return result The return data from the call.
*/
function genericCall(
address _strategy,
address _target,
bytes memory _calldata
) external virtual onlyStrategyOwner(_strategy) returns (bytes memory) {
(bool success, bytes memory result) = _target.call(_calldata);

// If the call reverted. Return the error.
if (!success) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}

// Return the result.
return result;
}
}
Loading

0 comments on commit 7856970

Please sign in to comment.