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

build: 303 deployments #59

Merged
merged 12 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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 lib/forge-std
Submodule forge-std updated 5 files
+181 −0 CONTRIBUTING.md
+16 −0 README.md
+12 −1 scripts/vm.py
+106 −16 src/Vm.sol
+2 −2 test/Vm.t.sol
4 changes: 4 additions & 0 deletions src/addressProviders/ProtocolAddressProvider.vy
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
@notice
Protocol Address provider for the periphery contracts for the Yearn V3 system.
"""

interface IAddressProvider:
def getAddress(address_id: bytes32) -> address: view

#### EVENTS ####

event UpdatedAddress:
Expand Down
14 changes: 7 additions & 7 deletions src/debtAllocators/DebtAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ contract DebtAllocator is Governance {
uint256 maxDebt;
uint256 currentIdle;
uint256 minIdle;
uint256 max;
//uint256 max;
uint256 toChange;
}

Expand Down Expand Up @@ -296,20 +296,21 @@ contract DebtAllocator is Governance {
if (strategyDebtInfo.targetDebt > params.current_debt) {
strategyDebtInfo.currentIdle = IVault(_vault).totalIdle();
strategyDebtInfo.minIdle = IVault(_vault).minimum_total_idle();
strategyDebtInfo.max = IVault(_strategy).maxDeposit(_vault);

// We can't add more than the available idle.
if (strategyDebtInfo.minIdle >= strategyDebtInfo.currentIdle) {
return (false, bytes("No Idle"));
}

//strategyDebtInfo.max = IVault(_strategy).maxDeposit(_vault);

// Add up to the max if possible
strategyDebtInfo.toChange = Math.min(
strategyDebtInfo.maxDebt - params.current_debt,
// Can't take more than is available.
Math.min(
strategyDebtInfo.currentIdle - strategyDebtInfo.minIdle,
strategyDebtInfo.max
IVault(_strategy).maxDeposit(_vault)
)
);

Expand Down Expand Up @@ -339,9 +340,6 @@ contract DebtAllocator is Governance {

strategyDebtInfo.currentIdle = IVault(_vault).totalIdle();
strategyDebtInfo.minIdle = IVault(_vault).minimum_total_idle();
strategyDebtInfo.max = IVault(_strategy).convertToAssets(
IVault(_strategy).maxRedeem(_vault)
);

if (strategyDebtInfo.minIdle > strategyDebtInfo.currentIdle) {
// Pull at least the amount needed for minIdle.
Expand All @@ -356,7 +354,9 @@ contract DebtAllocator is Governance {
strategyDebtInfo.toChange,
// Account for the current liquidity constraints.
// Use max redeem to match vault logic.
strategyDebtInfo.max
IVault(_strategy).convertToAssets(
IVault(_strategy).maxRedeem(_vault)
)
);

// Check if it's over the threshold.
Expand Down
41 changes: 41 additions & 0 deletions src/managers/RoleManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {Registry} from "../registry/Registry.sol";
import {Accountant} from "../accountants/Accountant.sol";
import {Roles} from "@yearn-vaults/interfaces/Roles.sol";
import {IVault} from "@yearn-vaults/interfaces/IVault.sol";
import {ReleaseRegistry} from "../registry/ReleaseRegistry.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IVaultFactory} from "@yearn-vaults/interfaces/IVaultFactory.sol";
import {DebtAllocatorFactory} from "../debtAllocators/DebtAllocatorFactory.sol";

/// @title Yearn V3 Vault Role Manager.
Expand Down Expand Up @@ -83,6 +85,7 @@ contract RoleManager is Positions {

/// @notice Mapping of vault addresses to its config.
mapping(address => VaultConfig) public vaultConfig;

/// @notice Mapping of underlying asset, api version and category to vault.
mapping(address => mapping(string => mapping(uint256 => address)))
internal _assetToVault;
Expand Down Expand Up @@ -650,6 +653,44 @@ contract RoleManager is Positions {
return _assetToVault[_asset][_apiVersion][_category];
}

/**
* @notice Get the latest vault for a specific asset.
* @dev This will default to using category 1.
* @param _asset The underlying asset used.
* @return _vault latest vault for the specified `_asset` if any.
*/
function latestVault(
address _asset
) external view virtual returns (address) {
return latestVault(_asset, 1);
}

/**
* @notice Get the latest vault for a specific asset.
* @param _asset The underlying asset used.
* @param _category The category of the vault.
* @return _vault latest vault for the specified `_asset` if any.
*/
function latestVault(
address _asset,
uint256 _category
) public view virtual returns (address _vault) {
address releaseRegistry = Registry(getPositionHolder(REGISTRY))
.releaseRegistry();
uint256 numReleases = ReleaseRegistry(releaseRegistry).numReleases();

for (uint256 i = numReleases; i > 0; --i) {
string memory apiVersion = IVaultFactory(
ReleaseRegistry(releaseRegistry).factories(i - 1)
).apiVersion();

_vault = _assetToVault[_asset][apiVersion][_category];
if (_vault != address(0)) {
break;
}
}
}

/**
* @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
2 changes: 1 addition & 1 deletion src/registry/RegistryFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract RegistryFactory {
}

function name() external pure virtual returns (string memory) {
return "Custom Vault Registry Factory";
return "Yearn V3 Vault Registry Factory";
}

/**
Expand Down
48 changes: 41 additions & 7 deletions src/registry/ReleaseRegistry.sol
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity >=0.8.18;

import {Governance} from "@periphery/utils/Governance.sol";
import {Governance2Step} from "@periphery/utils/Governance2Step.sol";

interface IFactory {
function apiVersion() external view returns (string memory);
}

interface ITokenizedStrategy {
function apiVersion() external view returns (string memory);
}

/**
* @title YearnV3 Release Registry
* @author yearn.finance
* @notice
* Used by Yearn Governance to track on chain all
* releases of the V3 vaults by API Version.
*/
contract ReleaseRegistry is Governance {
contract ReleaseRegistry is Governance2Step {
event NewRelease(
uint256 indexed releaseId,
address indexed factory,
address indexed tokenizedStrategy,
string apiVersion
);

Expand All @@ -30,27 +35,43 @@ contract ReleaseRegistry is Governance {
// of the corresponding factory for that release.
mapping(uint256 => address) public factories;

// Mapping of release id starting at 0 to the address
// of the corresponding Tokenized Strategy for that release.
mapping(uint256 => address) public tokenizedStrategies;

// Mapping of the API version for a specific release to the
// place in the order it was released.
mapping(string => uint256) public releaseTargets;

constructor(address _governance) Governance(_governance) {}
constructor(address _governance) Governance2Step(_governance) {}

/**
* @notice Returns the latest factory.
* @dev Throws if no releases are registered yet.
* @return The address of the factory for the latest release.
*/
function latestFactory() external view virtual returns (address) {
uint256 _numReleases = numReleases;
if (_numReleases == 0) return address(0);
return factories[numReleases - 1];
}

/**
* @notice Returns the latest tokenized strategy.
* @return The address of the tokenized strategy for the latest release.
*/
function latestTokenizedStrategy() external view virtual returns (address) {
uint256 _numReleases = numReleases;
if (_numReleases == 0) return address(0);
return tokenizedStrategies[numReleases - 1];
}

/**
* @notice Returns the api version of the latest release.
* @dev Throws if no releases are registered yet.
* @return The api version of the latest release.
*/
function latestRelease() external view virtual returns (string memory) {
uint256 _numReleases = numReleases;
if (_numReleases == 0) return "";
return IFactory(factories[numReleases - 1]).apiVersion(); // dev: no release
}

Expand All @@ -65,11 +86,23 @@ contract ReleaseRegistry is Governance {
*
* @param _factory The factory that will be used create new vaults.
*/
function newRelease(address _factory) external virtual onlyGovernance {
function newRelease(
address _factory,
address _tokenizedStrategy
) external virtual onlyGovernance {
// Check if the release is different from the current one
uint256 releaseId = numReleases;

string memory apiVersion = IFactory(_factory).apiVersion();
string memory tokenizedStrategyApiVersion = ITokenizedStrategy(
_tokenizedStrategy
).apiVersion();

require(
keccak256(bytes(apiVersion)) ==
keccak256(bytes(tokenizedStrategyApiVersion)),
"ReleaseRegistry: api version mismatch"
);

if (releaseId > 0) {
// Make sure this isn't the same as the last one
Expand All @@ -83,6 +116,7 @@ contract ReleaseRegistry is Governance {

// Update latest release.
factories[releaseId] = _factory;
tokenizedStrategies[releaseId] = _tokenizedStrategy;

// Set the api to the target.
releaseTargets[apiVersion] = releaseId;
Expand All @@ -91,6 +125,6 @@ contract ReleaseRegistry is Governance {
numReleases = releaseId + 1;

// Log the release for external listeners
emit NewRelease(releaseId, _factory, apiVersion);
emit NewRelease(releaseId, _factory, _tokenizedStrategy, apiVersion);
}
}
53 changes: 52 additions & 1 deletion src/test/managers/TestRoleManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.18;

import "forge-std/console2.sol";
import {MockFactory} from "../../mocks/MockFactory.sol";
import {Setup, RoleManager, IVault, Roles, MockStrategy, DebtAllocator} from "../utils/Setup.sol";

contract TestRoleManager is Setup {
Expand Down Expand Up @@ -64,7 +65,7 @@ contract TestRoleManager is Setup {
strategy = createStrategy(address(asset));

vm.prank(daddy);
releaseRegistry.newRelease(address(vaultFactory));
releaseRegistry.newRelease(address(vaultFactory), address(strategy));
}

function test_role_manager_setup() public {
Expand Down Expand Up @@ -1156,4 +1157,54 @@ contract TestRoleManager is Setup {

assertEq(newVault.role_manager(), daddy);
}

function test_latestVault() public {
// Deploy multiple vaults with different API versions
vm.prank(daddy);
address vault1 = roleManager.newVault(
address(asset),
1,
"Vault1",
"V1"
);

assertEq(roleManager.latestVault(address(asset)), vault1);

vm.prank(daddy);
address vault2 = roleManager.newVault(
address(asset),
2,
"Vault2",
"V2"
);

assertEq(roleManager.latestVault(address(asset)), vault1);
assertEq(roleManager.latestVault(address(asset), 2), vault2);

MockFactory newFactory = new MockFactory("1.0.0");
MockStrategy newStrategy = new MockStrategy(address(asset), "1.0.0");

vm.prank(daddy);
address vault4 = roleManager.newVault(
address(newStrategy),
1,
"Vault4",
"V2"
);

assertEq(roleManager.latestVault(address(asset)), vault1);
assertEq(roleManager.latestVault(address(asset), 2), vault2);
assertEq(roleManager.latestVault(address(newStrategy)), vault4);
// Check for a non-existent asset
assertEq(roleManager.latestVault(address(0x123)), address(0));

releaseRegistry.newRelease(address(newFactory), address(newStrategy));

assertEq(roleManager.latestVault(address(asset)), vault1);
assertEq(roleManager.latestVault(address(asset), 2), vault2);
// Check that the latest vault is still vault3
assertEq(roleManager.latestVault(address(newStrategy)), vault4);
// Check for a non-existent asset
assertEq(roleManager.latestVault(address(0x123)), address(0));
}
}
Loading
Loading