Commit c46616b3 authored by Maurelian's avatar Maurelian Committed by GitHub

feat: Use encodeCall for type safety (#13561)

* feat: Use encodeCall for type safety

A very common issue (both from experience and observation) is
failing to correctly encode the initializer arguments in
`encodeSystemConfigInitializer()`. This fixes that.

I believe that at one point we needed to be able to pass the 4 bytes
selector in as a string, but that no longer seems to be the case.

* semver

* feat: expand use of encodeCall to rest of OPCM

* semver lock
parent dd87dca5
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
"sourceCodeHash": "0xa91b445bdc666a02ba18e3b91ba94b6d54bbe65da714002fc734814201319d57" "sourceCodeHash": "0xa91b445bdc666a02ba18e3b91ba94b6d54bbe65da714002fc734814201319d57"
}, },
"src/L1/OPContractsManager.sol": { "src/L1/OPContractsManager.sol": {
"initCodeHash": "0x9b704574a7005dc2aa8d6a3e0d85572493cc4bbd60033a23e437632a5fef7720", "initCodeHash": "0x4b413cbe79bd10d41d8f3e9f0408e773dd49ced823d457b9f9aa92f446828105",
"sourceCodeHash": "0x05ed7ad68e4e9bca7334314e794a1f66e5899532bb01cfa3a7716cb2688df9d5" "sourceCodeHash": "0xe5179a20ae40d4e4773c52df98bac67e73e04044bec9e8750073b4e2f14fe81b"
}, },
"src/L1/OptimismPortal2.sol": { "src/L1/OptimismPortal2.sol": {
"initCodeHash": "0xfd14fd690752519064d6de6c3e15d69ec9146bc8714e56ac286305773dbb1533", "initCodeHash": "0xfd14fd690752519064d6de6c3e15d69ec9146bc8714e56ac286305773dbb1533",
......
...@@ -114,8 +114,8 @@ contract OPContractsManager is ISemver { ...@@ -114,8 +114,8 @@ contract OPContractsManager is ISemver {
// -------- Constants and Variables -------- // -------- Constants and Variables --------
/// @custom:semver 1.0.0-beta.26 /// @custom:semver 1.0.0-beta.27
string public constant version = "1.0.0-beta.26"; string public constant version = "1.0.0-beta.27";
/// @notice Represents the interface version so consumers know how to decode the DeployOutput struct /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct
/// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used. /// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used.
...@@ -286,12 +286,12 @@ contract OPContractsManager is ISemver { ...@@ -286,12 +286,12 @@ contract OPContractsManager is ISemver {
// -------- Set and Initialize Proxy Implementations -------- // -------- Set and Initialize Proxy Implementations --------
bytes memory data; bytes memory data;
data = encodeL1ERC721BridgeInitializer(IL1ERC721Bridge.initialize.selector, output); data = encodeL1ERC721BridgeInitializer(output);
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, address(output.l1ERC721BridgeProxy), implementation.l1ERC721BridgeImpl, data output.opChainProxyAdmin, address(output.l1ERC721BridgeProxy), implementation.l1ERC721BridgeImpl, data
); );
data = encodeOptimismPortalInitializer(IOptimismPortal2.initialize.selector, output); data = encodeOptimismPortalInitializer(output);
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, address(output.optimismPortalProxy), implementation.optimismPortalImpl, data output.opChainProxyAdmin, address(output.optimismPortalProxy), implementation.optimismPortalImpl, data
); );
...@@ -304,7 +304,7 @@ contract OPContractsManager is ISemver { ...@@ -304,7 +304,7 @@ contract OPContractsManager is ISemver {
output.opChainProxyAdmin, address(output.systemConfigProxy), implementation.systemConfigImpl, data output.opChainProxyAdmin, address(output.systemConfigProxy), implementation.systemConfigImpl, data
); );
data = encodeOptimismMintableERC20FactoryInitializer(IOptimismMintableERC20Factory.initialize.selector, output); data = encodeOptimismMintableERC20FactoryInitializer(output);
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, output.opChainProxyAdmin,
address(output.optimismMintableERC20FactoryProxy), address(output.optimismMintableERC20FactoryProxy),
...@@ -312,7 +312,7 @@ contract OPContractsManager is ISemver { ...@@ -312,7 +312,7 @@ contract OPContractsManager is ISemver {
data data
); );
data = encodeL1CrossDomainMessengerInitializer(IL1CrossDomainMessenger.initialize.selector, output); data = encodeL1CrossDomainMessengerInitializer(output);
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, output.opChainProxyAdmin,
address(output.l1CrossDomainMessengerProxy), address(output.l1CrossDomainMessengerProxy),
...@@ -320,12 +320,12 @@ contract OPContractsManager is ISemver { ...@@ -320,12 +320,12 @@ contract OPContractsManager is ISemver {
data data
); );
data = encodeL1StandardBridgeInitializer(IL1StandardBridge.initialize.selector, output); data = encodeL1StandardBridgeInitializer(output);
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, address(output.l1StandardBridgeProxy), implementation.l1StandardBridgeImpl, data output.opChainProxyAdmin, address(output.l1StandardBridgeProxy), implementation.l1StandardBridgeImpl, data
); );
data = encodeDelayedWETHInitializer(IDelayedWETH.initialize.selector, _input); data = encodeDelayedWETHInitializer(_input);
// Eventually we will switch from DelayedWETHPermissionedGameProxy to DelayedWETHPermissionlessGameProxy. // Eventually we will switch from DelayedWETHPermissionedGameProxy to DelayedWETHPermissionlessGameProxy.
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, output.opChainProxyAdmin,
...@@ -335,7 +335,7 @@ contract OPContractsManager is ISemver { ...@@ -335,7 +335,7 @@ contract OPContractsManager is ISemver {
); );
// We set the initial owner to this contract, set game implementations, then transfer ownership. // We set the initial owner to this contract, set game implementations, then transfer ownership.
data = encodeDisputeGameFactoryInitializer(IDisputeGameFactory.initialize.selector, _input); data = encodeDisputeGameFactoryInitializer();
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, output.opChainProxyAdmin,
address(output.disputeGameFactoryProxy), address(output.disputeGameFactoryProxy),
...@@ -347,7 +347,7 @@ contract OPContractsManager is ISemver { ...@@ -347,7 +347,7 @@ contract OPContractsManager is ISemver {
); );
output.disputeGameFactoryProxy.transferOwnership(address(_input.roles.opChainProxyAdminOwner)); output.disputeGameFactoryProxy.transferOwnership(address(_input.roles.opChainProxyAdminOwner));
data = encodeAnchorStateRegistryInitializer(IAnchorStateRegistry.initialize.selector, _input); data = encodeAnchorStateRegistryInitializer(_input);
upgradeAndCall( upgradeAndCall(
output.opChainProxyAdmin, output.opChainProxyAdmin,
address(output.anchorStateRegistryProxy), address(output.anchorStateRegistryProxy),
...@@ -394,7 +394,7 @@ contract OPContractsManager is ISemver { ...@@ -394,7 +394,7 @@ contract OPContractsManager is ISemver {
/// @notice Helper method for computing a salt that's used in CREATE2 deployments. /// @notice Helper method for computing a salt that's used in CREATE2 deployments.
/// Including the contract name ensures that the resultant address from CREATE2 is unique /// Including the contract name ensures that the resultant address from CREATE2 is unique
/// across our smart contract system. For example, we deploy multiple proxy contracts /// across our smart contract system. For example, we deploy multiple proxy contracts
/// with the same bytecode from this contract, so they need different salts to avoid an address collision /// with the same bytecode from this contract, so they each require a unique salt for determinism.
function computeSalt( function computeSalt(
uint256 _l2ChainId, uint256 _l2ChainId,
string memory _saltMixer, string memory _saltMixer,
...@@ -426,34 +426,30 @@ contract OPContractsManager is ISemver { ...@@ -426,34 +426,30 @@ contract OPContractsManager is ISemver {
// -------- Initializer Encoding -------- // -------- Initializer Encoding --------
/// @notice Helper method for encoding the L1ERC721Bridge initializer data. /// @notice Helper method for encoding the L1ERC721Bridge initializer data.
function encodeL1ERC721BridgeInitializer( function encodeL1ERC721BridgeInitializer(DeployOutput memory _output)
bytes4 _selector,
DeployOutput memory _output
)
internal internal
view view
virtual virtual
returns (bytes memory) returns (bytes memory)
{ {
return abi.encodeWithSelector(_selector, _output.l1CrossDomainMessengerProxy, superchainConfig); return abi.encodeCall(IL1ERC721Bridge.initialize, (_output.l1CrossDomainMessengerProxy, superchainConfig));
} }
/// @notice Helper method for encoding the OptimismPortal initializer data. /// @notice Helper method for encoding the OptimismPortal initializer data.
function encodeOptimismPortalInitializer( function encodeOptimismPortalInitializer(DeployOutput memory _output)
bytes4 _selector,
DeployOutput memory _output
)
internal internal
view view
virtual virtual
returns (bytes memory) returns (bytes memory)
{ {
return abi.encodeWithSelector( return abi.encodeCall(
_selector, IOptimismPortal2.initialize,
(
_output.disputeGameFactoryProxy, _output.disputeGameFactoryProxy,
_output.systemConfigProxy, _output.systemConfigProxy,
superchainConfig, superchainConfig,
GameTypes.PERMISSIONED_CANNON GameTypes.PERMISSIONED_CANNON
)
); );
} }
...@@ -467,12 +463,12 @@ contract OPContractsManager is ISemver { ...@@ -467,12 +463,12 @@ contract OPContractsManager is ISemver {
virtual virtual
returns (bytes memory) returns (bytes memory)
{ {
bytes4 selector = ISystemConfig.initialize.selector;
(IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) = (IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) =
defaultSystemConfigParams(selector, _input, _output); defaultSystemConfigParams(_input, _output);
return abi.encodeWithSelector( return abi.encodeCall(
selector, ISystemConfig.initialize,
(
_input.roles.systemConfigOwner, _input.roles.systemConfigOwner,
_input.basefeeScalar, _input.basefeeScalar,
_input.blobBasefeeScalar, _input.blobBasefeeScalar,
...@@ -482,69 +478,53 @@ contract OPContractsManager is ISemver { ...@@ -482,69 +478,53 @@ contract OPContractsManager is ISemver {
referenceResourceConfig, referenceResourceConfig,
chainIdToBatchInboxAddress(_input.l2ChainId), chainIdToBatchInboxAddress(_input.l2ChainId),
opChainAddrs opChainAddrs
)
); );
} }
/// @notice Helper method for encoding the OptimismMintableERC20Factory initializer data. /// @notice Helper method for encoding the OptimismMintableERC20Factory initializer data.
function encodeOptimismMintableERC20FactoryInitializer( function encodeOptimismMintableERC20FactoryInitializer(DeployOutput memory _output)
bytes4 _selector,
DeployOutput memory _output
)
internal internal
pure pure
virtual virtual
returns (bytes memory) returns (bytes memory)
{ {
return abi.encodeWithSelector(_selector, _output.l1StandardBridgeProxy); return abi.encodeCall(IOptimismMintableERC20Factory.initialize, (address(_output.l1StandardBridgeProxy)));
} }
/// @notice Helper method for encoding the L1CrossDomainMessenger initializer data. /// @notice Helper method for encoding the L1CrossDomainMessenger initializer data.
function encodeL1CrossDomainMessengerInitializer( function encodeL1CrossDomainMessengerInitializer(DeployOutput memory _output)
bytes4 _selector,
DeployOutput memory _output
)
internal internal
view view
virtual virtual
returns (bytes memory) returns (bytes memory)
{ {
return return abi.encodeCall(
abi.encodeWithSelector(_selector, superchainConfig, _output.optimismPortalProxy, _output.systemConfigProxy); IL1CrossDomainMessenger.initialize,
(superchainConfig, _output.optimismPortalProxy, _output.systemConfigProxy)
);
} }
/// @notice Helper method for encoding the L1StandardBridge initializer data. /// @notice Helper method for encoding the L1StandardBridge initializer data.
function encodeL1StandardBridgeInitializer( function encodeL1StandardBridgeInitializer(DeployOutput memory _output)
bytes4 _selector,
DeployOutput memory _output
)
internal internal
view view
virtual virtual
returns (bytes memory) returns (bytes memory)
{ {
return abi.encodeWithSelector( return abi.encodeCall(
_selector, _output.l1CrossDomainMessengerProxy, superchainConfig, _output.systemConfigProxy IL1StandardBridge.initialize,
(_output.l1CrossDomainMessengerProxy, superchainConfig, _output.systemConfigProxy)
); );
} }
function encodeDisputeGameFactoryInitializer( function encodeDisputeGameFactoryInitializer() internal view virtual returns (bytes memory) {
bytes4 _selector,
DeployInput memory
)
internal
view
virtual
returns (bytes memory)
{
// This contract must be the initial owner so we can set game implementations, then // This contract must be the initial owner so we can set game implementations, then
// ownership is transferred after. // ownership is transferred after.
return abi.encodeWithSelector(_selector, address(this)); return abi.encodeCall(IDisputeGameFactory.initialize, (address(this)));
} }
function encodeAnchorStateRegistryInitializer( function encodeAnchorStateRegistryInitializer(DeployInput memory _input)
bytes4 _selector,
DeployInput memory _input
)
internal internal
view view
virtual virtual
...@@ -552,19 +532,11 @@ contract OPContractsManager is ISemver { ...@@ -552,19 +532,11 @@ contract OPContractsManager is ISemver {
{ {
IAnchorStateRegistry.StartingAnchorRoot[] memory startingAnchorRoots = IAnchorStateRegistry.StartingAnchorRoot[] memory startingAnchorRoots =
abi.decode(_input.startingAnchorRoots, (IAnchorStateRegistry.StartingAnchorRoot[])); abi.decode(_input.startingAnchorRoots, (IAnchorStateRegistry.StartingAnchorRoot[]));
return abi.encodeWithSelector(_selector, startingAnchorRoots, superchainConfig); return abi.encodeCall(IAnchorStateRegistry.initialize, (startingAnchorRoots, superchainConfig));
} }
function encodeDelayedWETHInitializer( function encodeDelayedWETHInitializer(DeployInput memory _input) internal view virtual returns (bytes memory) {
bytes4 _selector, return abi.encodeCall(IDelayedWETH.initialize, (_input.roles.opChainProxyAdminOwner, superchainConfig));
DeployInput memory _input
)
internal
view
virtual
returns (bytes memory)
{
return abi.encodeWithSelector(_selector, _input.roles.opChainProxyAdminOwner, superchainConfig);
} }
function encodePermissionedDisputeGameConstructor( function encodePermissionedDisputeGameConstructor(
...@@ -595,7 +567,6 @@ contract OPContractsManager is ISemver { ...@@ -595,7 +567,6 @@ contract OPContractsManager is ISemver {
/// @notice Returns default, standard config arguments for the SystemConfig initializer. /// @notice Returns default, standard config arguments for the SystemConfig initializer.
/// This is used by subclasses to reduce code duplication. /// This is used by subclasses to reduce code duplication.
function defaultSystemConfigParams( function defaultSystemConfigParams(
bytes4, /* selector */
DeployInput memory, /* _input */ DeployInput memory, /* _input */
DeployOutput memory _output DeployOutput memory _output
) )
......
...@@ -34,9 +34,8 @@ contract OPContractsManagerInterop is OPContractsManager { ...@@ -34,9 +34,8 @@ contract OPContractsManagerInterop is OPContractsManager {
override override
returns (bytes memory) returns (bytes memory)
{ {
bytes4 selector = ISystemConfigInterop.initialize.selector;
(IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) = (IResourceMetering.ResourceConfig memory referenceResourceConfig, ISystemConfig.Addresses memory opChainAddrs) =
defaultSystemConfigParams(selector, _input, _output); defaultSystemConfigParams(_input, _output);
// TODO For now we assume that the dependency manager is the same as system config owner. // TODO For now we assume that the dependency manager is the same as system config owner.
// This is currently undefined since it's not part of the standard config, so we may need // This is currently undefined since it's not part of the standard config, so we may need
...@@ -45,8 +44,9 @@ contract OPContractsManagerInterop is OPContractsManager { ...@@ -45,8 +44,9 @@ contract OPContractsManagerInterop is OPContractsManager {
// we will make the change described in https://github.com/ethereum-optimism/optimism/issues/11783. // we will make the change described in https://github.com/ethereum-optimism/optimism/issues/11783.
address dependencyManager = address(_input.roles.systemConfigOwner); address dependencyManager = address(_input.roles.systemConfigOwner);
return abi.encodeWithSelector( return abi.encodeCall(
selector, ISystemConfigInterop.initialize,
(
_input.roles.systemConfigOwner, _input.roles.systemConfigOwner,
_input.basefeeScalar, _input.basefeeScalar,
_input.blobBasefeeScalar, _input.blobBasefeeScalar,
...@@ -57,6 +57,7 @@ contract OPContractsManagerInterop is OPContractsManager { ...@@ -57,6 +57,7 @@ contract OPContractsManagerInterop is OPContractsManager {
chainIdToBatchInboxAddress(_input.l2ChainId), chainIdToBatchInboxAddress(_input.l2ChainId),
opChainAddrs, opChainAddrs,
dependencyManager dependencyManager
)
); );
} }
} }
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