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 all 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
4 changes: 3 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ jobs:
- uses: actions/setup-node@v3
with:
node-version: 16
- uses: ApeWorX/github-action@v2.4
- uses: ApeWorX/github-action@v2.0
with:
python-version: '3.10'
ape-version-pin: "==0.6.27"
ape-plugins-list: 'solidity==0.6.11 vyper==0.6.13 infura==0.6.5 hardhat==0.6.13 etherscan==0.6.11'

- name: install vyper
run: pip install git+https://github.com/vyperlang/vyper
Expand Down
7 changes: 6 additions & 1 deletion ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@ name: yearn-v3-vault-periphery

plugins:
- name: solidity
version: 0.6.11
- name: vyper
version: 0.6.13
- name: etherscan
version: 0.6.11
- name: hardhat
version: 0.6.13
- name: infura
version: 0.6.5

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
105 changes: 81 additions & 24 deletions contracts/Managers/RoleManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,46 @@ import {IVault} from "@yearn-vaults/interfaces/IVault.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Governance2Step} from "@periphery/utils/Governance2Step.sol";
import {HealthCheckAccountant} from "../accountants/HealthCheckAccountant.sol";
import {GenericDebtAllocatorFactory} from "../debtAllocators/GenericDebtAllocatorFactory.sol";
import {DebtAllocatorFactory} from "../debtAllocators/DebtAllocatorFactory.sol";

/// @title Yearn V3 Vault Role Manager.
contract RoleManager is Governance2Step {
/// @notice Revert message for when a vault has already been deployed.
error AlreadyDeployed(address _vault);

/// @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 vaults debt allocator is updated.
event UpdateDebtAllocator(
address indexed vault,
address indexed debtAllocator
);

/// @notice Emitted when a new address is set for a position.
event UpdatePositionHolder(
bytes32 indexed position,
address indexed newAddress
);

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

/// @notice Emitted when a new set of roles is set for a position
event UpdatePositionRoles(bytes32 indexed position, uint256 newRoles);

/// @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 Position struct
struct Position {
address holder;
uint96 roles;
}

/// @notice Config that holds all vault info.
struct VaultConfig {
Expand All @@ -41,12 +56,6 @@ contract RoleManager is Governance2Step {
uint256 index;
}

/// @notice Position struct
struct Position {
address holder;
uint96 roles;
}

/// @notice Only allow either governance or the position holder to call.
modifier onlyPositionHolder(bytes32 _positionId) {
_isPositionHolder(_positionId);
Expand Down Expand Up @@ -109,6 +118,9 @@ contract RoleManager is Governance2Step {
mapping(bytes32 => Position) internal _positions;
/// @notice Mapping of vault addresses to its config.
mapping(address => VaultConfig) public vaultConfig;
/// @notice Mapping of underlying asset, api version and rating to vault.
mapping(address => mapping(string => mapping(uint256 => address)))
public _assetToVault;

constructor(
address _governance,
Expand Down Expand Up @@ -261,6 +273,12 @@ contract RoleManager is Governance2Step {
_profitMaxUnlockTime
);

// Check that a vault does not exist for that asset, api and rating.
// This reverts late to not waste gas when used correctly.
string memory _apiVersion = IVault(_vault).apiVersion();
if (_assetToVault[_asset][_apiVersion][_rating] != address(0))
revert AlreadyDeployed(_assetToVault[_asset][_apiVersion][_rating]);

// Deploy a new debt allocator for the vault.
address _debtAllocator = _deployAllocator(_vault);

Expand All @@ -282,6 +300,9 @@ contract RoleManager is Governance2Step {
index: vaults.length
});

// Add the vault to the mapping.
_assetToVault[_asset][_apiVersion][_rating] = _vault;

// Add the vault to the array.
vaults.push(_vault);

Expand All @@ -302,8 +323,9 @@ contract RoleManager is Governance2Step {
// If we have a factory set.
if (factory != address(0)) {
// Deploy a new debt allocator for the vault with Brain as the gov.
_debtAllocator = GenericDebtAllocatorFactory(factory)
.newGenericDebtAllocator(_vault, getPositionHolder(BRAIN));
_debtAllocator = DebtAllocatorFactory(factory).newDebtAllocator(
_vault
);
} else {
// If no factory is set we should be using one central allocator.
_debtAllocator = getPositionHolder(DEBT_ALLOCATOR);
Expand Down Expand Up @@ -438,6 +460,12 @@ contract RoleManager is Governance2Step {
) public virtual onlyPositionHolder(DADDY) {
require(_rating > 0 && _rating < 6, "rating out of range");

// Check that a vault does not exist for that asset, api and rating.
address _asset = IVault(_vault).asset();
string memory _apiVersion = IVault(_vault).apiVersion();
if (_assetToVault[_asset][_apiVersion][_rating] != address(0))
revert AlreadyDeployed(_assetToVault[_asset][_apiVersion][_rating]);

// If not the current role manager.
if (IVault(_vault).role_manager() != address(this)) {
// Accept the position of role manager.
Expand All @@ -450,6 +478,7 @@ contract RoleManager is Governance2Step {
// Check if the vault has been endorsed yet in the registry.
if (!Registry(registry).isEndorsed(_vault)) {
// If not endorse it.
// NOTE: This will revert if adding a vault of an older version.
Registry(registry).endorseMultiStrategyVault(_vault);
}

Expand All @@ -463,12 +492,15 @@ contract RoleManager is Governance2Step {

// Add the vault config to the mapping.
vaultConfig[_vault] = VaultConfig({
asset: IVault(_vault).asset(),
asset: _asset,
rating: _rating,
debtAllocator: _debtAllocator,
index: vaults.length
});

// Add the vault to the mapping.
_assetToVault[_asset][_apiVersion][_rating] = _vault;

// Add the vault to the array.
vaults.push(_vault);

Expand Down Expand Up @@ -512,6 +544,9 @@ contract RoleManager is Governance2Step {

// Update the vaults config.
vaultConfig[_vault].debtAllocator = _debtAllocator;

// Emit event.
emit UpdateDebtAllocator(_vault, _debtAllocator);
}

/**
Expand All @@ -522,24 +557,29 @@ contract RoleManager is Governance2Step {
function removeVault(
address _vault
) external virtual onlyPositionHolder(BRAIN) {
// Get the vault specific config.
VaultConfig memory config = vaultConfig[_vault];
// Make sure the vault has been added to the role manager.
require(vaultConfig[_vault].asset != address(0), "vault not added");
require(config.asset != address(0), "vault not added");

// Transfer the role manager position.
IVault(_vault).transfer_role_manager(chad);

// Index that the vault is in the array.
uint256 index = vaultConfig[_vault].index;
// Address of the vault to replace it with.
address vaultToMove = vaults[vaults.length - 1];

// Move the last vault to the index of `_vault`
vaults[index] = vaultToMove;
vaultConfig[vaultToMove].index = index;
vaults[config.index] = vaultToMove;
vaultConfig[vaultToMove].index = config.index;

// Remove the last item.
vaults.pop();

// Delete the vault from the mapping.
delete _assetToVault[config.asset][IVault(_vault).apiVersion()][
config.rating
];

// Delete the config for `_vault`.
delete vaultConfig[_vault];

Expand Down Expand Up @@ -611,6 +651,23 @@ contract RoleManager is Governance2Step {
return vaults;
}

/**
* @notice Get the vault for a specific asset, api and rating.
* @dev This will return address(0) if one has not been added or deployed.
*
* @param _asset The underlying asset used.
* @param _apiVersion The version of the vault.
* @param _rating The rating of the vault.
* @return The vault for the specified `_asset`, `_apiVersion` and `_rating`.
*/
function getVault(
address _asset,
string memory _apiVersion,
uint256 _rating
) external view virtual returns (address) {
return _assetToVault[_asset][_apiVersion][_rating];
}

/**
* @notice Check if a vault is managed by this contract.
* @dev This will check if the `asset` variable in the struct has been
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 {}
54 changes: 54 additions & 0 deletions contracts/Mocks/MockTokenizedStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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;
uint256 public limit;

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 {}

function availableWithdrawLimit(
address _owner
) public view virtual override returns (uint256) {
if (limit != 0) {
uint256 _totalAssets = strategyStorage().totalIdle;
return _totalAssets > limit ? _totalAssets - limit : 0;
} else {
return super.availableWithdrawLimit(_owner);
}
}

function setLimit(uint256 _limit) external {
limit = _limit;
}
}
Loading