Commit 59fbf91e authored by Matt Solomon's avatar Matt Solomon Committed by GitHub

feat(opsm): scaffold OP Stack Manager and tests (#11274)

* init scaffolding of OPSM contract and tests

* add chainIdToBatchInboxAddress

* chore: address semgrep findings

* test: add auth spec

* update functions sig, semver lock, snapshots

* semver lock update after installing correct foundry version

* udpates based on style guide

* snapshots
parent 02e04637
......@@ -31,6 +31,10 @@
"initCodeHash": "0x14c3a582ca46ef2a6abad5590323f4de26ff4de54415c927c62e131ccbf8d9ba",
"sourceCodeHash": "0xf5fcf570721e25459fadbb37e02f9efe349e1c8afcbf1e3b5fdb09c9f612cdc0"
},
"src/L1/OPStackManager.sol": {
"initCodeHash": "0x1630942414d9711137028c48f37b6fcd7fbbedc147102e31ebcdcc03a9645c6a",
"sourceCodeHash": "0x8e9a47583c4c3d711c2b7cc5e0f86495e29d4e79c38415dd3d342e1d1aae4fb7"
},
"src/L1/OptimismPortal.sol": {
"initCodeHash": "0xfdc8cf0b0b26961f6ac493ee564761716447d263291bea4d366a7b94afe33392",
"sourceCodeHash": "0x9fe0a9001edecd2a04daada4ca9e17d66141b1c982f73653493b4703d2c675c4"
......
[
{
"inputs": [
{
"internalType": "uint256",
"name": "_l2ChainId",
"type": "uint256"
},
{
"internalType": "uint32",
"name": "_basefeeScalar",
"type": "uint32"
},
{
"internalType": "uint32",
"name": "_blobBasefeeScalar",
"type": "uint32"
},
{
"components": [
{
"internalType": "address",
"name": "proxyAdminOwner",
"type": "address"
},
{
"internalType": "address",
"name": "systemConfigOwner",
"type": "address"
},
{
"internalType": "address",
"name": "batcher",
"type": "address"
},
{
"internalType": "address",
"name": "unsafeBlockSigner",
"type": "address"
},
{
"internalType": "address",
"name": "proposer",
"type": "address"
},
{
"internalType": "address",
"name": "challenger",
"type": "address"
}
],
"internalType": "struct OPStackManager.Roles",
"name": "_roles",
"type": "tuple"
}
],
"name": "deploy",
"outputs": [
{
"internalType": "contract SystemConfig",
"name": "systemConfig_",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "l2ChainId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "contract SystemConfig",
"name": "systemConfig",
"type": "address"
}
],
"name": "Deployed",
"type": "event"
},
{
"inputs": [
{
"internalType": "string",
"name": "reason",
"type": "string"
}
],
"name": "DeploymentFailed",
"type": "error"
},
{
"inputs": [],
"name": "InvalidChainId",
"type": "error"
},
{
"inputs": [],
"name": "NotImplemented",
"type": "error"
}
]
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { ISemver } from "src/universal/ISemver.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
/// @custom:proxied
contract OPStackManager is ISemver {
/// @custom:semver 1.0.0-beta.1
string public constant version = "1.0.0-beta.1";
/// @notice Represents the roles that can be set when deploying a standard OP Stack chain.
struct Roles {
address proxyAdminOwner;
address systemConfigOwner;
address batcher;
address unsafeBlockSigner;
address proposer;
address challenger;
}
/// @notice Emitted when a new OP Stack chain is deployed.
/// @param l2ChainId The chain ID of the new chain.
/// @param systemConfig The address of the new chain's SystemConfig contract.
event Deployed(uint256 indexed l2ChainId, SystemConfig indexed systemConfig);
/// @notice Thrown when an invalid `l2ChainId` is provided to `deploy`.
error InvalidChainId();
/// @notice Thrown when a deployment fails.
error DeploymentFailed(string reason);
/// @notice Temporary error since the deploy method is not yet implemented.
error NotImplemented();
function deploy(
uint256 _l2ChainId,
uint32 _basefeeScalar,
uint32 _blobBasefeeScalar,
Roles calldata _roles
)
external
view // This is only here to silence the compiler warning until the function is fully implemented.
returns (SystemConfig systemConfig_)
{
if (_l2ChainId == 0 || _l2ChainId == block.chainid) revert InvalidChainId();
// Silence compiler warnings.
_roles;
_basefeeScalar;
_blobBasefeeScalar;
systemConfig_;
revert NotImplemented();
}
/// @notice Maps an L2 chain ID to an L1 batch inbox address as defined by the standard
/// configuration's convention. This convention is `versionByte || keccak256(bytes32(chainId))[:19]`,
/// where || denotes concatenation`, versionByte is 0x00, and chainId is a uint256.
/// https://specs.optimism.io/protocol/configurability.html#consensus-parameters
function chainIdToBatchInboxAddress(uint256 _l2ChainId) internal pure returns (address) {
bytes1 versionByte = 0x00;
bytes32 hashedChainId = keccak256(bytes.concat(bytes32(_l2ChainId)));
bytes19 first19Bytes = bytes19(hashedChainId);
return address(uint160(bytes20(bytes.concat(versionByte, first19Bytes))));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { Test } from "forge-std/Test.sol";
// Target contract
import { OPStackManager } from "src/L1/OPStackManager.sol";
// Exposes internal functions for testing.
contract OPStackManager_Harness is OPStackManager {
function chainIdToBatchInboxAddress_exposed(uint256 l2ChainId) public pure returns (address) {
return super.chainIdToBatchInboxAddress(l2ChainId);
}
}
// Unlike other test suites, we intentionally do not inherit from CommonTest or Setup. This is
// because OPStackManager acts as a deploy script, so we start from a clean slate here and
// work OPStackManager's deployment into the existing test setup, instead of using the existing
// test setup to deploy OPStackManager.
contract OPStackManager_Init is Test {
OPStackManager opsm;
// Default dummy parameters for the deploy function.
OPStackManager.Roles roles;
uint256 l2ChainId = 1234;
uint32 basefeeScalar = 1;
uint32 blobBasefeeScalar = 1;
function setUp() public {
opsm = new OPStackManager();
}
}
contract OPStackManager_Deploy_Test is OPStackManager_Init {
function test_deploy_l2ChainIdEqualsZero_reverts() public {
vm.expectRevert(OPStackManager.InvalidChainId.selector);
opsm.deploy(0, basefeeScalar, blobBasefeeScalar, roles);
}
function test_deploy_l2ChainIdEqualsCurrentChainId_reverts() public {
vm.expectRevert(OPStackManager.InvalidChainId.selector);
opsm.deploy(block.chainid, basefeeScalar, blobBasefeeScalar, roles);
}
}
// These tests use the harness which exposes internal functions for testing.
contract OPStackManager_InternalMethods_Test is Test {
function test_calculatesBatchInboxAddress_succeeds() public {
OPStackManager_Harness opsmHarness = new OPStackManager_Harness();
// These test vectors were calculated manually:
// 1. Compute the bytes32 encoding of the chainId: bytes32(uint256(chainId));
// 2. Hash it and manually take the first 19 bytes, and prefixed it with 0x00.
uint256 chainId = 1234;
address expected = 0x0017FA14b0d73Aa6A26D6b8720c1c84b50984f5C;
address actual = opsmHarness.chainIdToBatchInboxAddress_exposed(chainId);
vm.assertEq(expected, actual);
chainId = type(uint256).max;
expected = 0x00a9C584056064687E149968cBaB758a3376D22A;
actual = opsmHarness.chainIdToBatchInboxAddress_exposed(chainId);
vm.assertEq(expected, actual);
}
}
......@@ -10,6 +10,7 @@ import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol";
import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
import { SystemConfig } from "src/L1/SystemConfig.sol";
import { DataAvailabilityChallenge } from "src/L1/DataAvailabilityChallenge.sol";
import { OPStackManager } from "src/L1/OPStackManager.sol";
import { ForgeArtifacts, Abi, AbiEntry } from "scripts/ForgeArtifacts.sol";
/// @title Specification_Test
......@@ -781,6 +782,10 @@ contract Specification_Test is CommonTest {
_addSpec({ _name: "WETH98", _sel: _getSel("transferFrom(address,address,uint256)") });
_addSpec({ _name: "WETH98", _sel: _getSel("withdraw(uint256)") });
// OPStackManager
_addSpec({ _name: "OPStackManager", _sel: _getSel("version()") });
_addSpec({ _name: "OPStackManager", _sel: OPStackManager.deploy.selector });
// DeputyGuardianModule
_addSpec({
_name: "DeputyGuardianModule",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment