Skip to content

Commit 8cf3673

Browse files
authored
STABLE-8092: Update deployment scripts to use CREATE2 for implementation contracts (#57)
1 parent 8a25c1a commit 8cf3673

File tree

8 files changed

+142
-62
lines changed

8 files changed

+142
-62
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,13 @@ Deploy the implementation contracts.
119119
1. Add the following [env](.env) variables
120120

121121
- `CREATE2_FACTORY_CONTRACT_ADDRESS`
122+
- `CREATE2_FACTORY_OWNER_KEY`
123+
- `TOKEN_MINTER_V2_OWNER_ADDRESS`
124+
- `TOKEN_MINTER_V2_OWNER_KEY`
122125
- `TOKEN_CONTROLLER_ADDRESS`
123126
- `DOMAIN`
124127
- `MESSAGE_BODY_VERSION`
125128
- `VERSION`
126-
- `IMPLEMENTATION_DEPLOYER_PRIVATE_KEY`
127129

128130
2. Run `make simulate-deploy-implementations-v2 RPC_URL=<RPC_URL> SENDER=<SENDER>` to perform a dry run.
129131

@@ -170,8 +172,8 @@ The proxies are deployed via `CREATE2` through Create2Factory. The scripts assum
170172
- `BURN_LIMIT_PER_MESSAGE`
171173

172174
- `CREATE2_FACTORY_OWNER_KEY`
173-
- `TOKEN_MINTER_V2_DEPLOYER_KEY`
174175
- `TOKEN_CONTROLLER_KEY`
176+
- `TOKEN_MINTER_V2_OWNER_KEY`
175177

176178
2. Run `make simulate-deploy-proxies-v2 RPC_URL=<RPC_URL> SENDER=<SENDER>` to perform a dry run.
177179

scripts/v2/DeployAddressUtilsExternal.s.sol

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pragma solidity 0.7.6;
2020
import {Script} from "forge-std/Script.sol";
2121
import {AddressUtilsExternal} from "../../src/messages/v2/AddressUtilsExternal.sol";
2222
import {Create2Factory} from "../../src/v2/Create2Factory.sol";
23+
import {SALT_ADDRESS_UTILS_EXTERNAL} from "./Salts.sol";
2324

2425
contract DeployAddressUtilsExternalScript is Script {
2526
Create2Factory private create2Factory;
@@ -33,7 +34,7 @@ contract DeployAddressUtilsExternalScript is Script {
3334
_addressUtilsExternal = AddressUtilsExternal(
3435
create2Factory.deploy(
3536
0,
36-
bytes32(0),
37+
SALT_ADDRESS_UTILS_EXTERNAL,
3738
type(AddressUtilsExternal).creationCode
3839
)
3940
);

scripts/v2/DeployImplementationsV2.s.sol

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616
* limitations under the License.
1717
*/
1818
pragma solidity 0.7.6;
19+
pragma abicoder v2;
1920

2021
import {Script} from "forge-std/Script.sol";
2122
import {AdminUpgradableProxy} from "../../src/proxy/AdminUpgradableProxy.sol";
2223
import {TokenMessengerV2} from "../../src/v2/TokenMessengerV2.sol";
2324
import {TokenMinterV2} from "../../src/v2/TokenMinterV2.sol";
2425
import {MessageTransmitterV2} from "../../src/v2/MessageTransmitterV2.sol";
26+
import {Create2Factory} from "../../src/v2/Create2Factory.sol";
27+
import {Ownable2Step} from "../../src/roles/Ownable2Step.sol";
28+
import {SALT_MESSAGE_TRANSMITTER, SALT_TOKEN_MESSENGER, SALT_TOKEN_MINTER} from "./Salts.sol";
2529

2630
contract DeployImplementationsV2Script is Script {
2731
// Expose for tests
@@ -31,18 +35,21 @@ contract DeployImplementationsV2Script is Script {
3135
address public expectedMessageTransmitterV2ProxyAddress;
3236

3337
address private factoryAddress;
38+
address private tokenMinterOwnerAddress;
39+
uint256 private tokenMinterOwnerKey;
3440
address private tokenControllerAddress;
3541
uint32 private messageBodyVersion;
3642
uint32 private version;
3743
uint32 private domain;
38-
uint256 private implementationDeployerPrivateKey;
44+
uint256 private create2FactoryOwnerPrivateKey;
3945

40-
function deployImplementationsV2(
41-
uint256 privateKey
42-
) private returns (MessageTransmitterV2, TokenMinterV2, TokenMessengerV2) {
46+
function deployImplementationsV2()
47+
private
48+
returns (MessageTransmitterV2, TokenMinterV2, TokenMessengerV2)
49+
{
4350
// Calculate MessageTransmitterV2 proxy address
4451
expectedMessageTransmitterV2ProxyAddress = vm.computeCreate2Address(
45-
keccak256(type(MessageTransmitterV2).creationCode),
52+
SALT_MESSAGE_TRANSMITTER,
4653
keccak256(
4754
abi.encodePacked(
4855
type(AdminUpgradableProxy).creationCode,
@@ -52,47 +59,85 @@ contract DeployImplementationsV2Script is Script {
5259
factoryAddress
5360
);
5461

62+
Create2Factory factory = Create2Factory(factoryAddress);
63+
5564
// Start recording transactions
56-
vm.startBroadcast(privateKey);
65+
vm.startBroadcast(create2FactoryOwnerPrivateKey);
5766

5867
// Deploy MessageTransmitterV2 implementation
59-
MessageTransmitterV2 messageTransmitterV2Implementation = new MessageTransmitterV2(
60-
domain,
61-
version
62-
);
68+
messageTransmitterV2 = MessageTransmitterV2(
69+
factory.deploy(
70+
0,
71+
SALT_MESSAGE_TRANSMITTER,
72+
abi.encodePacked(
73+
type(MessageTransmitterV2).creationCode,
74+
abi.encode(domain, version)
75+
)
76+
)
77+
);
6378

64-
// Deploy TokenMinter
65-
TokenMinterV2 tokenMinterV2Implementation = new TokenMinterV2(
66-
tokenControllerAddress
79+
// Deploy TokenMessengerV2 implementation
80+
tokenMessengerV2 = TokenMessengerV2(
81+
factory.deploy(
82+
0,
83+
SALT_TOKEN_MESSENGER,
84+
abi.encodePacked(
85+
type(TokenMessengerV2).creationCode,
86+
abi.encode(
87+
expectedMessageTransmitterV2ProxyAddress,
88+
messageBodyVersion
89+
)
90+
)
91+
)
6792
);
6893

69-
// Deploy TokenMessengerV2
70-
TokenMessengerV2 tokenMessengerV2Implementation = new TokenMessengerV2(
71-
expectedMessageTransmitterV2ProxyAddress,
72-
messageBodyVersion
94+
// Since the TokenMinter sets the msg.sender of the deployment to be
95+
// the Owner, we'll need to rotate it from the Create2Factory atomically.
96+
bytes memory tokenMinterOwnershipRotation = abi.encodeWithSelector(
97+
Ownable2Step.transferOwnership.selector,
98+
tokenMinterOwnerAddress
99+
);
100+
bytes[] memory tokenMinterMultiCallData = new bytes[](1);
101+
tokenMinterMultiCallData[0] = tokenMinterOwnershipRotation;
102+
103+
// Deploy TokenMinter
104+
tokenMinterV2 = TokenMinterV2(
105+
factory.deployAndMultiCall(
106+
0,
107+
SALT_TOKEN_MINTER,
108+
abi.encodePacked(
109+
type(TokenMinterV2).creationCode,
110+
abi.encode(tokenControllerAddress)
111+
),
112+
tokenMinterMultiCallData
113+
)
73114
);
74115

75116
// Stop recording transactions
76117
vm.stopBroadcast();
77-
return (
78-
messageTransmitterV2Implementation,
79-
tokenMinterV2Implementation,
80-
tokenMessengerV2Implementation
81-
);
118+
119+
// Accept the TokenMinter 2-step ownership
120+
vm.startBroadcast(tokenMinterOwnerKey);
121+
tokenMinterV2.acceptOwnership();
122+
vm.stopBroadcast();
123+
124+
return (messageTransmitterV2, tokenMinterV2, tokenMessengerV2);
82125
}
83126

84127
/**
85128
* @notice initialize variables from environment
86129
*/
87130
function setUp() public {
88131
factoryAddress = vm.envAddress("CREATE2_FACTORY_CONTRACT_ADDRESS");
132+
create2FactoryOwnerPrivateKey = vm.envUint("CREATE2_FACTORY_OWNER_KEY");
133+
tokenMinterOwnerAddress = vm.envAddress(
134+
"TOKEN_MINTER_V2_OWNER_ADDRESS"
135+
);
136+
tokenMinterOwnerKey = vm.envUint("TOKEN_MINTER_V2_OWNER_KEY");
89137
tokenControllerAddress = vm.envAddress("TOKEN_CONTROLLER_ADDRESS");
90138
domain = uint32(vm.envUint("DOMAIN"));
91139
messageBodyVersion = uint32(vm.envUint("MESSAGE_BODY_VERSION"));
92140
version = uint32(vm.envUint("VERSION"));
93-
implementationDeployerPrivateKey = vm.envUint(
94-
"IMPLEMENTATION_DEPLOYER_PRIVATE_KEY"
95-
);
96141
}
97142

98143
/**
@@ -103,6 +148,6 @@ contract DeployImplementationsV2Script is Script {
103148
messageTransmitterV2,
104149
tokenMinterV2,
105150
tokenMessengerV2
106-
) = deployImplementationsV2(implementationDeployerPrivateKey);
151+
) = deployImplementationsV2();
107152
}
108153
}

scripts/v2/DeployProxiesV2.s.sol

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {TokenMessengerV2} from "../../src/v2/TokenMessengerV2.sol";
2525
import {TokenMinterV2} from "../../src/v2/TokenMinterV2.sol";
2626
import {MessageTransmitterV2} from "../../src/v2/MessageTransmitterV2.sol";
2727
import {AddressUtils} from "../../src/messages/v2/AddressUtils.sol";
28+
import {SALT_TOKEN_MESSENGER, SALT_MESSAGE_TRANSMITTER} from "./Salts.sol";
2829

2930
contract DeployProxiesV2Script is Script {
3031
// Expose for tests
@@ -64,7 +65,7 @@ contract DeployProxiesV2Script is Script {
6465
uint256 private burnLimitPerMessage;
6566

6667
uint256 private create2FactoryOwnerPrivateKey;
67-
uint256 private tokenMinterV2DeployerPrivateKey;
68+
uint256 private tokenMinterV2OwnerPrivateKey;
6869
uint256 private tokenControllerPrivateKey;
6970

7071
function getProxyCreationCode(
@@ -129,7 +130,7 @@ contract DeployProxiesV2Script is Script {
129130
address messageTransmitterV2ProxyAddress = Create2Factory(factory)
130131
.deployAndMultiCall(
131132
0,
132-
keccak256(type(MessageTransmitterV2).creationCode), // TODO: Verify salt
133+
SALT_MESSAGE_TRANSMITTER,
133134
proxyCreateCode,
134135
multiCallData
135136
);
@@ -153,14 +154,13 @@ contract DeployProxiesV2Script is Script {
153154

154155
// Calculate TokenMessengerV2 proxy address
155156
address expectedTokenMessengerV2ProxyAddress = vm.computeCreate2Address(
156-
keccak256(type(TokenMessengerV2).creationCode),
157-
keccak256(
158-
proxyCreateCode
159-
),
157+
SALT_TOKEN_MESSENGER,
158+
keccak256(proxyCreateCode),
160159
factory
161160
);
162161

163-
bool remoteTokenMessengerV2FromEnv = remoteTokenMessengerV2Addresses.length > 0;
162+
bool remoteTokenMessengerV2FromEnv = remoteTokenMessengerV2Addresses
163+
.length > 0;
164164

165165
// Construct initializer
166166
bytes32[] memory remoteTokenMessengerAddresses = new bytes32[](
@@ -169,9 +169,13 @@ contract DeployProxiesV2Script is Script {
169169
uint256 remoteDomainsLength = remoteDomains.length;
170170
for (uint256 i = 0; i < remoteDomainsLength; ++i) {
171171
if (remoteTokenMessengerV2FromEnv) {
172-
remoteTokenMessengerAddresses[i] = remoteTokenMessengerV2Addresses[i];
172+
remoteTokenMessengerAddresses[
173+
i
174+
] = remoteTokenMessengerV2Addresses[i];
173175
} else {
174-
remoteTokenMessengerAddresses[i] = AddressUtils.toBytes32(expectedTokenMessengerV2ProxyAddress);
176+
remoteTokenMessengerAddresses[i] = AddressUtils.toBytes32(
177+
expectedTokenMessengerV2ProxyAddress
178+
);
175179
}
176180
}
177181
bytes memory initializer = abi.encodeWithSelector(
@@ -209,7 +213,7 @@ contract DeployProxiesV2Script is Script {
209213
address tokenMessengerV2ProxyAddress = Create2Factory(factory)
210214
.deployAndMultiCall(
211215
0,
212-
keccak256(type(TokenMessengerV2).creationCode), // TODO: Verify salt
216+
SALT_TOKEN_MESSENGER,
213217
proxyCreateCode,
214218
multiCallData
215219
);
@@ -341,12 +345,12 @@ contract DeployProxiesV2Script is Script {
341345
burnLimitPerMessage = vm.envUint("BURN_LIMIT_PER_MESSAGE");
342346

343347
create2FactoryOwnerPrivateKey = vm.envUint("CREATE2_FACTORY_OWNER_KEY");
344-
tokenMinterV2DeployerPrivateKey = vm.envUint(
345-
"TOKEN_MINTER_V2_DEPLOYER_KEY"
346-
);
348+
tokenMinterV2OwnerPrivateKey = vm.envUint("TOKEN_MINTER_V2_OWNER_KEY");
347349
tokenControllerPrivateKey = vm.envUint("TOKEN_CONTROLLER_KEY");
348350

349-
bytes32[] memory emptyRemoteTokenMessengerV2Addresses = new bytes32[](0);
351+
bytes32[] memory emptyRemoteTokenMessengerV2Addresses = new bytes32[](
352+
0
353+
);
350354
remoteTokenMessengerV2Addresses = vm.envOr(
351355
"REMOTE_TOKEN_MESSENGER_V2_ADDRESSES",
352356
",",
@@ -370,7 +374,7 @@ contract DeployProxiesV2Script is Script {
370374
);
371375

372376
addMessengerPauserRescuerToTokenMinterV2(
373-
tokenMinterV2DeployerPrivateKey,
377+
tokenMinterV2OwnerPrivateKey,
374378
tokenControllerPrivateKey,
375379
tokenMinterV2,
376380
address(tokenMessengerV2)

scripts/v2/Salts.sol

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2024 Circle Internet Group, Inc. All rights reserved.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
pragma solidity 0.7.6;
19+
20+
// Salts used for CREATE2 deployments
21+
22+
bytes32 constant SALT_TOKEN_MINTER = keccak256("cctp.v2.tokenminter");
23+
bytes32 constant SALT_TOKEN_MESSENGER = keccak256("cctp.v2.tokenmessenger");
24+
bytes32 constant SALT_MESSAGE_TRANSMITTER = keccak256(
25+
"cctp.v2.messagetransmitter"
26+
);
27+
bytes32 constant SALT_ADDRESS_UTILS_EXTERNAL = keccak256(
28+
"cctp.v2.addressutilsexternal"
29+
);

test/scripts/v2/DeployImplementationsV2.t.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ contract DeployImplementationsV2Test is ScriptV2TestUtils {
3737

3838
// TokenMinterV2
3939
assertEq(tokenMinterV2.tokenController(), deployer);
40+
assertEq(tokenMinterV2.owner(), deployer);
4041

4142
// TokenMessengerV2
4243
assertEq(

test/scripts/v2/DeployProxiesV2.t.sol

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {DeployImplementationsV2Script} from "../../../scripts/v2/DeployImplement
2222
import {DeployProxiesV2Script} from "../../../scripts/v2/DeployProxiesV2.s.sol";
2323
import {MessageTransmitterV2} from "../../../src/v2/MessageTransmitterV2.sol";
2424
import {TokenMessengerV2} from "../../../src/v2/TokenMessengerV2.sol";
25+
import {SALT_MESSAGE_TRANSMITTER, SALT_TOKEN_MESSENGER} from "../../../scripts/v2/Salts.sol";
2526

2627
contract DeployProxiesV2Test is ScriptV2TestUtils {
2728
DeployProxiesV2Script deployProxiesV2Script;
@@ -36,7 +37,7 @@ contract DeployProxiesV2Test is ScriptV2TestUtils {
3637
function testDeployMessageTransmitterV2() public {
3738
// create2 address
3839
address predicted = create2Factory.computeAddress(
39-
keccak256(type(MessageTransmitterV2).creationCode),
40+
SALT_MESSAGE_TRANSMITTER,
4041
keccak256(
4142
deployProxiesV2Script.getProxyCreationCode(
4243
address(create2Factory),
@@ -74,7 +75,7 @@ contract DeployProxiesV2Test is ScriptV2TestUtils {
7475
function testDeployTokenMessengerV2() public {
7576
// create2 address
7677
address predicted = create2Factory.computeAddress(
77-
keccak256(type(TokenMessengerV2).creationCode),
78+
SALT_TOKEN_MESSENGER,
7879
keccak256(
7980
deployProxiesV2Script.getProxyCreationCode(
8081
address(create2Factory),
@@ -105,9 +106,13 @@ contract DeployProxiesV2Test is ScriptV2TestUtils {
105106
// remote token messengers
106107
for (uint256 i = 0; i < remoteDomains.length; i++) {
107108
uint32 remoteDomain = remoteDomains[i];
108-
bytes32 remoteTokenMessengerAddress = bytes32(uint256(uint160(address(tokenMessengerV2))));
109+
bytes32 remoteTokenMessengerAddress = bytes32(
110+
uint256(uint160(address(tokenMessengerV2)))
111+
);
109112
if (remoteTokenMessengerV2FromEnv) {
110-
remoteTokenMessengerAddress = bytes32(uint256(uint160(address(remoteTokenMessengerV2s[i]))));
113+
remoteTokenMessengerAddress = bytes32(
114+
uint256(uint160(address(remoteTokenMessengerV2s[i])))
115+
);
111116
}
112117
assertEq(
113118
tokenMessengerV2.remoteTokenMessengers(remoteDomain),

0 commit comments

Comments
 (0)