-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* ignore lcov file Signed-off-by: Elliot <[email protected]> * add: rpc endpoints Signed-off-by: Elliot <[email protected]> * fps init: address registry and mock addresses Signed-off-by: Elliot <[email protected]> * init: mock data for address registry tests Signed-off-by: Elliot <[email protected]> * add: mainnet registry tests Signed-off-by: Elliot <[email protected]> * add: local registry tests Signed-off-by: Elliot <[email protected]> * add logic to read superchain addresses * add tests * use l2 instance chain id in registry mapping * update interface * move chainList toml to addresses folder * update addresses toml file names as per chain ids in chainList toml * fix tests * forge fmt * update superchain addresses type check * update variable name * add: forge test to circleci Signed-off-by: Elliot <[email protected]> * add additional assertions for supported chain ids, add more construction failure tests Signed-off-by: Elliot <[email protected]> * add supported chainids and corresponding checks on getters, natspec, improve error messages Signed-off-by: Elliot <[email protected]> * natspec Signed-off-by: Elliot <[email protected]> * initial constants Signed-off-by: Elliot <[email protected]> * mock with duplicate chain configurations Signed-off-by: Elliot <[email protected]> * remove lcov from gitignore Signed-off-by: Elliot <[email protected]> * address registry doco Signed-off-by: Elliot <[email protected]> * remove supportedChainId variable Signed-off-by: Elliot <[email protected]> * naming: OPTIMISM_CHAINID -> OP_CHAINID Signed-off-by: Elliot <[email protected]> * fmt Signed-off-by: Elliot <[email protected]> --------- Signed-off-by: Elliot <[email protected]> Co-authored-by: prateek <[email protected]>
- Loading branch information
1 parent
8ba0014
commit 1d3cbec
Showing
21 changed files
with
587 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
pragma solidity 0.8.15; | ||
|
||
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; | ||
|
||
import {Test} from "forge-std/Test.sol"; | ||
import {IAddressRegistry} from "src/fps/IAddressRegistry.sol"; | ||
import {SUPERCHAIN_REGISTRY_PATH} from "src/fps/utils/Constants.sol"; | ||
|
||
/// @title Network Address Manager | ||
/// @notice This contract provides a single source of truth for storing and retrieving addresses across multiple networks. | ||
/// @dev Handles addresses for contracts and externally owned accounts (EOAs) while ensuring correctness and uniqueness. | ||
contract AddressRegistry is IAddressRegistry, Test { | ||
using EnumerableSet for EnumerableSet.UintSet; | ||
|
||
/// @dev Structure for reading address details from JSON files. | ||
struct InputAddress { | ||
/// Blockchain network identifier | ||
address addr; | ||
/// contract identifier (name) | ||
string identifier; | ||
/// Address (contract or EOA) | ||
/// Indicates if the address is a contract | ||
bool isContract; | ||
} | ||
|
||
/// @dev Structure for storing address details in the contract. | ||
struct RegistryEntry { | ||
address addr; | ||
/// Address (contract or EOA) | ||
/// Indicates if the address is a contract | ||
bool isContract; | ||
} | ||
|
||
/// @dev Structure for reading chain list details from toml file | ||
struct Superchain { | ||
uint256 chainId; | ||
string name; | ||
} | ||
|
||
/// @notice Maps an identifier and l2 instance chain ID to a stored address entry. | ||
/// All addresses will live on the same chain. | ||
mapping(string => mapping(uint256 => RegistryEntry)) private registry; | ||
|
||
/// @notice Supported L2 chain IDs for this Address Registry instance. | ||
mapping(uint256 => bool) public supportedL2ChainIds; | ||
|
||
/// @notice Array of supported superchains and their configurations | ||
Superchain[] public superchains; | ||
|
||
/// @notice Initializes the contract by loading addresses from TOML files and configuring the supported L2 chains. | ||
/// @param addressFolderPath The path to the folder containing chain-specific TOML address files | ||
/// @param superchainListFilePath The path to the TOML file containing the list of supported L2 chains | ||
constructor(string memory addressFolderPath, string memory superchainListFilePath) { | ||
bytes memory superchainListContent = vm.parseToml(vm.readFile(superchainListFilePath), ".chains"); | ||
superchains = abi.decode(superchainListContent, (Superchain[])); | ||
|
||
string memory superchainAddressesContent = vm.readFile(SUPERCHAIN_REGISTRY_PATH); | ||
|
||
for (uint256 i = 0; i < superchains.length; i++) { | ||
uint256 superchainId = superchains[i].chainId; | ||
string memory superchainName = superchains[i].name; | ||
require(!supportedL2ChainIds[superchainId], "Duplicate chain ID in superchain config"); | ||
require(superchainId != 0, "Invalid chain ID in superchain config"); | ||
require(bytes(superchainName).length > 0, "Empty name in superchain config"); | ||
|
||
supportedL2ChainIds[superchainId] = true; | ||
|
||
string memory filePath = | ||
string(abi.encodePacked(addressFolderPath, "/", vm.toString(superchainId), ".toml")); | ||
bytes memory fileContent = vm.parseToml(vm.readFile(filePath), ".addresses"); | ||
|
||
InputAddress[] memory parsedAddresses = abi.decode(fileContent, (InputAddress[])); | ||
|
||
for (uint256 j = 0; j < parsedAddresses.length; j++) { | ||
string memory identifier = parsedAddresses[j].identifier; | ||
address contractAddress = parsedAddresses[j].addr; | ||
bool isContract = parsedAddresses[j].isContract; | ||
|
||
require(contractAddress != address(0), "Invalid address: cannot be zero"); | ||
require( | ||
registry[identifier][superchainId].addr == address(0), | ||
"Address already registered with this identifier and chain ID" | ||
); | ||
|
||
_typeCheckAddress(contractAddress, isContract); | ||
|
||
registry[identifier][superchainId] = RegistryEntry(contractAddress, isContract); | ||
string memory prefixedIdentifier = | ||
string(abi.encodePacked(vm.replace(vm.toUppercase(superchainName), " ", "_"), "_", identifier)); | ||
vm.label(contractAddress, prefixedIdentifier); // Add label for debugging purposes | ||
} | ||
|
||
string[] memory keys = | ||
vm.parseJsonKeys(superchainAddressesContent, string.concat("$.", vm.toString(superchainId))); | ||
|
||
for (uint256 j = 0; j < keys.length; j++) { | ||
string memory key = keys[j]; | ||
address addr = vm.parseJsonAddress( | ||
superchainAddressesContent, string.concat("$.", vm.toString(superchainId), ".", key) | ||
); | ||
|
||
require(addr != address(0), "Invalid address: cannot be zero"); | ||
require( | ||
registry[key][superchainId].addr == address(0), | ||
"Address already registered with this identifier and chain ID" | ||
); | ||
|
||
registry[key][superchainId] = RegistryEntry(addr, addr.code.length > 0); | ||
string memory prefixedIdentifier = | ||
string(abi.encodePacked(vm.replace(vm.toUppercase(superchainName), " ", "_"), "_", key)); | ||
vm.label(addr, prefixedIdentifier); | ||
} | ||
} | ||
} | ||
|
||
/// @notice Retrieves an address by its identifier for a specified L2 chain | ||
/// @param identifier The unique identifier associated with the address | ||
/// @param l2ChainId The chain ID of the L2 network | ||
/// @return The address associated with the given identifier on the specified chain | ||
function getAddress(string memory identifier, uint256 l2ChainId) public view returns (address) { | ||
_l2ChainIdSupported(l2ChainId); | ||
|
||
// Fetch the stored registry entry | ||
RegistryEntry memory entry = registry[identifier][l2ChainId]; | ||
address resolvedAddress = entry.addr; | ||
|
||
require(resolvedAddress != address(0), "Address not found"); | ||
|
||
return resolvedAddress; | ||
} | ||
|
||
/// @notice Checks if an address is a contract for a given identifier and L2 chain | ||
/// @param identifier The unique identifier associated with the address | ||
/// @param l2ChainId The chain ID of the L2 network | ||
/// @return True if the address is a contract, false otherwise | ||
function isAddressContract(string memory identifier, uint256 l2ChainId) public view returns (bool) { | ||
_l2ChainIdSupported(l2ChainId); | ||
_checkAddressRegistered(identifier, l2ChainId); | ||
|
||
return registry[identifier][l2ChainId].isContract; | ||
} | ||
|
||
/// @notice Checks if an address exists for a specified identifier and L2 chain | ||
/// @param identifier The unique identifier associated with the address | ||
/// @param l2ChainId The chain ID of the L2 network | ||
/// @return True if the address exists, false otherwise | ||
function isAddressRegistered(string memory identifier, uint256 l2ChainId) public view returns (bool) { | ||
return registry[identifier][l2ChainId].addr != address(0); | ||
} | ||
|
||
/// @notice Verifies that an address is registered for a given identifier and chain | ||
/// @dev Reverts if the address is not registered | ||
/// @param identifier The unique identifier associated with the address | ||
/// @param l2ChainId The chain ID of the L2 network | ||
function _checkAddressRegistered(string memory identifier, uint256 l2ChainId) private view { | ||
require( | ||
isAddressRegistered(identifier, l2ChainId), | ||
string( | ||
abi.encodePacked("Address not found for identifier ", identifier, " on chain ", vm.toString(l2ChainId)) | ||
) | ||
); | ||
} | ||
|
||
/// @notice Verifies that the given L2 chain ID is supported | ||
/// @param l2ChainId The chain ID of the L2 network to verify | ||
function _l2ChainIdSupported(uint256 l2ChainId) private view { | ||
require( | ||
supportedL2ChainIds[l2ChainId], | ||
string(abi.encodePacked("L2 Chain ID ", vm.toString(l2ChainId), " not supported")) | ||
); | ||
} | ||
|
||
/// @notice Validates whether an address matches its expected type (contract or EOA) | ||
/// @dev Reverts if the address type does not match the expected type | ||
/// @param addr The address to validate | ||
/// @param isContract True if the address should be a contract, false if it should be an EOA | ||
function _typeCheckAddress(address addr, bool isContract) private view { | ||
if (isContract) { | ||
require(addr.code.length > 0, "Address must contain code"); | ||
} else { | ||
require(addr.code.length == 0, "Address must not contain code"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pragma solidity 0.8.15; | ||
|
||
/// @title Network Address Registry Interface | ||
/// @notice Interface for managing and retrieving addresses across different networks. | ||
interface IAddressRegistry { | ||
/// @notice Retrieves an address by its identifier for a specified L2 chain | ||
/// @param identifier The unique identifier associated with the address | ||
/// @param l2ChainId The chain ID of the L2 network | ||
/// @return The address associated with the given identifier on the specified chain | ||
function getAddress(string memory identifier, uint256 l2ChainId) external view returns (address); | ||
|
||
/// @notice Checks if an address is a contract for a given identifier and L2 chain | ||
/// @param identifier The unique identifier associated with the address | ||
/// @param l2ChainId The chain ID of the L2 network | ||
/// @return True if the address is a contract, false otherwise | ||
function isAddressContract(string memory identifier, uint256 l2ChainId) external view returns (bool); | ||
|
||
/// @notice Checks if an address exists for a specified identifier and L2 chain | ||
/// @param identifier The unique identifier associated with the address | ||
/// @param l2ChainId The chain ID of the L2 network | ||
/// @return True if the address exists, false otherwise | ||
function isAddressRegistered(string memory identifier, uint256 l2ChainId) external view returns (bool); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[[addresses]] | ||
addr = "0x9679E26bf0C470521DE83Ad77BB1bf1e7312f739" | ||
identifier = "DEPLOYER_EOA" | ||
isContract = false | ||
|
||
[[addresses]] | ||
addr = "0xc0Da02939E1441F497fd74F78cE7Decb17B66529" | ||
identifier = "COMPOUND_GOVERNOR_BRAVO" | ||
isContract = true | ||
|
||
[[addresses]] | ||
addr = "0x316f9708bB98af7dA9c68C1C3b5e79039cD336E3" | ||
identifier = "COMPOUND_CONFIGURATOR" | ||
isContract = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[[addresses]] | ||
addr = "0x9679E26bf0C470521DE83Ad77BB1bf1e7312f739" | ||
identifier = "DEPLOYER_EOA" | ||
isContract = false | ||
|
||
[[addresses]] | ||
addr = "0xc0Da02939E1441F497fd74F78cE7Decb17B66529" | ||
identifier = "COMPOUND_GOVERNOR_BRAVO" | ||
isContract = true | ||
|
||
[[addresses]] | ||
addr = "0x316f9708bB98af7dA9c68C1C3b5e79039cD336E3" | ||
identifier = "COMPOUND_CONFIGURATOR" | ||
isContract = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
[[chains]] | ||
name = "OP Mainnet" | ||
chain_id = 10 | ||
|
||
[[chains]] | ||
name = "Base" | ||
chain_id = 8453 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# Address Registry | ||
|
||
The address registry contract stores contract addresses on a single network. On construction, it reads in all of the configurations from the specified TOML configuration file. This TOML configuration file tells the address registry which L2 contracts to read in and store. As an example, if a task only touched the OP Mainnet contracts, the TOML file would only have a single entry | ||
|
||
```toml | ||
[[chains]] | ||
name = "OP Mainnet" | ||
chain_id = 10 | ||
``` | ||
|
||
## Usage | ||
|
||
Addresses can be fetched by calling the `getAddress(string memory identifier, uint256 l2ChainId)` function. This function will return the address of the contract with the given identifier on the given chain. If the contract does not exist, the function will revert. If the l2ChainId is unsupported by this address registry instance, the function will revert. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
pragma solidity 0.8.15; | ||
|
||
// Mainnet Chain Ids | ||
uint256 constant BASE_CHAIN_ID = 8453; | ||
uint256 constant OP_CHAIN_ID = 10; | ||
uint256 constant MODE_CHAIN_ID = 34443; | ||
uint256 constant ORDERLY_CHAIN_ID = 291; | ||
uint256 constant RACE_CHAIN_ID = 6805; | ||
uint256 constant ZORA_CHAIN_ID = 7777777; | ||
uint256 constant LYRA_CHAIN_ID = 957; | ||
uint256 constant METAL_CHAIN_ID = 1750; | ||
uint256 constant BINARY_CHAIN_ID = 624; | ||
|
||
// Testnet Chain Ids | ||
uint256 constant BASE_SEPOLIA_CHAIN_ID = 84532; | ||
uint256 constant OP_SEPOLIA_CHAIN_ID = 11155420; | ||
uint256 constant MODE_SEPOLIA_CHAIN_ID = 919; | ||
uint256 constant BASE_DEVNET_CHAIN_ID = 11763072; | ||
uint256 constant METAL_SEPOLIA_CHAIN_ID = 1740; | ||
uint256 constant RACE_SEPOLIA_CHAIN_ID = 6806; | ||
uint256 constant ZORA_SEPOLIA_CHAIN_ID = 999999999; | ||
uint256 constant OPLABS_DEVNET_CHAIN_ID = 11155421; | ||
|
||
uint256 constant LOCAL_CHAIN_ID = 31337; | ||
|
||
string constant SUPERCHAIN_REGISTRY_PATH = "lib/superchain-registry/superchain/extra/addresses/addresses.json"; |
Oops, something went wrong.