-
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 11 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 | ||
|
Contributor
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. can we add it to the Deploy procedures, and the Config Engine (tho it might be managed by a different admin than Gov, can be useful to have support if Gov ends up controling the Provider)
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. yes, I'll add a commit to have procedures as well, although I was thinking to not be part of the config engine. can be separate and can be configured in an AIP, wdyt?
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.
Contributor
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, the Config Engine part can be done later on when needed.
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. I don't think it needs to be part of config engine, I'm thinking it can be configured in an AIP, wdyt?
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. hmm I see, you mean when you add a new hub/spoke. I thought on the initialisation at the moment, since we already deployed and we need to do it retroactively. I will add in config engine for future listings
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.
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. we also have the option to do this in the configurator instead
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. its more fool proof if we do it in the configurator @avniculae
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. settled offline with Dhairya: the engine now reverts if a hub/spoke it targets is not registered in the provider, and registering/unregistering is a dedicated engine action (executeAddressesProviderEntryUpdates, runs before hub/spoke actions in the payload). 5650778 |
||
| pragma solidity 0.8.28; | ||
|
|
||
| import {Ownable2StepUpgradeable} from 'src/dependencies/openzeppelin-upgradeable/Ownable2StepUpgradeable.sol'; | ||
| import {EnumerableSet} from 'src/dependencies/openzeppelin/EnumerableSet.sol'; | ||
| import {AddressesProviderStorage} from 'src/addresses-provider/AddressesProviderStorage.sol'; | ||
| import {IAddressesProvider} from 'src/addresses-provider/interfaces/IAddressesProvider.sol'; | ||
|
|
||
| /// @title AddressesProvider | ||
| /// @author Aave Labs | ||
| /// @notice Main registry of Aave V4 contract addresses. | ||
| abstract contract AddressesProvider is | ||
| AddressesProviderStorage, | ||
| Ownable2StepUpgradeable, | ||
| IAddressesProvider | ||
| { | ||
| using EnumerableSet for *; | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| string public constant CANONICAL_HUB_TAG = 'CANONICAL_HUB'; | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| string public constant CANONICAL_SPOKE_TAG = 'CANONICAL_SPOKE'; | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| string public constant TOKENIZATION_SPOKE_TAG = 'TOKENIZATION_SPOKE'; | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| string public constant TREASURY_SPOKE_TAG = 'TREASURY_SPOKE'; | ||
|
|
||
| /// @dev To be overridden by the inheriting AddressesProvider instance contract. | ||
| function initialize(address owner) external virtual; | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function setEntry( | ||
| string calldata name, | ||
| string calldata tag, | ||
| address newAddress | ||
| ) external onlyOwner { | ||
| _setEntry({name: name, tag: tag, newAddress: newAddress}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function setCanonicalHub(string calldata name, address hub) external onlyOwner { | ||
| _setEntry({name: name, tag: CANONICAL_HUB_TAG, newAddress: hub}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function setCanonicalSpoke(string calldata name, address spoke) external onlyOwner { | ||
| _setEntry({name: name, tag: CANONICAL_SPOKE_TAG, newAddress: spoke}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function setTokenizationSpoke(string calldata name, address spoke) external onlyOwner { | ||
| _setEntry({name: name, tag: TOKENIZATION_SPOKE_TAG, newAddress: spoke}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function setTreasurySpoke(string calldata name, address spoke) external onlyOwner { | ||
| _setEntry({name: name, tag: TREASURY_SPOKE_TAG, newAddress: spoke}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getAddress(bytes32 id) external view returns (address) { | ||
| return _idToEntry[id].addr; | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getAddress(string calldata name, string calldata tag) external view returns (address) { | ||
| return _getAddress({name: name, tag: tag}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getEntry(bytes32 id) external view returns (Entry memory) { | ||
| return _idToEntry[id]; | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTagCount() external view returns (uint256) { | ||
| return _tagsSet.length(); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTags() external view returns (string[] memory) { | ||
| return _tagsSet.values(); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTags(uint256 start, uint256 end) external view returns (string[] memory) { | ||
| return _tagsSet.values(start, end); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getIdCount(string calldata tag) external view returns (uint256) { | ||
| return _tagToIdSet[tag].length(); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getIds(string calldata tag) external view returns (bytes32[] memory) { | ||
| return _tagToIdSet[tag].values(); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getIds( | ||
| string calldata tag, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (bytes32[] memory) { | ||
| return _tagToIdSet[tag].values(start, end); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getAddresses(string calldata tag) external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[tag].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getAddresses( | ||
| string calldata tag, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[tag].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getAddressIdCount(address addr) external view returns (uint256) { | ||
| return _addressToIdSet[addr].length(); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getAddressIds(address addr) external view returns (bytes32[] memory) { | ||
| return _addressToIdSet[addr].values(); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getAddressIds( | ||
| address addr, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (bytes32[] memory) { | ||
| return _addressToIdSet[addr].values(start, end); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getEntries(address addr) external view returns (Entry[] memory) { | ||
| return _toEntries(_addressToIdSet[addr].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getEntries( | ||
| address addr, | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (Entry[] memory) { | ||
| return _toEntries(_addressToIdSet[addr].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getCanonicalHub(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: CANONICAL_HUB_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getCanonicalHubs() external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[CANONICAL_HUB_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getCanonicalHubs(uint256 start, uint256 end) external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[CANONICAL_HUB_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getCanonicalSpoke(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: CANONICAL_SPOKE_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getCanonicalSpokes() external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[CANONICAL_SPOKE_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getCanonicalSpokes(uint256 start, uint256 end) external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[CANONICAL_SPOKE_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTokenizationSpoke(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: TOKENIZATION_SPOKE_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTokenizationSpokes() external view returns (address[] memory) { | ||
|
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. these dont have pointer getters, i would just keep those and not have full arr return one's for all
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. we can add range getters, but would keep these for convenience as well, wdyt?
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.
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 would kill these, unnecessary take space
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. we NEED range getters bc of potential out of gas, these arent needed
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.
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. I am not aligned here. What's the concern if we have both range-based and full getters?
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. overpopulation |
||
| return _toAddresses(_tagToIdSet[TOKENIZATION_SPOKE_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTokenizationSpokes( | ||
| uint256 start, | ||
| uint256 end | ||
| ) external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[TOKENIZATION_SPOKE_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTreasurySpoke(string calldata name) external view returns (address) { | ||
| return _getAddress({name: name, tag: TREASURY_SPOKE_TAG}); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTreasurySpokes() external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[TREASURY_SPOKE_TAG].values()); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getTreasurySpokes(uint256 start, uint256 end) external view returns (address[] memory) { | ||
| return _toAddresses(_tagToIdSet[TREASURY_SPOKE_TAG].values(start, end)); | ||
| } | ||
|
|
||
| /// @inheritdoc IAddressesProvider | ||
| function getId(string calldata name, string calldata tag) external pure returns (bytes32) { | ||
| return _getId({name: name, tag: tag}); | ||
| } | ||
|
|
||
| function _setEntry(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}); | ||
| Entry memory oldEntry = _idToEntry[id]; | ||
|
|
||
| if (newAddress == address(0)) { | ||
| require(oldEntry.addr != address(0), AddressNotSet(id)); | ||
| _tagToIdSet[oldEntry.tag].remove(id); | ||
| if (_tagToIdSet[oldEntry.tag].length() == 0) { | ||
| _tagsSet.remove(oldEntry.tag); | ||
| } | ||
| _addressToIdSet[oldEntry.addr].remove(id); | ||
| delete _idToEntry[id]; | ||
| } else { | ||
| require(oldEntry.addr == address(0), AddressAlreadySet(id)); | ||
| _idToEntry[id] = Entry({addr: newAddress, name: name, tag: tag}); | ||
| _tagToIdSet[tag].add(id); | ||
| _tagsSet.add(tag); | ||
| _addressToIdSet[newAddress].add(id); | ||
| } | ||
|
|
||
| emit SetEntry(id, name, tag, oldEntry.addr, newAddress); | ||
| } | ||
|
|
||
| function _getAddress(string memory name, string memory tag) internal view returns (address) { | ||
| return _idToEntry[_getId({name: name, tag: tag})].addr; | ||
| } | ||
|
|
||
| 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] = _idToEntry[ids[i]].addr; | ||
| } | ||
| return addresses; | ||
| } | ||
|
|
||
| function _toEntries(bytes32[] memory ids) internal view returns (Entry[] memory) { | ||
| Entry[] memory entries = new Entry[](ids.length); | ||
| for (uint256 i = 0; i < ids.length; i++) { | ||
| entries[i] = _idToEntry[ids[i]]; | ||
| } | ||
| return entries; | ||
| } | ||
|
|
||
| function _getId(string memory name, string memory tag) internal pure returns (bytes32) { | ||
| return keccak256(abi.encode(name, tag)); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // SPDX-License-Identifier: LicenseRef-BUSL | ||
| pragma solidity 0.8.28; | ||
|
|
||
| import {EnumerableSet} from 'src/dependencies/openzeppelin/EnumerableSet.sol'; | ||
| import {IAddressesProvider} from 'src/addresses-provider/interfaces/IAddressesProvider.sol'; | ||
|
|
||
| /// @title AddressesProviderStorage | ||
| /// @author Aave Labs | ||
| /// @notice Storage layout for the AddressesProvider contract. | ||
| /// @dev This contract defines all storage variables used by the AddressesProvider. | ||
| abstract contract AddressesProviderStorage { | ||
| /// @dev Map of entry identifiers to their respective entries. | ||
| mapping(bytes32 id => IAddressesProvider.Entry) internal _idToEntry; | ||
|
|
||
| /// @dev Map of tags to their respective sets of entry identifiers. | ||
| mapping(string tag => EnumerableSet.Bytes32Set) internal _tagToIdSet; | ||
|
|
||
| /// @dev Set of all tags. | ||
| /// @dev A tag is included in the set only if it has at least one registered entry. | ||
| EnumerableSet.StringSet internal _tagsSet; | ||
|
|
||
| /// @dev Map of registered addresses to their respective sets of entry identifiers. | ||
| /// @dev An address may be registered under more than one entry. | ||
| mapping(address addr => EnumerableSet.Bytes32Set) internal _addressToIdSet; | ||
|
|
||
| /// @dev Reserved storage space to allow for future layout updates. | ||
| uint256[50] private __gap; | ||
| } |
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.
idk if this should be in a separate folder, perhaps misc? nbd either way