diff --git a/package.json b/package.json index 7d0c4fe..92fd12e 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,9 @@ "scripts": { "clean": "rimraf cache out", "lint": "yarn lint:sol && yarn prettier:write", - "lint:sol": "yarn solhint \"{src,test,script}/**/*.sol\"", + "lint:sol": "yarn solhint \"{src}/**/*.sol\"", "postinstall": "husky install", "prettier:check": "prettier --check \"**/*.{json,md,yml}\"", "prettier:write": "prettier --write \"**/*.{json,md,yml}\"" } -} +} \ No newline at end of file diff --git a/src/factory/Factory.sol b/src/factory/Factory.sol index d2fa964..bf6885d 100644 --- a/src/factory/Factory.sol +++ b/src/factory/Factory.sol @@ -52,6 +52,7 @@ contract Factory { tenderizer = address(tenderizerImpl).clone(abi.encodePacked(asset, validator)); // Reverts if caller is not a registered factory + // Reverts if `validator` already has a registered `tenderizer` for `asset` Registry(registry).registerTenderizer(asset, validator, tenderizer); } } diff --git a/src/registry/Registry.sol b/src/registry/Registry.sol index 7110077..b86c76a 100644 --- a/src/registry/Registry.sol +++ b/src/registry/Registry.sol @@ -27,6 +27,7 @@ import { Adapter } from "core/adapters/Adapter.sol"; contract Registry is Initializable, UUPSUpgradeable, AccessControlUpgradeable, RegistryStorage { error InvalidAdapter(address adapter); error InvalidTreasury(address treasury); + error TenderizerAlreadyExists(address asset, address validator, address tenderizer); event AdapterRegistered(address indexed asset, address indexed adapter); event NewTenderizer(address indexed asset, address indexed validator, address tenderizer); @@ -57,6 +58,14 @@ contract Registry is Initializable, UUPSUpgradeable, AccessControlUpgradeable, R // Getters + /** + * @notice Returns the address of the adapter for a given asset + * @param asset Address of the underlying asset + */ + function adapter(address asset) external view returns (address) { + return _loadStorage().protocols[asset].adapter; + } + /** * @notice Returns the address of the tenderizer implementation */ @@ -66,19 +75,19 @@ contract Registry is Initializable, UUPSUpgradeable, AccessControlUpgradeable, R } /** - * @notice Returns the address of the unlocks contract + * @notice Returns the address of the treasury */ - function unlocks() external view returns (address) { + function treasury() external view returns (address) { Storage storage $ = _loadStorage(); - return $.unlocks; + return $.treasury; } /** - * @notice Returns the address of the adapter for a given asset - * @param asset Address of the underlying asset + * @notice Returns the address of the unlocks contract */ - function adapter(address asset) external view returns (address) { - return _loadStorage().protocols[asset].adapter; + function unlocks() external view returns (address) { + Storage storage $ = _loadStorage(); + return $.unlocks; } /** @@ -99,11 +108,13 @@ contract Registry is Initializable, UUPSUpgradeable, AccessControlUpgradeable, R } /** - * @notice Returns the address of the treasury + * @notice Returns the address of the tenderizer for a given asset and validator + * @param asset Address of the underlying asset + * @param validator Address of the validator + * @return Address of the tenderizer */ - function treasury() external view returns (address) { - Storage storage $ = _loadStorage(); - return $.treasury; + function getTenderizer(address asset, address validator) external view returns (address) { + return _loadStorage().tenderizers[asset][validator]; } // Setters @@ -129,6 +140,11 @@ contract Registry is Initializable, UUPSUpgradeable, AccessControlUpgradeable, R * @param tenderizer Address of the tenderizer */ function registerTenderizer(address asset, address validator, address tenderizer) external onlyRole(FACTORY_ROLE) { + Storage storage $ = _loadStorage(); + if ($.tenderizers[asset][validator] != address(0)) { + revert TenderizerAlreadyExists(asset, validator, $.tenderizers[asset][validator]); + } + $.tenderizers[asset][validator] = tenderizer; _grantRole(TENDERIZER_ROLE, tenderizer); emit NewTenderizer(asset, validator, tenderizer); } diff --git a/src/registry/RegistryStorage.sol b/src/registry/RegistryStorage.sol index 8468a78..41bd2e5 100644 --- a/src/registry/RegistryStorage.sol +++ b/src/registry/RegistryStorage.sol @@ -24,6 +24,7 @@ contract RegistryStorage { address unlocks; address treasury; mapping(address => Protocol) protocols; + mapping(address asset => mapping(address validator => address tenderizer)) tenderizers; } function _loadStorage() internal pure returns (Storage storage $) { diff --git a/test/registry/Registry.t.sol b/test/registry/Registry.t.sol index 625cf64..f516aec 100644 --- a/test/registry/Registry.t.sol +++ b/test/registry/Registry.t.sol @@ -204,6 +204,16 @@ contract RegistryTest is Test { assertEq(registry.hasRole(TENDERIZER_ROLE, tenderizer), true); } + function test_RegisterTenderizer_RevertsIfExists() public { + vm.prank(owner); + registry.grantRole(FACTORY_ROLE, factory); + vm.startPrank(factory); + registry.registerTenderizer(asset, account, tenderizer); + vm.expectRevert(abi.encodeWithSelector(Registry.TenderizerAlreadyExists.selector, asset, account, tenderizer)); + registry.registerTenderizer(asset, account, makeAddr("SECOND_TENDERIZER")); + vm.stopPrank(); + } + function test_RegisterTenderizer_RevertIfNotFactory() public { vm.prank(owner); vm.expectRevert();