-
Notifications
You must be signed in to change notification settings - Fork 112
feat: Add V4 Addresses Provider #1310
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
a57e24a
a826a62
a82f500
c012fac
80e400b
8e0bf2a
b55faac
d0631c3
6a1614a
626b556
d7e3340
5650778
efa03cc
e7c9097
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,276 @@ | ||
| // SPDX-License-Identifier: LicenseRef-BUSL | ||
| pragma solidity 0.8.28; | ||
|
|
||
| import {Ownable2StepUpgradeable} from 'src/dependencies/openzeppelin-upgradeable/Ownable2StepUpgradeable.sol'; | ||
| import {EnumerableSet} from 'src/dependencies/openzeppelin/EnumerableSet.sol'; | ||
| import {V4AddressesProviderStorage} from 'src/addresses-provider/V4AddressesProviderStorage.sol'; | ||
| import {IV4AddressesProvider} from 'src/addresses-provider/interfaces/IV4AddressesProvider.sol'; | ||
|
|
||
| /// @title V4AddressesProvider | ||
| /// @author Aave Labs | ||
| /// @notice Main registry of Aave V4 contract addresses. | ||
| abstract contract V4AddressesProvider is | ||
| V4AddressesProviderStorage, | ||
| Ownable2StepUpgradeable, | ||
| IV4AddressesProvider | ||
| { | ||
| using EnumerableSet for *; | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| string public constant CANONICAL_HUB_TAG = 'CANONICAL_HUB'; | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| string public constant CANONICAL_SPOKE_TAG = 'CANONICAL_SPOKE'; | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| string public constant TOKENIZATION_SPOKE_TAG = 'TOKENIZATION_SPOKE'; | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| string public constant TREASURY_SPOKE_TAG = 'TREASURY_SPOKE'; | ||
|
|
||
| /// @dev To be overridden by the inheriting V4AddressesProvider instance contract. | ||
| function initialize(address owner) external virtual; | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function setAddress( | ||
| string calldata name, | ||
| string calldata tag, | ||
| address newAddress | ||
| ) external onlyOwner { | ||
| _setAddress({name: name, tag: tag, newAddress: newAddress}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function setCanonicalHub(string calldata name, address hub) external onlyOwner { | ||
| _setAddress({name: name, tag: CANONICAL_HUB_TAG, newAddress: hub}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function setCanonicalSpoke(string calldata name, address spoke) external onlyOwner { | ||
| _setAddress({name: name, tag: CANONICAL_SPOKE_TAG, newAddress: spoke}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function setTokenizationSpoke(string calldata name, address spoke) external onlyOwner { | ||
| _setAddress({name: name, tag: TOKENIZATION_SPOKE_TAG, newAddress: spoke}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function setTreasurySpoke(string calldata name, address spoke) external onlyOwner { | ||
| _setAddress({name: name, tag: TREASURY_SPOKE_TAG, newAddress: spoke}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddress(bytes32 id) external view returns (address) { | ||
| return _addressEntries[id].addr; | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddress(string calldata name, string calldata tag) external view returns (address) { | ||
| return _getAddress({name: name, tag: tag}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddressEntry(bytes32 id) external view returns (AddressEntry memory) { | ||
| return _addressEntries[id]; | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTagCount() external view returns (uint256) { | ||
| return _tags.length(); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTags() external view returns (string[] memory) { | ||
| return _tags.values(); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTags(uint256 start, uint256 end) external view returns (string[] memory) { | ||
| return _tags.values(start, end); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getIdCount(string calldata tag) external view returns (uint256) { | ||
| return _taggedIds[tag].length(); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getIds(string calldata tag) external view returns (bytes32[] memory) { | ||
| return _taggedIds[tag].values(); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getIds( | ||
| string calldata tag, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (bytes32[] memory) { | ||
| return _taggedIds[tag].values(start, end); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddresses(string calldata tag) external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[tag].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddresses( | ||
| string calldata tag, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[tag].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddressIdCount(address addr) external view returns (uint256) { | ||
| return _addressIds[addr].length(); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddressIds(address addr) external view returns (bytes32[] memory) { | ||
| return _addressIds[addr].values(); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddressIds( | ||
| address addr, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (bytes32[] memory) { | ||
| return _addressIds[addr].values(start, end); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddressEntries(address addr) external view returns (AddressEntry[] memory) { | ||
| return _toEntries(_addressIds[addr].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getAddressEntries( | ||
| address addr, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (AddressEntry[] memory) { | ||
| return _toEntries(_addressIds[addr].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getCanonicalHub(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: CANONICAL_HUB_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getCanonicalHubs() external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[CANONICAL_HUB_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getCanonicalHubs(uint256 start, uint256 end) external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[CANONICAL_HUB_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getCanonicalSpoke(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: CANONICAL_SPOKE_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getCanonicalSpokes() external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[CANONICAL_SPOKE_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getCanonicalSpokes(uint256 start, uint256 end) external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[CANONICAL_SPOKE_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTokenizationSpoke(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: TOKENIZATION_SPOKE_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTokenizationSpokes() external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[TOKENIZATION_SPOKE_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTokenizationSpokes( | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[TOKENIZATION_SPOKE_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTreasurySpoke(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: TREASURY_SPOKE_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTreasurySpokes() external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[TREASURY_SPOKE_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getTreasurySpokes(uint256 start, uint256 end) external view returns (address[] memory) { | ||
| return _toAddresses(_taggedIds[TREASURY_SPOKE_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IV4AddressesProvider | ||
| function getId(string calldata name, string calldata tag) external pure returns (bytes32) { | ||
| return _getId({name: name, tag: tag}); | ||
| } | ||
|
|
||
| function _setAddress(string memory name, string memory tag, address newAddress) internal { | ||
| require(bytes(name).length > 0, InvalidName()); | ||
| require(bytes(tag).length > 0, InvalidTag()); | ||
|
|
||
| bytes32 id = _getId({name: name, tag: tag}); | ||
| AddressEntry memory oldEntry = _addressEntries[id]; | ||
|
|
||
| if (newAddress == address(0)) { | ||
| require(oldEntry.addr != address(0), AddressNotSet(id)); | ||
| _taggedIds[oldEntry.tag].remove(id); | ||
| if (_taggedIds[oldEntry.tag].length() == 0) { | ||
| _tags.remove(oldEntry.tag); | ||
| } | ||
| _addressIds[oldEntry.addr].remove(id); | ||
| delete _addressEntries[id]; | ||
| } else { | ||
| require(oldEntry.addr == address(0), AddressAlreadySet(id)); | ||
| _addressEntries[id] = AddressEntry({addr: newAddress, name: name, tag: tag}); | ||
| _taggedIds[tag].add(id); | ||
| _tags.add(tag); | ||
| _addressIds[newAddress].add(id); | ||
| } | ||
|
|
||
| emit AddressSet(id, name, tag, oldEntry.addr, newAddress); | ||
| } | ||
|
|
||
| function _getAddress(string memory name, string memory tag) internal view returns (address) { | ||
| return _addressEntries[_getId({name: name, tag: tag})].addr; | ||
| } | ||
|
|
||
| function _getId(string memory name, string memory tag) internal pure returns (bytes32) { | ||
| return keccak256(abi.encode(name, tag)); | ||
| } | ||
|
|
||
| function _toAddresses(bytes32[] memory ids) internal view returns (address[] memory) { | ||
| address[] memory addresses = new address[](ids.length); | ||
| for (uint256 i = 0; i < ids.length; i++) { | ||
| addresses[i] = _addressEntries[ids[i]].addr; | ||
| } | ||
| return addresses; | ||
| } | ||
|
|
||
| function _toEntries(bytes32[] memory ids) internal view returns (AddressEntry[] memory) { | ||
| AddressEntry[] memory entries = new AddressEntry[](ids.length); | ||
| for (uint256 i = 0; i < ids.length; i++) { | ||
| entries[i] = _addressEntries[ids[i]]; | ||
| } | ||
| return entries; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| // SPDX-License-Identifier: LicenseRef-BUSL | ||
| pragma solidity 0.8.28; | ||
|
|
||
| import {EnumerableSet} from 'src/dependencies/openzeppelin/EnumerableSet.sol'; | ||
| import {IV4AddressesProvider} from 'src/addresses-provider/interfaces/IV4AddressesProvider.sol'; | ||
|
|
||
| /// @title V4AddressesProviderStorage | ||
| /// @author Aave Labs | ||
| /// @notice Storage layout for the V4AddressesProvider contract. | ||
| /// @dev This contract defines all storage variables used by the V4AddressesProvider. | ||
| abstract contract V4AddressesProviderStorage { | ||
| /// @dev Map of entry identifiers to address entries. | ||
| mapping(bytes32 id => IV4AddressesProvider.AddressEntry) internal _addressEntries; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i d follow the same conventions we used as for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| /// @dev Map of tags to set of entry identifiers. | ||
| mapping(string tag => EnumerableSet.Bytes32Set ids) internal _taggedIds; | ||
|
|
||
| /// @dev Set of all tags with at least one registered entry. | ||
| EnumerableSet.StringSet internal _tags; | ||
|
|
||
| /// @dev Map of registered addresses to set of entry identifiers. | ||
| mapping(address addr => EnumerableSet.Bytes32Set ids) internal _addressIds; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so address - id is not 1:1?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this would mean that a single address has multiple (name,tag) combinations... don't know if that makes sense
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, as discussed we'd like to have support for multiple (name,tag) for one address, in case that address is needed for 2 different purposes: e.g., Babylon canonical spoke can be both under canonical and babylon tag |
||
|
|
||
| /// @dev Reserved storage space to allow for future layout updates. | ||
| uint256[49] private __gap; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why 49? and not 50
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thinking more its a registry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I followed the v3 naming convention here (also with the plural in "addresses" which I don't like). I'm fine changing the name, thoughts @Kogaroshi @miguelmtzinf ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to keep v3 name convention, so we don't have more difference of naming in the tooling.