Skip to content
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

FPS Initialize #406

Merged
merged 36 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f9c1762
ignore lcov file
ElliotFriedman Dec 10, 2024
03ba7f4
add: rpc endpoints
ElliotFriedman Dec 10, 2024
5ab001f
fps init: address registry and mock addresses
ElliotFriedman Dec 10, 2024
6e5172d
init: mock data for address registry tests
ElliotFriedman Dec 10, 2024
f575657
add: mainnet registry tests
ElliotFriedman Dec 10, 2024
d05dd96
add: local registry tests
ElliotFriedman Dec 10, 2024
e121753
add logic to read superchain addresses
prateek105 Dec 11, 2024
2dd3a0e
add tests
prateek105 Dec 11, 2024
3bd4cec
Merge pull request #3 from solidity-labs-io/feat/fps-read-superchain
ElliotFriedman Dec 12, 2024
1f6d997
use l2 instance chain id in registry mapping
prateek105 Dec 12, 2024
7d720b4
update interface
prateek105 Dec 12, 2024
5923b1c
move chainList toml to addresses folder
prateek105 Dec 12, 2024
7e80b5f
update addresses toml file names as per chain ids in chainList toml
prateek105 Dec 12, 2024
c7e313a
fix tests
prateek105 Dec 12, 2024
1256800
forge fmt
prateek105 Dec 12, 2024
b56bd5b
update superchain addresses type check
prateek105 Dec 12, 2024
3a52ad9
update variable name
prateek105 Dec 12, 2024
0193c15
add: forge test to circleci
ElliotFriedman Dec 12, 2024
490bf31
Merge pull request #5 from solidity-labs-io/fix/update-registry-l2cha…
ElliotFriedman Dec 12, 2024
58507b3
Merge pull request #6 from solidity-labs-io/add/ci-test
ElliotFriedman Dec 12, 2024
2dedb8f
add additional assertions for supported chain ids, add more construct…
ElliotFriedman Dec 12, 2024
47b4dde
add supported chainids and corresponding checks on getters, natspec, …
ElliotFriedman Dec 12, 2024
221ec63
natspec
ElliotFriedman Dec 12, 2024
b70d2b4
initial constants
ElliotFriedman Dec 12, 2024
09b2fb0
mock with duplicate chain configurations
ElliotFriedman Dec 12, 2024
6bb724b
Merge pull request #7 from solidity-labs-io/add-supported-chainids
ElliotFriedman Dec 12, 2024
10e1911
remove lcov from gitignore
ElliotFriedman Dec 12, 2024
7ac03a8
address registry doco
ElliotFriedman Dec 12, 2024
03a5fb6
Merge pull request #8 from solidity-labs-io/add-supported-chainids
ElliotFriedman Dec 12, 2024
5516dcf
Merge branch 'main' into feat/fps-init
ElliotFriedman Dec 13, 2024
2dc4d86
Merge pull request #9 from ethereum-optimism/main
ElliotFriedman Dec 13, 2024
93cb335
Merge branch 'main' into feat/fps-init
prateek105 Dec 13, 2024
3d07fe1
Merge branch 'main' into feat/fps-init
ElliotFriedman Dec 15, 2024
6fa31b7
remove supportedChainId variable
ElliotFriedman Dec 15, 2024
6528291
naming: OPTIMISM_CHAINID -> OP_CHAINID
ElliotFriedman Dec 15, 2024
a501b54
fmt
ElliotFriedman Dec 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,16 @@ jobs:
just install
forge --version
forge fmt --check
forge_test:
docker:
- image: <<pipeline.parameters.ci_builder_image>>
steps:
- checkout
- run:
name: forge test
command: |
forge --version
forge test -vvv

print_versions:
docker:
Expand All @@ -217,6 +227,7 @@ workflows:
# Forge checks.
- forge_build
- forge_fmt
- forge_test
# RPC endpoint checks.
- check_sepolia_rpc_endpoints
- check_mainnet_rpc_endpoints
Expand Down
4 changes: 4 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ remappings = [

[profile.ci]
deny_warnings = true

[rpc_endpoints]
localhost = "http://127.0.0.1:8545"
mainnet = "https://ethereum.publicnode.com"
184 changes: 184 additions & 0 deletions src/fps/AddressRegistry.sol
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");
}
}
}
23 changes: 23 additions & 0 deletions src/fps/IAddressRegistry.sol
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);
}
14 changes: 14 additions & 0 deletions src/fps/addresses/10.toml
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
14 changes: 14 additions & 0 deletions src/fps/addresses/8453.toml
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
7 changes: 7 additions & 0 deletions src/fps/addresses/chainList.toml
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
13 changes: 13 additions & 0 deletions src/fps/doc/ADDRESS_REGISTRY.md
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.
26 changes: 26 additions & 0 deletions src/fps/utils/Constants.sol
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";
Loading