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: use allocator #30

Merged
merged 35 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
54d3027
build: yield manager
Schlagonia Dec 1, 2023
dd5f7d6
fix: check loss and outsource reporting
Schlagonia Dec 4, 2023
8002529
fix: yield not apr
Schlagonia Dec 5, 2023
dcc343a
test: yield manager
Schlagonia Dec 12, 2023
f51b9e2
feat: add events
Schlagonia Dec 12, 2023
71db754
fix: remove generic call
Schlagonia Dec 13, 2023
56c9148
fix: permissions
Schlagonia Dec 14, 2023
2a7b1e9
fix: deployments
Schlagonia Dec 15, 2023
c42bfef
fix: workflow
Schlagonia Dec 15, 2023
16c0acf
fix: rebase tests
Schlagonia Dec 18, 2023
637886a
feat: use allocator
Schlagonia Dec 18, 2023
3785c34
feat: manage strategy
Schlagonia Dec 18, 2023
3885b32
test: update tests
Schlagonia Dec 19, 2023
fae7ee0
fix: fixes
Schlagonia Dec 19, 2023
3faefba
fix: remove event
Schlagonia Dec 20, 2023
429d82f
feat: just a keeper contract
Schlagonia Dec 20, 2023
2ee855d
fix: removal
Schlagonia Dec 20, 2023
5337260
feat: report when going to 0
Schlagonia Dec 20, 2023
232b754
feat: script and changes
Schlagonia Dec 21, 2023
ac75d6f
fix: make virtual
Schlagonia Dec 21, 2023
3cbfec6
fix: formatting
Schlagonia Dec 21, 2023
fc3b236
feat: check max redeem and deposit
Schlagonia Dec 28, 2023
610019a
feat: dont duplicate vaults
Schlagonia Dec 28, 2023
d41e2d7
chore: change dep
Schlagonia Dec 28, 2023
fe15fa4
fix: versions
Schlagonia Dec 28, 2023
d117149
chore: pin ape version
Schlagonia Dec 28, 2023
f4d901c
chore: downgrade ape
Schlagonia Jan 5, 2024
a29e6cf
chore: pin all versions
Schlagonia Jan 5, 2024
5ff8880
feat: increase and decrease
Schlagonia Jan 5, 2024
aa027df
fix: formatting
Schlagonia Jan 6, 2024
4a0b4d6
fix: naming
Schlagonia Jan 7, 2024
5332c50
build: use central allocator factory (#31)
Schlagonia Jan 8, 2024
3c3eb3c
chore: renaming
Schlagonia Jan 8, 2024
a390e09
chore: comments
Schlagonia Jan 8, 2024
9c01b1e
fix: black
Schlagonia Jan 9, 2024
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 ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ default_ecosystem: ethereum
dependencies:
- name: openzeppelin
github: OpenZeppelin/openzeppelin-contracts
version: 4.8.2
ref: 4.8.2
- name: yearn-vaults
github: yearn/yearn-vaults-v3
ref: v3.0.1
Expand Down
20 changes: 10 additions & 10 deletions contracts/Managers/RoleManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ import {GenericDebtAllocatorFactory} from "../debtAllocators/GenericDebtAllocato

/// @title Yearn V3 Vault Role Manager.
contract RoleManager is Governance2Step {
/// @notice Emitted when a new vault has been deployed or added.
event AddedNewVault(
address indexed vault,
address indexed debtAllocator,
uint256 rating
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is rating there for the future or is it used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be used once this is deployed, both for naming conventions as well as on chain tracking.

Also for future use by a strategy manager contract to potentially compare rating before adding certain strategies.

);

/// @notice Emitted when a vault is removed.
event RemovedVault(address indexed vault);

/// @notice Emitted when a new address is set for a position.
event UpdatePositionHolder(
bytes32 indexed position,
Expand All @@ -23,16 +33,6 @@ contract RoleManager is Governance2Step {
/// @notice Emitted when the defaultProfitMaxUnlock variable is updated.
event UpdateDefaultProfitMaxUnlock(uint256 newDefaultProfitMaxUnlock);

/// @notice Emitted when a new vault has been deployed or added.
event AddedNewVault(
address indexed vault,
address indexed debtAllocator,
uint256 rating
);

/// @notice Emitted when a vault is removed.
event RemovedVault(address indexed vault);

/// @notice Config that holds all vault info.
struct VaultConfig {
address asset;
Expand Down
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 {}
38 changes: 38 additions & 0 deletions contracts/Mocks/MockTokenizedStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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;
}

function tendThis(uint256) external {}
}
44 changes: 36 additions & 8 deletions contracts/debtAllocators/GenericDebtAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ contract GenericDebtAllocator is Governance {
// Can't be more than 10_000.
uint256 public debtRatio;

/// @notice Time to wait between debt updates.
uint256 public minimumWait;

/// @notice The minimum amount denominated in asset that will
// need to be moved to trigger a debt update.
uint256 public minimumChange;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth noting which units this uses (e.g. nominal, bps, percent)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/// @notice Time to wait between debt updates.
uint256 public minimumWait;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this removed so that a timelock could be used instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it was just moved up above 'minimumChange` for formatting looks.


/// @notice Max loss to accept on debt updates in basis points.
uint256 public maxDebtUpdateLoss;

Expand Down Expand Up @@ -123,16 +123,17 @@ contract GenericDebtAllocator is Governance {
uint256 _minimumChange
) public virtual {
require(address(vault) == address(0), "!initialized");
// Set initial variables.
vault = _vault;
governance = _governance;
minimumChange = _minimumChange;

// Default max loss on debt updates to 1 BP.
maxDebtUpdateLoss = 1;
// Default to allow governance to be a keeper.
keepers[_governance] = true;

minimumChange = _minimumChange;
// Default max base fee to uint256 max
maxAcceptableBaseFee = type(uint256).max;
// Default max loss on debt updates to 1 BP.
maxDebtUpdateLoss = 1;
}

/**
Expand All @@ -156,6 +157,11 @@ contract GenericDebtAllocator is Governance {
) external virtual onlyKeepers {
IVault _vault = IVault(vault);

// If going to 0 record full balance first.
if (_targetDebt == 0) {
_vault.process_report(_strategy);
}

// Cache initial values in case of loss.
uint256 initialDebt = _vault.strategies(_strategy).current_debt;
uint256 initialAssets = _vault.totalAssets();
Expand Down Expand Up @@ -358,7 +364,7 @@ contract GenericDebtAllocator is Governance {
* @param _address The address to set mapping for.
* @param _allowed If the address can call {update_debt}.
*/
function setKeepers(
function setKeeper(
address _address,
bool _allowed
) external virtual onlyGovernance {
Expand Down Expand Up @@ -428,4 +434,26 @@ contract GenericDebtAllocator is Governance {

emit UpdateMaxAcceptableBaseFee(_maxAcceptableBaseFee);
}

/**
* @notice Get a strategies target debt ratio.
* @param _strategy Address of the strategy.
* @return The strategies current targetRatio.
*/
function getStrategyTargetRatio(
address _strategy
) external view virtual returns (uint256) {
return configs[_strategy].targetRatio;
}

/**
* @notice Get a strategies max debt ratio.
* @param _strategy Address of the strategy.
* @return The strategies current maxRatio.
*/
function getStrategyMaxRatio(
address _strategy
) external view virtual returns (uint256) {
return configs[_strategy].maxRatio;
}
}
129 changes: 129 additions & 0 deletions contracts/debtAllocators/YieldManager/Keeper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.18;

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

/// @notice Holds the `keeper` role of a V3 strategy so that a
/// multiple addresses can call report.
contract Keeper is Governance {
/// @notice Emitted when a strategy is removed.
event StrategyRemoved(address indexed strategy);

/// @notice An event emitted when a keeper is added or removed.
event UpdateKeeper(address indexed keeper, bool allowed);

/// @notice Emitted when a new strategy is added to the manager.
event StrategyAdded(address indexed strategy, address indexed owner);

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

/// @notice Only the keepers can call.
modifier onlyKeepers(address _strategy) {
_checkKeepers(_strategy);
_;
}

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

/// @notice Checks if the msg sender is a keeper and the strategy is added.
function _checkKeepers(address _strategy) internal view virtual {
require(
keepers[msg.sender] && strategyOwner[_strategy] != address(0),
"!keeper"
);
}

/// @notice Address check for keepers allowed to call.
mapping(address => bool) public keepers;

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

constructor(address _governance) Governance(_governance) {}

/**
* @notice Add a new strategy, using the current `management` as the owner.
* @param _strategy The address of the strategy.
*/
function addNewStrategy(address _strategy) external virtual onlyGovernance {
address currentManager = IStrategy(_strategy).management();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currentManager could be moved down in this function to save gas on reverts

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require(strategyOwner[_strategy] == address(0), "already active");
require(IStrategy(_strategy).keeper() == address(this), "!keeper");

// Store the owner of the strategy.
strategyOwner[_strategy] = currentManager;

emit StrategyAdded(_strategy, currentManager);
}

/**
* @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 virtual onlyStrategyOwner(_strategy) {
require(
_newOwner != address(0) &&
_newOwner != address(this) &&
_newOwner != _strategy,
"bad address"
);
strategyOwner[_strategy] = _newOwner;
}

/**
* @notice Removes the strategy.
* @param _strategy The address of the strategy.
*/
function removeStrategy(address _strategy) external virtual {
// Only governance or the strategy owner can call.
if (msg.sender != governance) _checkStrategyOwner(_strategy);

delete strategyOwner[_strategy];

emit StrategyRemoved(_strategy);
}

/**
* @notice Reports full profit for a strategy.
* @param _strategy The address of the strategy.
*/
function report(address _strategy) external virtual onlyKeepers(_strategy) {
// Report profits.
IStrategy(_strategy).report();
}

/**
* @notice Tends a strategy.
* @param _strategy The address of the strategy.
*/
function tend(address _strategy) external virtual onlyKeepers(_strategy) {
// Tend.
IStrategy(_strategy).tend();
}

/**
* @notice Set if a keeper can update debt.
* @param _address The address to set mapping for.
* @param _allowed If the address can call {update_debt}.
*/
function setKeeper(
address _address,
bool _allowed
) external virtual onlyGovernance {
keepers[_address] = _allowed;

emit UpdateKeeper(_address, _allowed);
}
}
Loading
Loading