From 992dd21b133580738d8dc61a2665076b880d8605 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Thu, 10 Oct 2024 15:07:09 -0600 Subject: [PATCH 01/12] build: add ts to rr --- lib/forge-std | 2 +- .../ProtocolAddressProvider.vy | 4 + src/registry/ReleaseRegistry.sol | 41 ++++++- src/test/managers/TestRoleManager.t.sol | 2 +- src/test/registry/TestRegistry.t.sol | 45 ++++---- src/test/registry/TestReleaseRegistry.t.sol | 106 ++++++++++++++---- 6 files changed, 155 insertions(+), 45 deletions(-) diff --git a/lib/forge-std b/lib/forge-std index 8f24d6b..4f57c59 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 8f24d6b04c92975e0795b5868aa0d783251cdeaa +Subproject commit 4f57c59f066a03d13de8c65bb34fca8247f5fcb2 diff --git a/src/addressProviders/ProtocolAddressProvider.vy b/src/addressProviders/ProtocolAddressProvider.vy index d1305da..b00956a 100644 --- a/src/addressProviders/ProtocolAddressProvider.vy +++ b/src/addressProviders/ProtocolAddressProvider.vy @@ -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: diff --git a/src/registry/ReleaseRegistry.sol b/src/registry/ReleaseRegistry.sol index bafdbeb..e6c9bda 100644 --- a/src/registry/ReleaseRegistry.sol +++ b/src/registry/ReleaseRegistry.sol @@ -1,12 +1,16 @@ // 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 @@ -14,10 +18,11 @@ interface IFactory { * 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 ); @@ -30,11 +35,15 @@ 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. @@ -45,6 +54,15 @@ contract ReleaseRegistry is Governance { return factories[numReleases - 1]; } + /** + * @notice Returns the latest tokenized strategy. + * @dev Throws if no releases are registered yet. + * @return The address of the tokenized strategy for the latest release. + */ + function latestTokenizedStrategy() external view virtual returns (address) { + return tokenizedStrategies[numReleases - 1]; + } + /** * @notice Returns the api version of the latest release. * @dev Throws if no releases are registered yet. @@ -65,11 +83,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 @@ -83,6 +113,7 @@ contract ReleaseRegistry is Governance { // Update latest release. factories[releaseId] = _factory; + tokenizedStrategies[releaseId] = _tokenizedStrategy; // Set the api to the target. releaseTargets[apiVersion] = releaseId; @@ -91,6 +122,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); } } diff --git a/src/test/managers/TestRoleManager.t.sol b/src/test/managers/TestRoleManager.t.sol index 8c14cfc..1cd26a6 100644 --- a/src/test/managers/TestRoleManager.t.sol +++ b/src/test/managers/TestRoleManager.t.sol @@ -64,7 +64,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 { diff --git a/src/test/registry/TestRegistry.t.sol b/src/test/registry/TestRegistry.t.sol index 9248370..ff4c6de 100644 --- a/src/test/registry/TestRegistry.t.sol +++ b/src/test/registry/TestRegistry.t.sol @@ -44,7 +44,7 @@ contract TestRegistry is Setup { } function test__deploy_new_vault() public { - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -103,7 +103,7 @@ contract TestRegistry is Setup { function test__endorse_deployed_vault() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -168,7 +168,7 @@ contract TestRegistry is Setup { function test__endorse_deployed_strategy() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -212,7 +212,7 @@ contract TestRegistry is Setup { function test__endorse_deployed_vault__default_values() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -270,7 +270,7 @@ contract TestRegistry is Setup { function test__endorse_deployed_strategy__default_values() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -315,13 +315,14 @@ contract TestRegistry is Setup { function test__deploy_vault_with_new_release() public { // Add a mock factory for version release 1 MockFactory mockFactory = new MockFactory("2.0.0"); + MockStrategy mockStrategy = new MockStrategy(address(asset), "2.0.0"); vm.prank(daddy); - releaseRegistry.newRelease(address(mockFactory)); + releaseRegistry.newRelease(address(mockFactory), address(mockStrategy)); assertEq(releaseRegistry.numReleases(), 1); // Add the factory as the second release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 2); @@ -380,14 +381,15 @@ contract TestRegistry is Setup { function test__deploy_vault_with_old_release() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); // Add a mock factory for version release 2 MockFactory mockFactory = new MockFactory("2.0.0"); + MockStrategy mockStrategy = new MockStrategy(address(asset), "2.0.0"); vm.prank(daddy); - releaseRegistry.newRelease(address(mockFactory)); + releaseRegistry.newRelease(address(mockFactory), address(mockStrategy)); assertEq(releaseRegistry.numReleases(), 2); @@ -443,16 +445,18 @@ contract TestRegistry is Setup { function test__endorse_deployed_vault_wrong_api__reverts() public { // Add a mock factory for version release 1 MockFactory mockFactory = new MockFactory("6.9"); + MockStrategy mockStrategy = new MockStrategy(address(asset), "6.9"); addNewRelease( releaseRegistry, IVaultFactory(address(mockFactory)), + address(mockStrategy), daddy ); assertEq(releaseRegistry.numReleases(), 1); // Set the factory as the second release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 2); @@ -483,16 +487,18 @@ contract TestRegistry is Setup { function test__endorse_strategy_wrong_api__reverts() public { // Add a mock factory for version release 1 MockFactory mockFactory = new MockFactory("6.9"); + MockStrategy mockStrategy = new MockStrategy(address(asset), "6.9"); addNewRelease( releaseRegistry, IVaultFactory(address(mockFactory)), + address(mockStrategy), daddy ); assertEq(releaseRegistry.numReleases(), 1); // Set the factory as the second release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 2); @@ -508,7 +514,7 @@ contract TestRegistry is Setup { function test__remove_vault() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -585,7 +591,7 @@ contract TestRegistry is Setup { function test__remove_vault__two_vaults() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -655,7 +661,7 @@ contract TestRegistry is Setup { function test__remove_strategy() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -711,7 +717,7 @@ contract TestRegistry is Setup { function test__remove_strategy__two_strategies() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -770,7 +776,7 @@ contract TestRegistry is Setup { function test__remove_asset() public { // Add the factory as the first release - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -804,7 +810,7 @@ contract TestRegistry is Setup { } function test__tag_vault() public { - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); assertEq(releaseRegistry.numReleases(), 1); @@ -863,7 +869,7 @@ contract TestRegistry is Setup { } function test__access() public { - addNewRelease(releaseRegistry, vaultFactory, daddy); + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); string memory name = "New vaults"; string memory symbol = "yvTest"; @@ -982,9 +988,10 @@ contract TestRegistry is Setup { function addNewRelease( ReleaseRegistry _releaseRegistry, IVaultFactory _factory, + address _tokenizedStrategy, address _owner ) internal { vm.prank(_owner); - _releaseRegistry.newRelease(address(_factory)); + _releaseRegistry.newRelease(address(_factory), _tokenizedStrategy); } } diff --git a/src/test/registry/TestReleaseRegistry.t.sol b/src/test/registry/TestReleaseRegistry.t.sol index 60aa546..9e04516 100644 --- a/src/test/registry/TestReleaseRegistry.t.sol +++ b/src/test/registry/TestReleaseRegistry.t.sol @@ -8,6 +8,7 @@ contract TestReleaseRegistry is Setup { event NewRelease( uint256 indexed releaseId, address indexed factory, + address indexed tokenizedStrategy, string apiVersion ); event GovernanceTransferred( @@ -15,50 +16,73 @@ contract TestReleaseRegistry is Setup { address indexed newGovernance ); + address public tokenizedStrategy; + function setUp() public override { super.setUp(); + tokenizedStrategy = address( + new MockStrategy(address(asset), vaultFactory.apiVersion()) + ); } function test__deployment() public { assertEq(releaseRegistry.governance(), daddy); assertEq(releaseRegistry.numReleases(), 0); assertEq(releaseRegistry.factories(0), address(0)); + assertEq(releaseRegistry.tokenizedStrategies(0), address(0)); assertEq(releaseRegistry.releaseTargets("3.0.3"), 0); } function test_new_release() public { assertEq(releaseRegistry.numReleases(), 0); assertEq(releaseRegistry.factories(0), address(0)); - + assertEq(releaseRegistry.tokenizedStrategies(0), address(0)); vm.prank(daddy); vm.expectEmit(true, true, false, true); - emit NewRelease(0, address(vaultFactory), vaultFactory.apiVersion()); - releaseRegistry.newRelease(address(vaultFactory)); + emit NewRelease( + 0, + address(vaultFactory), + tokenizedStrategy, + vaultFactory.apiVersion() + ); + releaseRegistry.newRelease(address(vaultFactory), tokenizedStrategy); assertEq(releaseRegistry.numReleases(), 1); assertEq(releaseRegistry.factories(0), address(vaultFactory)); + assertEq(releaseRegistry.tokenizedStrategies(0), tokenizedStrategy); assertEq(releaseRegistry.releaseTargets(vaultFactory.apiVersion()), 0); assertEq(releaseRegistry.latestFactory(), address(vaultFactory)); assertEq(releaseRegistry.latestRelease(), vaultFactory.apiVersion()); - + assertEq(releaseRegistry.latestTokenizedStrategy(), tokenizedStrategy); string memory new_api = "4.3.2"; // Deploy a new mock factory with a different api MockFactory new_factory = new MockFactory(new_api); + MockStrategy new_strategy = new MockStrategy(address(asset), new_api); vm.prank(daddy); vm.expectEmit(true, true, false, true); - emit NewRelease(1, address(new_factory), new_api); - releaseRegistry.newRelease(address(new_factory)); + emit NewRelease( + 1, + address(new_factory), + address(new_strategy), + new_api + ); + releaseRegistry.newRelease(address(new_factory), address(new_strategy)); assertEq(releaseRegistry.numReleases(), 2); assertEq(releaseRegistry.factories(1), address(new_factory)); + assertEq(releaseRegistry.tokenizedStrategies(1), address(new_strategy)); assertEq(releaseRegistry.releaseTargets(new_api), 1); assertEq(releaseRegistry.latestFactory(), address(new_factory)); assertEq(releaseRegistry.latestRelease(), new_api); - + assertEq( + releaseRegistry.latestTokenizedStrategy(), + address(new_strategy) + ); // make sure the first factory is still returning assertEq(releaseRegistry.factories(0), address(vaultFactory)); assertEq(releaseRegistry.releaseTargets(vaultFactory.apiVersion()), 0); + assertEq(releaseRegistry.tokenizedStrategies(0), tokenizedStrategy); } function test_access() public { @@ -68,16 +92,27 @@ contract TestReleaseRegistry is Setup { // only daddy should be able to set a new release vm.prank(user); vm.expectRevert(); - releaseRegistry.newRelease(address(vaultFactory)); + releaseRegistry.newRelease( + address(vaultFactory), + address(tokenizedStrategy) + ); assertEq(releaseRegistry.numReleases(), 0); assertEq(releaseRegistry.factories(0), address(0)); + assertEq(releaseRegistry.tokenizedStrategies(0), address(0)); vm.prank(daddy); - releaseRegistry.newRelease(address(vaultFactory)); + releaseRegistry.newRelease( + address(vaultFactory), + address(tokenizedStrategy) + ); assertEq(releaseRegistry.numReleases(), 1); assertEq(releaseRegistry.factories(0), address(vaultFactory)); + assertEq( + releaseRegistry.tokenizedStrategies(0), + address(tokenizedStrategy) + ); } function test__add_same_factory() public { @@ -86,35 +121,68 @@ contract TestReleaseRegistry is Setup { vm.prank(daddy); vm.expectEmit(true, true, false, true); - emit NewRelease(0, address(vaultFactory), vaultFactory.apiVersion()); - releaseRegistry.newRelease(address(vaultFactory)); + emit NewRelease( + 0, + address(vaultFactory), + tokenizedStrategy, + vaultFactory.apiVersion() + ); + releaseRegistry.newRelease(address(vaultFactory), tokenizedStrategy); assertEq(releaseRegistry.numReleases(), 1); assertEq(releaseRegistry.factories(0), address(vaultFactory)); assertEq(releaseRegistry.latestFactory(), address(vaultFactory)); assertEq(releaseRegistry.latestRelease(), vaultFactory.apiVersion()); + assertEq(releaseRegistry.tokenizedStrategies(0), tokenizedStrategy); + assertEq(releaseRegistry.latestTokenizedStrategy(), tokenizedStrategy); vm.prank(daddy); vm.expectRevert("ReleaseRegistry: same api version"); - releaseRegistry.newRelease(address(vaultFactory)); + releaseRegistry.newRelease(address(vaultFactory), tokenizedStrategy); assertEq(releaseRegistry.numReleases(), 1); } - function test__transfer_governance() public { + function test_revert_mismatched_api_versions() public { + // Deploy a new vault factory with a different API version + MockFactory newVaultFactory = new MockFactory("2.0.0"); + + vm.prank(daddy); + vm.expectRevert("ReleaseRegistry: api version mismatch"); + releaseRegistry.newRelease(address(newVaultFactory), tokenizedStrategy); + + // Ensure no new release was added + assertEq(releaseRegistry.numReleases(), 0); + assertEq(releaseRegistry.factories(0), address(0)); + assertEq(releaseRegistry.tokenizedStrategies(0), address(0)); + } + + function test__transfer_governance_two_step() public { + address newGovernance = user; + + // Initial state assertEq(releaseRegistry.governance(), daddy); + assertEq(releaseRegistry.pendingGovernance(), address(0)); + // Step 1: Current governance initiates transfer vm.prank(daddy); - vm.expectRevert("ZERO ADDRESS"); - releaseRegistry.transferGovernance(address(0)); + releaseRegistry.transferGovernance(newGovernance); + // Check intermediate state assertEq(releaseRegistry.governance(), daddy); + assertEq(releaseRegistry.pendingGovernance(), newGovernance); + // Attempt to accept from wrong address vm.prank(daddy); - vm.expectEmit(true, true, false, true); - emit GovernanceTransferred(daddy, user); - releaseRegistry.transferGovernance(user); + vm.expectRevert("!pending governance"); + releaseRegistry.acceptGovernance(); + + // Step 2: New governance accepts transfer + vm.prank(newGovernance); + releaseRegistry.acceptGovernance(); - assertEq(releaseRegistry.governance(), user); + // Check final state + assertEq(releaseRegistry.governance(), newGovernance); + assertEq(releaseRegistry.pendingGovernance(), address(0)); } } From 9d8fe4a1fed4d5814b694e1355a64a68c06e2b6a Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Fri, 11 Oct 2024 13:49:07 -0600 Subject: [PATCH 02/12] chore: add latest vault --- src/managers/RoleManager.sol | 41 ++++++++++++++++++++ src/registry/Registry.sol | 13 +++++++ src/test/managers/TestRoleManager.t.sol | 51 +++++++++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/src/managers/RoleManager.sol b/src/managers/RoleManager.sol index 960bbb3..dc8a110 100644 --- a/src/managers/RoleManager.sol +++ b/src/managers/RoleManager.sol @@ -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. @@ -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; @@ -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 diff --git a/src/registry/Registry.sol b/src/registry/Registry.sol index c197c01..094a531 100644 --- a/src/registry/Registry.sol +++ b/src/registry/Registry.sol @@ -105,6 +105,9 @@ contract Registry is Governance { // Custom name for this Registry. string public name; + // A previous registry to fallback to as well. + address public legacyRegistry; + // Mapping for any address that is allowed to tag a vault. mapping(address => bool) public taggers; @@ -533,4 +536,14 @@ contract Registry is Governance { emit UpdateTagger(_account, _canTag); } + + /** + * @notice Set a legacy registry to fallback to. + * @param _legacyRegistry The address of the legacy registry. + */ + function setLegacyRegistry( + address _legacyRegistry + ) external virtual onlyGovernance { + legacyRegistry = _legacyRegistry; + } } diff --git a/src/test/managers/TestRoleManager.t.sol b/src/test/managers/TestRoleManager.t.sol index 1cd26a6..16b3ead 100644 --- a/src/test/managers/TestRoleManager.t.sol +++ b/src/test/managers/TestRoleManager.t.sol @@ -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 { @@ -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)); + } } From 66d7283980fe1491dbc083ec95d929383767233f Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Fri, 11 Oct 2024 14:18:09 -0600 Subject: [PATCH 03/12] chore: no legacy --- src/registry/Registry.sol | 13 ------------- src/registry/RegistryFactory.sol | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/registry/Registry.sol b/src/registry/Registry.sol index 094a531..c197c01 100644 --- a/src/registry/Registry.sol +++ b/src/registry/Registry.sol @@ -105,9 +105,6 @@ contract Registry is Governance { // Custom name for this Registry. string public name; - // A previous registry to fallback to as well. - address public legacyRegistry; - // Mapping for any address that is allowed to tag a vault. mapping(address => bool) public taggers; @@ -536,14 +533,4 @@ contract Registry is Governance { emit UpdateTagger(_account, _canTag); } - - /** - * @notice Set a legacy registry to fallback to. - * @param _legacyRegistry The address of the legacy registry. - */ - function setLegacyRegistry( - address _legacyRegistry - ) external virtual onlyGovernance { - legacyRegistry = _legacyRegistry; - } } diff --git a/src/registry/RegistryFactory.sol b/src/registry/RegistryFactory.sol index 22bd878..dcc2135 100644 --- a/src/registry/RegistryFactory.sol +++ b/src/registry/RegistryFactory.sol @@ -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"; } /** From 833e767ddf2c9a26ffdf2bce129126b3fb284bb2 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Fri, 11 Oct 2024 14:50:46 -0600 Subject: [PATCH 04/12] chore: clean up allocator --- src/debtAllocators/DebtAllocator.sol | 14 +++++++------- src/test/registry/TestRegistryFactory.t.sol | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/debtAllocators/DebtAllocator.sol b/src/debtAllocators/DebtAllocator.sol index 68a649d..f83b060 100644 --- a/src/debtAllocators/DebtAllocator.sol +++ b/src/debtAllocators/DebtAllocator.sol @@ -120,7 +120,7 @@ contract DebtAllocator is Governance { uint256 maxDebt; uint256 currentIdle; uint256 minIdle; - uint256 max; + //uint256 max; uint256 toChange; } @@ -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) ) ); @@ -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. @@ -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. diff --git a/src/test/registry/TestRegistryFactory.t.sol b/src/test/registry/TestRegistryFactory.t.sol index db192f3..821c2db 100644 --- a/src/test/registry/TestRegistryFactory.t.sol +++ b/src/test/registry/TestRegistryFactory.t.sol @@ -13,7 +13,7 @@ contract TestRegistryFactory is Setup { address(registryFactory.releaseRegistry()), address(releaseRegistry) ); - assertEq(registryFactory.name(), "Custom Vault Registry Factory"); + assertEq(registryFactory.name(), "Yearn V3 Vault Registry Factory"); } function test__new_registry() public { From bb559dab2bc97b7b646b7a8f565a11ac74a50d05 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Fri, 11 Oct 2024 15:52:44 -0600 Subject: [PATCH 05/12] chore: dont revert --- src/registry/ReleaseRegistry.sol | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/registry/ReleaseRegistry.sol b/src/registry/ReleaseRegistry.sol index e6c9bda..0b2113d 100644 --- a/src/registry/ReleaseRegistry.sol +++ b/src/registry/ReleaseRegistry.sol @@ -47,28 +47,31 @@ contract ReleaseRegistry is Governance2Step { /** * @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. - * @dev Throws if no releases are registered yet. * @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 } From 13a369a1873be7f0abf4bb850d3e1bdc2391d6e1 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Mon, 14 Oct 2024 11:54:45 -0600 Subject: [PATCH 06/12] fix: comments --- src/debtAllocators/DebtAllocator.sol | 25 +++++++++++++++---------- src/registry/Registry.sol | 2 +- src/registry/ReleaseRegistry.sol | 3 ++- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/debtAllocators/DebtAllocator.sol b/src/debtAllocators/DebtAllocator.sol index f83b060..a707eea 100644 --- a/src/debtAllocators/DebtAllocator.sol +++ b/src/debtAllocators/DebtAllocator.sol @@ -17,6 +17,7 @@ interface IBaseFee { * Yearn V3 vaults to provide the needed triggers for a keeper * to perform automated debt updates for the vaults strategies. * + * @dev * Each vault that should be managed by this allocator will * need to be added by first setting a `minimumChange` for the * vault, which will act as the minimum amount of funds to move that will @@ -26,7 +27,7 @@ interface IBaseFee { * The allocator aims to allocate debt between the strategies * based on their set target ratios. Which are denominated in basis * points and represent the percent of total assets that specific - * strategy should hold. + * strategy should hold (i.e 1_000 == 10% of the vaults `totalAssets`). * * The trigger will attempt to allocate up to the `maxRatio` when * the strategy has `minimumChange` amount less than the `targetRatio`. @@ -408,9 +409,11 @@ contract DebtAllocator is Governance { address _strategy, uint256 _increase ) external virtual { - uint256 _currentRatio = getStrategyConfig(_vault, _strategy) - .targetRatio; - setStrategyDebtRatio(_vault, _strategy, _currentRatio + _increase); + setStrategyDebtRatio( + _vault, + _strategy, + getStrategyTargetRatio(_vault, _strategy) + _increase + ); } /** @@ -423,9 +426,11 @@ contract DebtAllocator is Governance { address _strategy, uint256 _decrease ) external virtual { - uint256 _currentRatio = getStrategyConfig(_vault, _strategy) - .targetRatio; - setStrategyDebtRatio(_vault, _strategy, _currentRatio - _decrease); + setStrategyDebtRatio( + _vault, + _strategy, + getStrategyTargetRatio(_vault, _strategy) - _decrease + ); } /** @@ -460,7 +465,7 @@ contract DebtAllocator is Governance { uint256 _targetRatio, uint256 _maxRatio ) public virtual onlyManagers { - VaultConfig storage vaultConfig = _vaultConfigs[_vault]; + VaultConfig storage vaultConfig = getVaultConfig(_vault); // Make sure a minimumChange has been set. require(vaultConfig.minimumChange != 0, "!minimum"); // Cannot be more than 100%. @@ -734,7 +739,7 @@ contract DebtAllocator is Governance { function getStrategyTargetRatio( address _vault, address _strategy - ) external view virtual returns (uint256) { + ) public view virtual returns (uint256) { return getStrategyConfig(_vault, _strategy).targetRatio; } @@ -747,7 +752,7 @@ contract DebtAllocator is Governance { function getStrategyMaxRatio( address _vault, address _strategy - ) external view virtual returns (uint256) { + ) public view virtual returns (uint256) { return getStrategyConfig(_vault, _strategy).maxRatio; } diff --git a/src/registry/Registry.sol b/src/registry/Registry.sol index c197c01..a2ab190 100644 --- a/src/registry/Registry.sol +++ b/src/registry/Registry.sol @@ -21,7 +21,7 @@ interface IVaultFactory { * @title YearnV3 Registry * @author yearn.finance * @notice - * Serves as an on chain registry to track any Yearn + * Serves as an on chain registry to track any Yearn V3 * vaults and strategies that a certain party wants to * endorse. * diff --git a/src/registry/ReleaseRegistry.sol b/src/registry/ReleaseRegistry.sol index 0b2113d..55c006b 100644 --- a/src/registry/ReleaseRegistry.sol +++ b/src/registry/ReleaseRegistry.sol @@ -72,7 +72,7 @@ contract ReleaseRegistry is Governance2Step { function latestRelease() external view virtual returns (string memory) { uint256 _numReleases = numReleases; if (_numReleases == 0) return ""; - return IFactory(factories[numReleases - 1]).apiVersion(); // dev: no release + return IFactory(factories[numReleases - 1]).apiVersion(); } /** @@ -82,6 +82,7 @@ contract ReleaseRegistry is Governance2Step { * * Throws if caller isn't `governance`. * Throws if the api version is the same as the previous release. + * Throws if the factory does not have the same api version as the tokenized strategy. * Emits a `NewRelease` event. * * @param _factory The factory that will be used create new vaults. From 29c77fe8b186716bebc7bbdbe8b84aebadc28416 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Mon, 14 Oct 2024 14:23:49 -0600 Subject: [PATCH 07/12] fix: comments --- README.md | 3 ++- src/debtAllocators/DebtAllocator.sol | 7 ++----- src/debtAllocators/DebtOptimizerApplicator.sol | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index dbe22f8..79f560c 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,11 @@ NOTE: If you are on a windows machine it is recommended to use [WSL](https://lea ### Fork this repository - git clone https://github.com/yearn/vault-periphery + git clone --recursive https://github.com/yearn/vault-periphery cd vault-periphery + pip install vyper==0.3.7 ### Deployment diff --git a/src/debtAllocators/DebtAllocator.sol b/src/debtAllocators/DebtAllocator.sol index a707eea..f2aa0ec 100644 --- a/src/debtAllocators/DebtAllocator.sol +++ b/src/debtAllocators/DebtAllocator.sol @@ -121,7 +121,6 @@ contract DebtAllocator is Governance { uint256 maxDebt; uint256 currentIdle; uint256 minIdle; - //uint256 max; uint256 toChange; } @@ -303,8 +302,6 @@ contract DebtAllocator is Governance { 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, @@ -465,7 +462,7 @@ contract DebtAllocator is Governance { uint256 _targetRatio, uint256 _maxRatio ) public virtual onlyManagers { - VaultConfig storage vaultConfig = getVaultConfig(_vault); + VaultConfig memory vaultConfig = getVaultConfig(_vault); // Make sure a minimumChange has been set. require(vaultConfig.minimumChange != 0, "!minimum"); // Cannot be more than 100%. @@ -499,7 +496,7 @@ contract DebtAllocator is Governance { // Write to storage. _strategyConfigs[_vault][_strategy] = strategyConfig; - vaultConfig.totalDebtRatio = uint16(newTotalDebtRatio); + _vaultConfigs[_vault].totalDebtRatio = uint16(newTotalDebtRatio); emit UpdateStrategyDebtRatio( _vault, diff --git a/src/debtAllocators/DebtOptimizerApplicator.sol b/src/debtAllocators/DebtOptimizerApplicator.sol index f6d41ea..4b96e72 100644 --- a/src/debtAllocators/DebtOptimizerApplicator.sol +++ b/src/debtAllocators/DebtOptimizerApplicator.sol @@ -43,7 +43,7 @@ contract DebtOptimizerApplicator { ); } - /// @notice The address of the debt allocator factory to use for some role checks. + /// @notice The address of the debt allocator. address public immutable debtAllocator; /// @notice Mapping of addresses that are allowed to update debt ratios. From 75fd3267645235c465cbf98da434edeebd84b962 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Mon, 14 Oct 2024 15:33:10 -0600 Subject: [PATCH 08/12] fix: readme --- README.md | 27 ++++++++----------- src/accountants/Accountant.sol | 4 +-- .../ProtocolAddressProvider.vy | 2 +- src/debtAllocators/DebtAllocator.sol | 6 +++-- src/managers/RoleManager.sol | 6 +++-- src/managers/RoleManagerFactory.sol | 4 +-- src/registry/Registry.sol | 2 +- src/test/utils/VyperDeployer.sol | 2 +- 8 files changed, 26 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 79f560c..3483c58 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,22 @@ - First you will need to install [Foundry](https://book.getfoundry.sh/getting-started/installation). NOTE: If you are on a windows machine it is recommended to use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) -- Install [Node.js](https://nodejs.org/en/download/package-manager/) ### Fork this repository - git clone --recursive https://github.com/yearn/vault-periphery +```sh +git clone --recursive https://github.com/yearn/vault-periphery - cd vault-periphery +cd vault-periphery - pip install vyper==0.3.7 - -### Deployment +pip install vyper==0.3.7 + +make build -Deployment of periphery contracts such as the [Registry Factory](https://github.com/yearn/vault-periphery/blob/master/contracts/registry/RegistryFactory.sol) or [Address Provider](https://github.com/yearn/vault-periphery/blob/master/contracts/AddressProvider.vy) are done using a create2 factory in order to get a deterministic address that is the same on each EVM chain. +make test +``` +### Deployment -This can be done permissionlessly if the most recent contract has not yet been deployed on a chain you would like to use it on. +Deployment of periphery contracts are done using a create2 factory in order to get a deterministic address that is the same on each EVM chain. -1. [Add an Ape account](https://docs.apeworx.io/ape/stable/commands/accounts.html) -2. Run the deployment the contracts specific deployment script under `scripts/` - ```sh - ape run scripts/deploy_contract_name.py --network YOUR_RPC_URL - ``` - - For chains that don't support 1559 tx's you may need to add a `type="0x0"` argument at the end of the deployment tx. - - ie `tx = deployer_contract.deployCreate2(salt, init_code, sender=deployer, type="0x0")` -3. The address the contract was deployed at will print in the console and should match any other chain the same version has been deployed on. \ No newline at end of file +This can be done permissionlessly if the most recent contract has not yet been deployed on a chain you would like to use it on using this repo https://github.com/wavey0x/yearn-v3-deployer diff --git a/src/accountants/Accountant.sol b/src/accountants/Accountant.sol index 8b90305..acc63ff 100644 --- a/src/accountants/Accountant.sol +++ b/src/accountants/Accountant.sol @@ -306,8 +306,8 @@ contract Accountant { } /** - * @notice Function to update the default fee configuration used for - all strategies that don't have a custom config set. + * @notice Function to update the default fee configuration used for + * all strategies that don't have a custom config set. * @param defaultManagement Default annual management fee to charge. * @param defaultPerformance Default performance fee to charge. * @param defaultRefund Default refund ratio to give back on losses. diff --git a/src/addressProviders/ProtocolAddressProvider.vy b/src/addressProviders/ProtocolAddressProvider.vy index b00956a..e99108a 100644 --- a/src/addressProviders/ProtocolAddressProvider.vy +++ b/src/addressProviders/ProtocolAddressProvider.vy @@ -40,9 +40,9 @@ COMMON_REPORT_TRIGGER: constant(bytes32) = keccak256("Common Report Trigger") AUCTION_FACTORY: constant(bytes32) = keccak256("Auction Factory") SPLITTER_FACTORY: constant(bytes32) = keccak256("Splitter Factory") REGISTRY_FACTORY: constant(bytes32) = keccak256("Registry Factory") -DEBT_ALLOCATOR_FACTORY: constant(bytes32) = keccak256("Debt Allocator Factory") ACCOUNTANT_FACTORY: constant(bytes32) = keccak256("Accountant Factory") ROLE_MANAGER_FACTORY: constant(bytes32) = keccak256("Role Manager Factory") +DEBT_ALLOCATOR_FACTORY: constant(bytes32) = keccak256("Debt Allocator Factory") name: public(constant(String[34])) = "Yearn V3 Protocol Address Provider" diff --git a/src/debtAllocators/DebtAllocator.sol b/src/debtAllocators/DebtAllocator.sol index f2aa0ec..a7c20bb 100644 --- a/src/debtAllocators/DebtAllocator.sol +++ b/src/debtAllocators/DebtAllocator.sol @@ -247,8 +247,9 @@ contract DebtAllocator is Governance { strategyDebtInfo.vaultConfig = getVaultConfig(_vault); // Don't do anything if paused. - if (strategyDebtInfo.vaultConfig.paused) + if (strategyDebtInfo.vaultConfig.paused) { return (false, bytes("Paused")); + } // Check the base fee isn't too high. if (!isCurrentBaseFeeAcceptable()) return (false, bytes("Base Fee")); @@ -257,8 +258,9 @@ contract DebtAllocator is Governance { strategyDebtInfo.strategyConfig = getStrategyConfig(_vault, _strategy); // Make sure the strategy has been added to the allocator. - if (!strategyDebtInfo.strategyConfig.added) + if (!strategyDebtInfo.strategyConfig.added) { return (false, bytes("!added")); + } if ( block.timestamp - strategyDebtInfo.strategyConfig.lastUpdate <= diff --git a/src/managers/RoleManager.sol b/src/managers/RoleManager.sol index dc8a110..27d04d0 100644 --- a/src/managers/RoleManager.sol +++ b/src/managers/RoleManager.sol @@ -210,10 +210,11 @@ contract RoleManager is Positions { // Check that a vault does not exist for that asset, api and category. // This reverts late to not waste gas when used correctly. string memory _apiVersion = IVault(_vault).apiVersion(); - if (_assetToVault[_asset][_apiVersion][_category] != address(0)) + if (_assetToVault[_asset][_apiVersion][_category] != address(0)) { revert AlreadyDeployed( _assetToVault[_asset][_apiVersion][_category] ); + } address _debtAllocator = getPositionHolder(DEBT_ALLOCATOR); // Give out roles on the new vault. @@ -367,10 +368,11 @@ contract RoleManager is Positions { // Check that a vault does not exist for that asset, api and category. address _asset = IVault(_vault).asset(); string memory _apiVersion = IVault(_vault).apiVersion(); - if (_assetToVault[_asset][_apiVersion][_category] != address(0)) + if (_assetToVault[_asset][_apiVersion][_category] != address(0)) { revert AlreadyDeployed( _assetToVault[_asset][_apiVersion][_category] ); + } // If not the current role manager. if (IVault(_vault).role_manager() != address(this)) { diff --git a/src/managers/RoleManagerFactory.sol b/src/managers/RoleManagerFactory.sol index ef6c4a2..63aafe2 100644 --- a/src/managers/RoleManagerFactory.sol +++ b/src/managers/RoleManagerFactory.sol @@ -83,8 +83,8 @@ contract RoleManagerFactory is Clonable { /** * @notice Create a new project with associated periphery contracts. - This will deploy and complete full setup with default configuration for - a new V3 project to exist. + * This will deploy and complete full setup with default configuration for + * a new V3 project to exist. * @param _name The name of the project * @param _governance The address of governance to use * @param _management The address of management to use diff --git a/src/registry/Registry.sol b/src/registry/Registry.sol index a2ab190..6f7cd82 100644 --- a/src/registry/Registry.sol +++ b/src/registry/Registry.sol @@ -306,7 +306,7 @@ contract Registry is Governance { * @notice Endorse an already deployed multi strategy vault. * @dev To be used with default values for `_releaseDelta`, `_vaultType` * and `_deploymentTimestamp`. - + * * @param _vault Address of the vault to endorse. */ function endorseMultiStrategyVault(address _vault) external virtual { diff --git a/src/test/utils/VyperDeployer.sol b/src/test/utils/VyperDeployer.sol index b739b6e..fe9cd1f 100644 --- a/src/test/utils/VyperDeployer.sol +++ b/src/test/utils/VyperDeployer.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.13; + import "forge-std/console.sol"; ///@notice This cheat codes interface is named _CheatCodes so you can use the CheatCodes interface in other testing files without errors @@ -15,7 +16,6 @@ interface _CheatCodes { * and deploys the corresponding Vyper contract, returning the address * that the bytecode was deployed to. */ - contract VyperDeployer { address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); From 273db84e31c0c548e006b39848ca9f51e987af02 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Mon, 14 Oct 2024 15:34:16 -0600 Subject: [PATCH 09/12] chore: remove scripts --- scripts/deploy_accountant.py | 78 ---------------------------- scripts/deploy_accountant_factory.py | 35 ------------- scripts/deploy_address_provider.py | 41 --------------- scripts/deploy_allocator_factory.py | 43 --------------- scripts/deploy_keeper.py | 35 ------------- scripts/deploy_registry.py | 72 ------------------------- scripts/deploy_role_manager.py | 49 ----------------- scripts/deploy_splitter_factory.py | 52 ------------------- scripts/deployments.py | 31 ----------- 9 files changed, 436 deletions(-) delete mode 100644 scripts/deploy_accountant.py delete mode 100644 scripts/deploy_accountant_factory.py delete mode 100644 scripts/deploy_address_provider.py delete mode 100644 scripts/deploy_allocator_factory.py delete mode 100644 scripts/deploy_keeper.py delete mode 100644 scripts/deploy_registry.py delete mode 100644 scripts/deploy_role_manager.py delete mode 100644 scripts/deploy_splitter_factory.py delete mode 100644 scripts/deployments.py diff --git a/scripts/deploy_accountant.py b/scripts/deploy_accountant.py deleted file mode 100644 index 9d366a5..0000000 --- a/scripts/deploy_accountant.py +++ /dev/null @@ -1,78 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_accountant(): - print("Deploying an Accountant on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - accountant - salt = getSalt(f"Accountant {chain.pending_timestamp}") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - version = input( - "Would you like to deploy a normal Accountant a Refund Accountant? g/r " - ).lower() - - if version == "g": - print("Deploying an Accountant.") - accountant = project.Accountant - - else: - print("Deploying a Refund accountant.") - accountant = project.RefundAccountant - - print("Enter the default amounts to use in Base Points. (100% == 10_000)") - - management_fee = input("Default management fee? ") - assert int(management_fee) <= 200 - - performance_fee = input("Default performance fee? ") - assert int(performance_fee) <= 5_000 - - refund_ratio = input("Default refund ratio? ") - assert int(refund_ratio) <= 2**16 - 1 - - max_fee = input("Default max fee? ") - assert int(max_fee) <= 2**16 - 1 - - max_gain = input("Default max gain? ") - assert int(max_gain) <= 2**16 - 1 - - max_loss = input("Default max loss? ") - assert int(max_loss) <= 10_000 - - constructor = accountant.constructor.encode_input( - deployer.address, - deployer.address, - management_fee, - performance_fee, - refund_ratio, - max_fee, - max_gain, - max_loss, - ) - - # generate and deploy - deploy_bytecode = HexBytes( - HexBytes(accountant.contract_type.deployment_bytecode.bytecode) + constructor - ) - - print(f"Deploying Accountant...") - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - print(f"Encoded Constructor to use for verifaction {constructor.hex()[2:]}") - - -def main(): - deploy_accountant() diff --git a/scripts/deploy_accountant_factory.py b/scripts/deploy_accountant_factory.py deleted file mode 100644 index f520cdc..0000000 --- a/scripts/deploy_accountant_factory.py +++ /dev/null @@ -1,35 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_accountant_factory(): - print("Deploying an Accountant Factory on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - accountant_factory = project.AccountantFactory - - salt = getSalt(f"Accountant Factory") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - # generate and deploy - deploy_bytecode = HexBytes( - accountant_factory.contract_type.deployment_bytecode.bytecode - ) - - print(f"Deploying Accountant actory...") - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - - -def main(): - deploy_accountant_factory() diff --git a/scripts/deploy_address_provider.py b/scripts/deploy_address_provider.py deleted file mode 100644 index ae9732f..0000000 --- a/scripts/deploy_address_provider.py +++ /dev/null @@ -1,41 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_address_provider(): - print("Deploying Address Provider on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - address_provider = project.ProtocolAddressProvider - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - salt = getSalt("Protocol Address Provider") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - # generate and deploy - constructor = address_provider.constructor.encode_input( - "0x33333333D5eFb92f19a5F94a43456b3cec2797AE" - ) - - deploy_bytecode = HexBytes( - HexBytes(address_provider.contract_type.deployment_bytecode.bytecode) - + constructor - ) - - print(f"Deploying Address Provider...") - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - print(f"Encoded Constructor to use for verifaction {constructor.hex()[2:]}") - - -def main(): - deploy_address_provider() diff --git a/scripts/deploy_allocator_factory.py b/scripts/deploy_allocator_factory.py deleted file mode 100644 index bd9c8e8..0000000 --- a/scripts/deploy_allocator_factory.py +++ /dev/null @@ -1,43 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_allocator_factory(): - print("Deploying Debt Allocator Factory on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - allocator_factory = project.DebtAllocatorFactory - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - salt = getSalt("Debt Allocator Factory") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - gov = input("Governance? ") - - allocator_constructor = allocator_factory.constructor.encode_input(gov) - - # generate and deploy - deploy_bytecode = HexBytes( - HexBytes(allocator_factory.contract_type.deployment_bytecode.bytecode) - + allocator_constructor - ) - - print(f"Deploying the Factory...") - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - print( - f"Encoded Constructor to use for verifaction {allocator_constructor.hex()[2:]}" - ) - - -def main(): - deploy_allocator_factory() diff --git a/scripts/deploy_keeper.py b/scripts/deploy_keeper.py deleted file mode 100644 index 863b6f8..0000000 --- a/scripts/deploy_keeper.py +++ /dev/null @@ -1,35 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_keeper(): - print("Deploying Keeper on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - keeper = project.Keeper - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - salt = getSalt("Keeper") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - # generate and deploy - deploy_bytecode = HexBytes( - HexBytes(keeper.contract_type.deployment_bytecode.bytecode) - ) - - print(f"Deploying the Keeper...") - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - - -def main(): - deploy_keeper() diff --git a/scripts/deploy_registry.py b/scripts/deploy_registry.py deleted file mode 100644 index 5e27e5d..0000000 --- a/scripts/deploy_registry.py +++ /dev/null @@ -1,72 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_release_and_factory(): - print("Deploying Vault Registry on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - release_registry = project.ReleaseRegistry - factory = project.RegistryFactory - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - salt = getSalt("registry") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - release_address = "0x990089173D5d5287c344092Be0bB37950A67d17B" - - if input("Do you want to deploy a new Release Registry? ") == "y": - - # generate and deploy release registry - release_constructor = release_registry.constructor.encode_input( - "0x33333333D5eFb92f19a5F94a43456b3cec2797AE" - ) - - release_deploy_bytecode = HexBytes( - HexBytes(release_registry.contract_type.deployment_bytecode.bytecode) - + release_constructor - ) - - print(f"Deploying Release Registry...") - - # Use old deployer contract to get the same address. - deployer_contract = project.Deployer.at( - "0x8D85e7c9A4e369E53Acc8d5426aE1568198b0112" - ) - - release_tx = deployer_contract.deploy( - release_deploy_bytecode, salt, sender=deployer - ) - - release_event = list(release_tx.decode_logs(deployer_contract.Deployed)) - - release_address = release_event[0].addr - - print(f"Deployed the vault release to {release_address}") - print("------------------") - print(f"Encoded Constructor to use for verifaction {release_constructor.hex()}") - - # Deploy factory - print(f"Deploying factory...") - - factory_constructor = factory.constructor.encode_input(release_address) - - factory_deploy_bytecode = HexBytes( - HexBytes(factory.contract_type.deployment_bytecode.bytecode) - + factory_constructor - ) - - deploy_contract(factory_deploy_bytecode, salt, deployer) - - print("------------------") - print(f"Encoded Constructor to use for verifaction {factory_constructor.hex()[2:]}") - - -def main(): - deploy_release_and_factory() diff --git a/scripts/deploy_role_manager.py b/scripts/deploy_role_manager.py deleted file mode 100644 index e1cab53..0000000 --- a/scripts/deploy_role_manager.py +++ /dev/null @@ -1,49 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_role_manager(): - - print("Deploying Role Manager on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - role_manager = project.RoleManager - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - salt = getSalt("Role Manager") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - print(f"Deploying the Role Manager...") - print("Enter the addresses to use on deployment.") - - gov = input("Governance? ") - daddy = input("Daddy? ") - brain = input("Brain? ") - security = input("Security? ") - keeper = input("Keeper? ") - strategy_manager = input("Strategy manager? ") - registry = input("Registry? ") - - constructor = role_manager.constructor.encode_input( - gov, daddy, brain, security, keeper, strategy_manager, registry - ) - - deploy_bytecode = HexBytes( - HexBytes(role_manager.contract_type.deployment_bytecode.bytecode) + constructor - ) - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - print(f"Encoded Constructor to use for verifaction {constructor.hex()[2:]}") - - -def main(): - deploy_role_manager() diff --git a/scripts/deploy_splitter_factory.py b/scripts/deploy_splitter_factory.py deleted file mode 100644 index 7bba33a..0000000 --- a/scripts/deploy_splitter_factory.py +++ /dev/null @@ -1,52 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -from scripts.deployments import getSalt, deploy_contract - - -def deploy_splitter_factory(): - print("Deploying Splitter Factory on ChainID", chain.chain_id) - - if input("Do you want to continue? ") == "n": - return - - splitter = project.Splitter - splitter_factory = project.SplitterFactory - - deployer = input("Name of account to use? ") - deployer = accounts.load(deployer) - - salt = getSalt("Splitter Factory") - - print(f"Salt we are using {salt}") - print("Init balance:", deployer.balance / 1e18) - - print(f"Deploying Original.") - - original_deploy_bytecode = HexBytes( - HexBytes(splitter.contract_type.deployment_bytecode.bytecode) - ) - - original_address = deploy_contract(original_deploy_bytecode, salt, deployer) - - print(f"Original deployed to {original_address}") - - allocator_constructor = splitter_factory.constructor.encode_input(original_address) - - # generate and deploy - deploy_bytecode = HexBytes( - HexBytes(splitter_factory.contract_type.deployment_bytecode.bytecode) - + allocator_constructor - ) - - print(f"Deploying the Factory...") - - deploy_contract(deploy_bytecode, salt, deployer) - - print("------------------") - print( - f"Encoded Constructor to use for verifaction {allocator_constructor.hex()[2:]}" - ) - - -def main(): - deploy_splitter_factory() diff --git a/scripts/deployments.py b/scripts/deployments.py deleted file mode 100644 index 92e36ec..0000000 --- a/scripts/deployments.py +++ /dev/null @@ -1,31 +0,0 @@ -from ape import project, accounts, Contract, chain, networks -from hexbytes import HexBytes -import hashlib - - -def getSalt(salt_string): - # Create a SHA-256 hash object - hash_object = hashlib.sha256() - # Update the hash object with the string data - hash_object.update(salt_string.encode("utf-8")) - # Get the hexadecimal representation of the hash - hex_hash = hash_object.hexdigest() - # Convert the hexadecimal hash to an integer - return int(hex_hash, 16) - - -def deploy_contract(init_code, salt, deployer): - deployer_contract = project.Deployer.at( - "0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed" - ) - - tx = deployer_contract.deployCreate2(salt, init_code, sender=deployer) - - event = list(tx.decode_logs(deployer_contract.ContractCreation)) - - address = event[0].newContract - - print("------------------") - print(f"Deployed the contract to {address}") - - return address From cac6bee8f35f33202e3991c7ede7447b51e77e4f Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Mon, 14 Oct 2024 18:02:43 -0600 Subject: [PATCH 10/12] fix: final edits --- .gitignore | 1 + .prettierignore | 3 +- scripts/Deploy.s.sol | 41 +++++++++++++ scripts/DeployVyper.s.sol | 65 ++++++++++++++++++++ src/managers/RoleManager.sol | 11 ++-- src/managers/RoleManagerFactory.sol | 11 ++-- src/registry/Registry.sol | 28 ++++++++- src/test/registry/TestRegistry.t.sol | 88 ++++++++++++++++++++++++++++ 8 files changed, 234 insertions(+), 14 deletions(-) create mode 100644 scripts/Deploy.s.sol create mode 100644 scripts/DeployVyper.s.sol diff --git a/.gitignore b/.gitignore index 4836c80..c9d26e8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ out/ !/broadcast /broadcast/*/31337/ /broadcast/**/dry-run/ +/broadcast/ # Docs docs/ diff --git a/.prettierignore b/.prettierignore index dcf7dcd..940145c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,4 +6,5 @@ venv/ venv/ lib/ out/ -cache/ \ No newline at end of file +cache/ +broadcast/ \ No newline at end of file diff --git a/scripts/Deploy.s.sol b/scripts/Deploy.s.sol new file mode 100644 index 0000000..e65bc19 --- /dev/null +++ b/scripts/Deploy.s.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity >=0.8.18; + +import "forge-std/Script.sol"; + +// Deploy a contract to a deterministic address with create2 factory. +contract Deploy is Script { + // Create X address. + Deployer public deployer = + Deployer(0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed); + + address public initGov = 0x6f3cBE2ab3483EC4BA7B672fbdCa0E9B33F88db8; + + function run() external { + vm.startBroadcast(); + + // Append constructor args to the bytecode + bytes memory bytecode = abi.encodePacked( + vm.getCode("registry/ReleaseRegistry.sol:ReleaseRegistry"), + abi.encode(initGov) + ); + + // Use salt of 0. + bytes32 salt; + + address contractAddress = deployer.deployCreate2(salt, bytecode); + + console.log("Address is ", contractAddress); + + vm.stopBroadcast(); + } +} + +contract Deployer { + event ContractCreation(address indexed newContract, bytes32 indexed salt); + + function deployCreate2( + bytes32 salt, + bytes memory initCode + ) public payable returns (address newContract) {} +} diff --git a/scripts/DeployVyper.s.sol b/scripts/DeployVyper.s.sol new file mode 100644 index 0000000..b4daf37 --- /dev/null +++ b/scripts/DeployVyper.s.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity >=0.8.18; + +import "forge-std/Script.sol"; + +///@notice This cheat codes interface is named _CheatCodes so you can use the CheatCodes interface in other testing files without errors +interface _CheatCodes { + function ffi(string[] calldata) external returns (bytes memory); +} + +// Deploy a contract to a deterministic address with create2 factory. +contract DeployVyper is Script { + address constant HEVM_ADDRESS = + address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); + + /// @notice Initializes cheat codes in order to use ffi to compile Vyper contracts + _CheatCodes cheatCodes = _CheatCodes(HEVM_ADDRESS); + + // Create X address. + Deployer public deployer = + Deployer(0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed); + + address public initGov = 0x6f3cBE2ab3483EC4BA7B672fbdCa0E9B33F88db8; + + function run() external { + vm.startBroadcast(); + + ///@notice compile the Vyper contract and return the bytecode + bytes memory bytecode = compileVyper( + "src/addressProviders/", + "ProtocolAddressProvider" + ); + + bytecode = abi.encodePacked(bytecode, abi.encode(initGov)); + + // Use salt of 0. + bytes32 salt; + + address contractAddress = deployer.deployCreate2(salt, bytecode); + + console.log("Address is ", contractAddress); + + vm.stopBroadcast(); + } + + function compileVyper( + string memory path, + string memory fileName + ) public returns (bytes memory) { + string[] memory cmds = new string[](2); + cmds[0] = "vyper"; + cmds[1] = string.concat(path, fileName, ".vy"); + + return cheatCodes.ffi(cmds); + } +} + +contract Deployer { + event ContractCreation(address indexed newContract, bytes32 indexed salt); + + function deployCreate2( + bytes32 salt, + bytes memory initCode + ) public payable returns (address newContract) {} +} diff --git a/src/managers/RoleManager.sol b/src/managers/RoleManager.sol index 27d04d0..4acc8c2 100644 --- a/src/managers/RoleManager.sol +++ b/src/managers/RoleManager.sol @@ -7,10 +7,7 @@ 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. contract RoleManager is Positions { @@ -57,6 +54,7 @@ contract RoleManager is Positions { bytes32 public constant GOVERNANCE = keccak256("Governance"); /// @notice Position ID for "brain". bytes32 public constant MANAGEMENT = keccak256("Management"); + /// @notice Position ID for "keeper". bytes32 public constant KEEPER = keccak256("Keeper"); /// @notice Position ID for the Registry. @@ -70,7 +68,7 @@ contract RoleManager is Positions { STORAGE //////////////////////////////////////////////////////////////*/ - /// @notice Immutable address that the RoleManager position + /// @notice Immutable address that the `role_manager` position // will be transferred to when a vault is removed. address public chad; @@ -91,7 +89,7 @@ contract RoleManager is Positions { internal _assetToVault; constructor() { - chad == address(1); + chad = address(1); } function initialize( @@ -106,9 +104,8 @@ contract RoleManager is Positions { require(chad == address(0), "initialized"); require(_governance != address(0), "ZERO ADDRESS"); - projectName = _projectName; chad = _governance; - + projectName = _projectName; defaultProfitMaxUnlockTime = 10 days; // Governance gets all the roles. diff --git a/src/managers/RoleManagerFactory.sol b/src/managers/RoleManagerFactory.sol index 63aafe2..1b7dc29 100644 --- a/src/managers/RoleManagerFactory.sol +++ b/src/managers/RoleManagerFactory.sol @@ -24,17 +24,17 @@ contract RoleManagerFactory is Clonable { address debtAllocator; } - bytes32 public constant KEEPER = keccak256("Keeper"); - /// @notice Position ID for the Registry. - bytes32 public constant REGISTRY_FACTORY = keccak256("Registry Factory"); /// @notice Position ID for the Accountant. bytes32 public constant ACCOUNTANT_FACTORY = keccak256("Accountant Factory"); /// @notice Position ID for Debt Allocator Factory bytes32 public constant DEBT_ALLOCATOR_FACTORY = keccak256("Debt Allocator Factory"); + bytes32 public constant KEEPER = keccak256("Keeper"); + /// @notice Position ID for the Registry. + bytes32 public constant REGISTRY_FACTORY = keccak256("Registry Factory"); - string public apiVersion = "v3.0.3"; + string public apiVersion = "3.0.3"; address public immutable protocolAddressProvider; @@ -98,7 +98,7 @@ contract RoleManagerFactory is Clonable { bytes32 _id = getProjectId(_name, _governance); require(projects[_id].roleManager == address(0), "project exists"); - // Deploy new Registry + // Deploy the needed periphery contracts. address _registry = RegistryFactory( _fromAddressProvider(REGISTRY_FACTORY) ).createNewRegistry(string(abi.encodePacked(_name, " Registry"))); @@ -111,6 +111,7 @@ contract RoleManagerFactory is Clonable { _fromAddressProvider(DEBT_ALLOCATOR_FACTORY) ).newDebtAllocator(_management); + // Clone and initialize the RoleManager. _roleManager = _clone(); RoleManager(_roleManager).initialize( diff --git a/src/registry/Registry.sol b/src/registry/Registry.sol index 6f7cd82..cf7d224 100644 --- a/src/registry/Registry.sol +++ b/src/registry/Registry.sol @@ -105,6 +105,9 @@ contract Registry is Governance { // Custom name for this Registry. string public name; + // Old version of the registry to fall back to if exists. + address public legacyRegistry; + // Mapping for any address that is allowed to tag a vault. mapping(address => bool) public taggers; @@ -209,7 +212,20 @@ contract Registry is Governance { * @return . The vaults endorsement status. */ function isEndorsed(address _vault) external view virtual returns (bool) { - return vaultInfo[_vault].asset != address(0); + return vaultInfo[_vault].asset != address(0) || isLegacyVault(_vault); + } + + /** + * @notice Check if a vault is endorsed in the legacy registry. + * @param _vault The vault to check. + * @return True if the vault is endorsed in the legacy registry, false otherwise. + */ + function isLegacyVault(address _vault) public view virtual returns (bool) { + address _legacy = legacyRegistry; + + if (_legacy == address(0)) return false; + + return Registry(_legacy).isEndorsed(_vault); } /** @@ -533,4 +549,14 @@ contract Registry is Governance { emit UpdateTagger(_account, _canTag); } + + /** + * @notice Set a legacy registry if one exists. + * @param _legacyRegistry The address of the legacy registry. + */ + function setLegacyRegistry( + address _legacyRegistry + ) external virtual onlyGovernance { + legacyRegistry = _legacyRegistry; + } } diff --git a/src/test/registry/TestRegistry.t.sol b/src/test/registry/TestRegistry.t.sol index ff4c6de..5b01507 100644 --- a/src/test/registry/TestRegistry.t.sol +++ b/src/test/registry/TestRegistry.t.sol @@ -969,6 +969,94 @@ contract TestRegistry is Setup { registry.transferGovernance(user); } + function test__set_legacy_registry() public { + address mockLegacyRegistry = address(1234569); + + assertEq(registry.legacyRegistry(), address(0)); + + // Non-governance address can't set legacy registry + vm.prank(user); + vm.expectRevert("!governance"); + registry.setLegacyRegistry(mockLegacyRegistry); + + // Initially, legacy registry should be zero address + assertEq(registry.legacyRegistry(), address(0)); + + // Set legacy registry + vm.prank(daddy); + registry.setLegacyRegistry(mockLegacyRegistry); + + // Check if legacy registry is set correctly + assertEq(registry.legacyRegistry(), mockLegacyRegistry); + + // Can set to zero address + vm.prank(daddy); + registry.setLegacyRegistry(address(0)); + + assertEq(registry.legacyRegistry(), address(0)); + } + + function test__is_endorsed_legacy_vault() public { + addNewRelease(releaseRegistry, vaultFactory, address(strategy), daddy); + + // Deploy a mock legacy registry + address mockLegacyRegistry = registryFactory.createNewRegistry( + "mock ", + daddy + ); + + // Deploy a new vault + string memory name = "Legacy Vault"; + string memory symbol = "lvTest"; + + vm.prank(daddy); + address legacyVaultAddress = vaultFactory.deploy_new_vault( + address(asset), + name, + symbol, + daddy, + WEEK + ); + + assertEq(registry.isEndorsed(legacyVaultAddress), false); + assertEq( + Registry(mockLegacyRegistry).isEndorsed(legacyVaultAddress), + false + ); + + // Endorse the vault in the legacy registry + vm.prank(daddy); + Registry(mockLegacyRegistry).endorseMultiStrategyVault( + legacyVaultAddress + ); + + assertEq(registry.isEndorsed(legacyVaultAddress), false); + assertEq( + Registry(mockLegacyRegistry).isEndorsed(legacyVaultAddress), + true + ); + + // Set the mock legacy registry in the main registry + vm.prank(daddy); + registry.setLegacyRegistry(address(mockLegacyRegistry)); + + assertEq(registry.isEndorsed(legacyVaultAddress), true); + assertEq( + Registry(mockLegacyRegistry).isEndorsed(legacyVaultAddress), + true + ); + + // Check that the vault is not endorsed in the main registry + assertEq(registry.isLegacyVault(legacyVaultAddress), true); + assertEq( + Registry(mockLegacyRegistry).isLegacyVault(legacyVaultAddress), + false + ); + + (address vaultAsset, , , , , ) = registry.vaultInfo(legacyVaultAddress); + assertEq(vaultAsset, address(0)); + } + function test__transfer_governance() public { assertEq(registry.governance(), daddy); From 1303b0a36ab8054083eea2b795a326f941cb5113 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Tue, 15 Oct 2024 12:58:36 -0600 Subject: [PATCH 11/12] fix: role manager comments --- src/managers/RoleManager.sol | 12 ++++++------ src/managers/RoleManagerFactory.sol | 7 +++++-- src/test/managers/TestRoleManager.t.sol | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/managers/RoleManager.sol b/src/managers/RoleManager.sol index 4acc8c2..2d50f69 100644 --- a/src/managers/RoleManager.sol +++ b/src/managers/RoleManager.sol @@ -52,16 +52,16 @@ contract RoleManager is Positions { keccak256("Pending Governance"); /// @notice Position ID for "Governance". bytes32 public constant GOVERNANCE = keccak256("Governance"); - /// @notice Position ID for "brain". + /// @notice Position ID for "Brain". bytes32 public constant MANAGEMENT = keccak256("Management"); - /// @notice Position ID for "keeper". + /// @notice Position ID for "Keeper". bytes32 public constant KEEPER = keccak256("Keeper"); - /// @notice Position ID for the Registry. + /// @notice Position ID for the "Registry". bytes32 public constant REGISTRY = keccak256("Registry"); - /// @notice Position ID for the Accountant. + /// @notice Position ID for the "Accountant". bytes32 public constant ACCOUNTANT = keccak256("Accountant"); - /// @notice Position ID for Debt Allocator + /// @notice Position ID for the "Debt Allocator". bytes32 public constant DEBT_ALLOCATOR = keccak256("Debt Allocator"); /*////////////////////////////////////////////////////////////// @@ -106,7 +106,7 @@ contract RoleManager is Positions { chad = _governance; projectName = _projectName; - defaultProfitMaxUnlockTime = 10 days; + defaultProfitMaxUnlockTime = 7 days; // Governance gets all the roles. _setPositionHolder(GOVERNANCE, _governance); diff --git a/src/managers/RoleManagerFactory.sol b/src/managers/RoleManagerFactory.sol index 1b7dc29..ea535fa 100644 --- a/src/managers/RoleManagerFactory.sol +++ b/src/managers/RoleManagerFactory.sol @@ -87,7 +87,7 @@ contract RoleManagerFactory is Clonable { * a new V3 project to exist. * @param _name The name of the project * @param _governance The address of governance to use - * @param _management The address of management to use + * @param _management The address of management to use if any * @return _roleManager address of the newly created RoleManager for the project */ function newProject( @@ -107,9 +107,12 @@ contract RoleManagerFactory is Clonable { _fromAddressProvider(ACCOUNTANT_FACTORY) ).newAccountant(address(this), _governance); + // If management is not used, use governance as the default owner of the debt allocator. address _debtAllocator = DebtAllocatorFactory( _fromAddressProvider(DEBT_ALLOCATOR_FACTORY) - ).newDebtAllocator(_management); + ).newDebtAllocator( + _management != address(0) ? _management : _governance + ); // Clone and initialize the RoleManager. _roleManager = _clone(); diff --git a/src/test/managers/TestRoleManager.t.sol b/src/test/managers/TestRoleManager.t.sol index 16b3ead..1289fba 100644 --- a/src/test/managers/TestRoleManager.t.sol +++ b/src/test/managers/TestRoleManager.t.sol @@ -552,7 +552,7 @@ contract TestRoleManager is Setup { assertEq(newVault.roles(brain), brain_roles); assertEq(newVault.roles(address(keeper)), keeper_roles); assertEq(newVault.roles(vaultDebtAllocator), debt_allocator_roles); - assertEq(newVault.profitMaxUnlockTime(), 10 days); + assertEq(newVault.profitMaxUnlockTime(), 7 days); assertEq(address(newVault.accountant()), address(accountant)); assertTrue(accountant.vaults(address(newVault))); From 7f3a8d563a6dc6420a10e6b3844023cd3fd52632 Mon Sep 17 00:00:00 2001 From: Schlagonia Date: Tue, 15 Oct 2024 14:29:47 -0600 Subject: [PATCH 12/12] fix: github action --- .github/workflows/test.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0ed6b0a..2aba55f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -22,6 +22,11 @@ jobs: with: submodules: recursive + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.10' + - name: Install Vyper run: pip install vyper==0.3.7