-
Notifications
You must be signed in to change notification settings - Fork 14
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
Changes from 21 commits
54d3027
dd5f7d6
8002529
dcc343a
f51b9e2
71db754
56c9148
2a7b1e9
c42bfef
16c0acf
637886a
3785c34
3885b32
fae7ee0
3faefba
429d82f
2ee855d
5337260
232b754
ac75d6f
3cbfec6
fc3b236
610019a
d41e2d7
fe15fa4
d117149
f4d901c
a29e6cf
5ff8880
aa027df
4a0b4d6
5332c50
3c3eb3c
a390e09
9c01b1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 {} |
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 {} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably worth noting which units this uses (e.g. nominal, bps, percent) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
/// @notice Time to wait between debt updates. | ||
uint256 public minimumWait; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was this removed so that a timelock could be used instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
||
|
@@ -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; | ||
} | ||
|
||
/** | ||
|
@@ -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(); | ||
|
@@ -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 { | ||
|
@@ -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; | ||
} | ||
} |
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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.