Skip to content

Commit eea9572

Browse files
authored
ctb: Make implementations deployments deterministic and idempotent (#13717)
* ctb: Make implementations deployments deterministic and idempotent * code review updates * cr updates * imports * make superchain deployments deterministic * forgot to add test * code review updates * broadcast in dpeloysuperchain * add forgotten broadcast
1 parent f0f0643 commit eea9572

File tree

9 files changed

+317
-498
lines changed

9 files changed

+317
-498
lines changed

op-chain-ops/interopgen/deploy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func CreateL1(logger log.Logger, fa *foundry.ArtifactsFS, srcFS *foundry.SourceM
114114
PrevRandao: cfg.L1GenesisBlockMixHash,
115115
BlobHashes: nil,
116116
}
117-
l1Host := script.NewHost(logger.New("role", "l1", "chain", cfg.ChainID), fa, srcFS, l1Context)
117+
l1Host := script.NewHost(logger.New("role", "l1", "chain", cfg.ChainID), fa, srcFS, l1Context, script.WithCreate2Deployer())
118118
return l1Host
119119
}
120120

packages/contracts-bedrock/scripts/deploy/Deploy.s.sol

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,12 +292,8 @@ contract Deploy is Deployer {
292292
dii.set(dii.mipsVersion.selector, Config.useMultithreadedCannon() ? 2 : 1);
293293
string memory release = "dev";
294294
dii.set(dii.l1ContractsRelease.selector, release);
295-
dii.set(
296-
dii.standardVersionsToml.selector, string.concat(vm.projectRoot(), "/test/fixtures/standard-versions.toml")
297-
);
298295
dii.set(dii.superchainConfigProxy.selector, artifacts.mustGetAddress("SuperchainConfigProxy"));
299296
dii.set(dii.protocolVersionsProxy.selector, artifacts.mustGetAddress("ProtocolVersionsProxy"));
300-
dii.set(dii.salt.selector, _implSalt());
301297

302298
if (_isInterop) {
303299
di = DeployImplementations(new DeployImplementationsInterop());

packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol

Lines changed: 166 additions & 368 deletions
Large diffs are not rendered by default.

packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ contract DeploySuperchainOutput is BaseDeployIO {
292292
// default sender would be the broadcaster during test, but the broadcaster needs to be the deployer
293293
// since they are set to the initial proxy admin owner.
294294
contract DeploySuperchain is Script {
295+
bytes32 internal _salt = DeployUtils.DEFAULT_SALT;
296+
295297
// -------- Core Deployment Methods --------
296298

297299
function run(DeploySuperchainInput _dsi, DeploySuperchainOutput _dso) public {
@@ -342,15 +344,17 @@ contract DeploySuperchain is Script {
342344
// Deploy implementation contracts.
343345
vm.startBroadcast(msg.sender);
344346
ISuperchainConfig superchainConfigImpl = ISuperchainConfig(
345-
DeployUtils.create1({
347+
DeployUtils.createDeterministic({
346348
_name: "SuperchainConfig",
347-
_args: DeployUtils.encodeConstructor(abi.encodeCall(ISuperchainConfig.__constructor__, ()))
349+
_args: DeployUtils.encodeConstructor(abi.encodeCall(ISuperchainConfig.__constructor__, ())),
350+
_salt: _salt
348351
})
349352
);
350353
IProtocolVersions protocolVersionsImpl = IProtocolVersions(
351-
DeployUtils.create1({
354+
DeployUtils.createDeterministic({
352355
_name: "ProtocolVersions",
353-
_args: DeployUtils.encodeConstructor(abi.encodeCall(IProtocolVersions.__constructor__, ()))
356+
_args: DeployUtils.encodeConstructor(abi.encodeCall(IProtocolVersions.__constructor__, ())),
357+
_salt: _salt
354358
})
355359
);
356360
vm.stopBroadcast();

packages/contracts-bedrock/scripts/libraries/DeployUtils.sol

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { Artifacts } from "scripts/Artifacts.s.sol";
1010
import { LibString } from "@solady/utils/LibString.sol";
1111
import { Bytes } from "src/libraries/Bytes.sol";
1212
import { Constants } from "src/libraries/Constants.sol";
13+
import { Blueprint } from "src/libraries/Blueprint.sol";
1314

1415
// Interfaces
1516
import { IProxy } from "interfaces/universal/IProxy.sol";
@@ -20,6 +21,8 @@ import { IResolvedDelegateProxy } from "interfaces/legacy/IResolvedDelegateProxy
2021
library DeployUtils {
2122
Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
2223

24+
bytes32 internal constant DEFAULT_SALT = keccak256("op-stack-contract-impls-salt-v0");
25+
2326
/// @notice Deploys a contract with the given name and arguments via CREATE.
2427
/// @param _name Name of the contract to deploy.
2528
/// @param _args ABI-encoded constructor arguments.
@@ -97,19 +100,11 @@ library DeployUtils {
97100
/// @param _args ABI-encoded constructor arguments.
98101
/// @param _salt Salt for the CREATE2 operation.
99102
/// @return addr_ Address of the deployed contract.
100-
function create2(string memory _name, bytes memory _args, bytes32 _salt) internal returns (address payable addr_) {
103+
function create2(string memory _name, bytes memory _args, bytes32 _salt) internal returns (address payable) {
101104
bytes memory initCode = abi.encodePacked(vm.getCode(_name), _args);
102105
address preComputedAddress = vm.computeCreate2Address(_salt, keccak256(initCode));
103106
require(preComputedAddress.code.length == 0, "DeployUtils: contract already deployed");
104-
assembly {
105-
addr_ := create2(0, add(initCode, 0x20), mload(initCode), _salt)
106-
if iszero(addr_) {
107-
let size := returndatasize()
108-
returndatacopy(0, 0, size)
109-
revert(0, size)
110-
}
111-
}
112-
assertValidContractAddress(addr_);
107+
return create2asm(initCode, _salt);
113108
}
114109

115110
/// @notice Deploys a contract with the given name via CREATE2.
@@ -120,6 +115,18 @@ library DeployUtils {
120115
return create2(_name, hex"", _salt);
121116
}
122117

118+
function create2asm(bytes memory _initCode, bytes32 _salt) internal returns (address payable addr_) {
119+
assembly {
120+
addr_ := create2(0, add(_initCode, 0x20), mload(_initCode), _salt)
121+
if iszero(addr_) {
122+
let size := returndatasize()
123+
returndatacopy(0, 0, size)
124+
revert(0, size)
125+
}
126+
}
127+
assertValidContractAddress(addr_);
128+
}
129+
123130
/// @notice Deploys a contract with the given name and arguments via CREATE2 and saves the result.
124131
/// @param _save Artifacts contract.
125132
/// @param _name Name of the contract to deploy.
@@ -195,6 +202,64 @@ library DeployUtils {
195202
return create2AndSave(_save, _name, _name, hex"", _salt);
196203
}
197204

205+
/// @notice Deploys a contract with the given name using CREATE2. If the contract is already deployed, this method
206+
/// does nothing.
207+
/// @param _name Name of the contract to deploy.
208+
/// @param _args ABI-encoded constructor arguments.
209+
function createDeterministic(
210+
string memory _name,
211+
bytes memory _args,
212+
bytes32 _salt
213+
)
214+
internal
215+
returns (address payable addr_)
216+
{
217+
bytes memory initCode = abi.encodePacked(vm.getCode(_name), _args);
218+
address preComputedAddress = vm.computeCreate2Address(_salt, keccak256(initCode));
219+
if (preComputedAddress.code.length > 0) {
220+
addr_ = payable(preComputedAddress);
221+
} else {
222+
addr_ = DeployUtils.create2asm(initCode, _salt);
223+
}
224+
}
225+
226+
/// @notice Deploys a blueprint contract with the given name using CREATE2. If the contract is already deployed,
227+
/// this method does nothing.
228+
/// @param _rawBytecode Raw bytecode of the contract the blueprint will deploy.
229+
function createDeterministicBlueprint(
230+
bytes memory _rawBytecode,
231+
bytes32 _salt
232+
)
233+
internal
234+
returns (address newContract1_, address newContract2_)
235+
{
236+
uint32 maxSize = Blueprint.maxInitCodeSize();
237+
if (_rawBytecode.length <= maxSize) {
238+
bytes memory bpBytecode = Blueprint.blueprintDeployerBytecode(_rawBytecode);
239+
newContract1_ = vm.computeCreate2Address(_salt, keccak256(bpBytecode));
240+
if (newContract1_.code.length == 0) {
241+
(address deployedContract) = Blueprint.deploySmallBytecode(bpBytecode, _salt);
242+
require(deployedContract == newContract1_, "DeployUtils: unexpected blueprint address");
243+
}
244+
newContract2_ = address(0);
245+
} else {
246+
bytes memory part1Slice = Bytes.slice(_rawBytecode, 0, maxSize);
247+
bytes memory part2Slice = Bytes.slice(_rawBytecode, maxSize, _rawBytecode.length - maxSize);
248+
bytes memory bp1Bytecode = Blueprint.blueprintDeployerBytecode(part1Slice);
249+
bytes memory bp2Bytecode = Blueprint.blueprintDeployerBytecode(part2Slice);
250+
newContract1_ = vm.computeCreate2Address(_salt, keccak256(bp1Bytecode));
251+
if (newContract1_.code.length == 0) {
252+
address deployedContract = Blueprint.deploySmallBytecode(bp1Bytecode, _salt);
253+
require(deployedContract == newContract1_, "DeployUtils: unexpected part 1 blueprint address");
254+
}
255+
newContract2_ = vm.computeCreate2Address(_salt, keccak256(bp2Bytecode));
256+
if (newContract2_.code.length == 0) {
257+
address deployedContract = Blueprint.deploySmallBytecode(bp2Bytecode, _salt);
258+
require(deployedContract == newContract2_, "DeployUtils: unexpected part 2 blueprint address");
259+
}
260+
}
261+
}
262+
198263
/// @notice Takes a sender and an identifier and returns a deterministic address based on the
199264
/// two. The result is used to etch the input and output contracts to a deterministic
200265
/// address based on those two values, where the identifier represents the input or

packages/contracts-bedrock/test/fixtures/standard-versions.toml

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)