Skip to content

Commit 4cc5238

Browse files
0xClandestineypatil12
authored andcommitted
feat: multichain deployments (#1474)
**Motivation:** *Explain here the context, and why you're making that change. What is the problem you're trying to solve.* **Modifications:** *Describe the modifications you've done.* **Result:** *After your change, what will change.*
1 parent 3de885a commit 4cc5238

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
5+
import "src/contracts/core/ReleaseManager.sol";
6+
import "src/contracts/permissions/PermissionController.sol";
7+
import "script/utils/CrosschainDeployLib.sol";
8+
9+
import "forge-std/Script.sol";
10+
import "forge-std/Test.sol";
11+
12+
contract DeployFromScratch is Script, Test {
13+
using CrosschainDeployLib for *;
14+
15+
Vm cheats = Vm(VM_ADDRESS);
16+
17+
PermissionController constant permissionController =
18+
PermissionController(0x0000000000000000000000000000000000000000);
19+
20+
string semver = "0.0.0";
21+
22+
ProxyAdmin proxyAdmin;
23+
24+
function run() public {
25+
address emptyContract = CrosschainDeployLib.deployEmptyContract();
26+
ReleaseManager proxy =
27+
ReleaseManager(address(emptyContract.deployCrosschainProxy({salt: bytes11(uint88(0x1234))})));
28+
ReleaseManager implementation = new ReleaseManager(permissionController, semver);
29+
ITransparentUpgradeableProxy(address(proxy)).upgradeTo(address(implementation));
30+
ITransparentUpgradeableProxy(address(proxy)).changeAdmin(address(proxyAdmin));
31+
}
32+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.12;
3+
4+
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
5+
import "src/test/mocks/EmptyContract.sol";
6+
7+
/// @dev https://github.com/pcaversaccio/createx/tree/main
8+
ICreateX constant createx = ICreateX(0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed);
9+
10+
interface ICreateX {
11+
function deployCreate2(bytes32 salt, bytes memory initCode) external payable returns (address newContract);
12+
function computeCreate2Address(
13+
bytes32 salt,
14+
bytes32 initCodeHash
15+
) external view returns (address computedAddress);
16+
}
17+
18+
bytes11 constant EMPTY_CONTRACT_SALT = bytes11(uint88(0xffffffffffffffffffffff));
19+
20+
library CrosschainDeployLib {
21+
using CrosschainDeployLib for *;
22+
23+
/// -----------------------------------------------------------------------
24+
/// Write
25+
/// -----------------------------------------------------------------------
26+
27+
/*
28+
* @notice Deploys a crosschain empty contract.
29+
* @dev The empty contract MUST stay consistent across all chains/deployments.
30+
* @dev The empty contract MUST always be deployed with the same salt.
31+
*/
32+
function deployEmptyContract() internal returns (address) {
33+
address computedAddress =
34+
computeCrosschainAddress(msg.sender, keccak256(type(EmptyContract).creationCode), EMPTY_CONTRACT_SALT);
35+
if (computedAddress.code.length != 0) return computedAddress;
36+
return type(EmptyContract).creationCode.deployCrosschain(EMPTY_CONTRACT_SALT);
37+
}
38+
39+
/*
40+
* @notice Deploys a crosschain contract with CreateX.
41+
*
42+
* @dev Example usage:
43+
* ```solidity
44+
* type(EmptyContract).creationCode.deployCrosschain(EMPTY_CONTRACT_SALT)
45+
* ```
46+
*/
47+
function deployCrosschain(bytes memory initCode, bytes11 salt) internal returns (address) {
48+
return createx.deployCreate2(computeProtectedSalt(msg.sender, salt), initCode);
49+
}
50+
51+
/*
52+
* @notice Deploys a crosschain contract using CreateX with the `DEFAULT_SALT`.
53+
*
54+
* @dev Example usage:
55+
* ```solidity
56+
* address emptyContract = type(EmptyContract).creationCode.deployCrosschain();
57+
* ```
58+
*/
59+
function deployCrosschain(
60+
bytes memory initCode
61+
) internal returns (address) {
62+
return deployCrosschain(initCode, EMPTY_CONTRACT_SALT);
63+
}
64+
65+
/*
66+
* @notice Deploys a crosschain `TransparentUpgradeableProxy` using CreateX.
67+
* @dev The initial admin is msg.sender.
68+
* @dev The implementation MUST also be deterministic to ensure the contract can be deployed on all chains.
69+
* @dev The salt MUST be unique for each proxy deployment sharing the same implementation otherwise address collisions WILL occur.
70+
*
71+
* @dev Example usage:
72+
* ```solidity
73+
* bytes11 salt = bytes11(uint88(0xffffffffffffffffffffff));
74+
* address emptyContract = type(EmptyContract).creationCode.deployCrosschain();
75+
* address proxy = emptyContract.deployCrosschainProxy(salt);
76+
* ITransparentUpgradeableProxy(address(proxy)).upgradeTo(address(implementation));
77+
* ITransparentUpgradeableProxy(address(proxy)).changeAdmin(address(admin));
78+
* ```
79+
*/
80+
function deployCrosschainProxy(
81+
address implementation,
82+
bytes11 salt
83+
) internal returns (ITransparentUpgradeableProxy) {
84+
return ITransparentUpgradeableProxy(
85+
deployCrosschain(computeUpgradeableProxyInitCode(implementation, msg.sender), salt)
86+
);
87+
}
88+
89+
/**
90+
* @notice Deploys a crosschain name salted `TransparentUpgradeableProxy` using CreateX.
91+
*/
92+
function deployCrosschainProxy(
93+
address implementation,
94+
string memory name
95+
) internal returns (ITransparentUpgradeableProxy) {
96+
return deployCrosschainProxy(implementation, bytes11(keccak256(bytes(name))));
97+
}
98+
99+
/// -----------------------------------------------------------------------
100+
/// Helpers
101+
/// -----------------------------------------------------------------------
102+
103+
/*
104+
* @notice Returns an encoded CreateX salt.
105+
* @dev The deployer is the only account that can use this salt via CreateX hence the name "protected".
106+
* @dev The salt is structured as: Deployer EOA (20 bytes) | Cross-chain flag (1 byte) | Entropy (11 bytes)
107+
* @dev Example: 0xbebebebebebebebebebebebebebebebebebebebe|ff|1212121212121212121212
108+
*/
109+
function computeProtectedSalt(address deployer, bytes11 salt) internal pure returns (bytes32) {
110+
return bytes32(
111+
bytes.concat(
112+
bytes20(deployer),
113+
bytes1(uint8(1)), // Cross-chain deployments are allowed (0: false, 1: true)
114+
bytes11(salt)
115+
)
116+
);
117+
}
118+
119+
/*
120+
* @notice Returns the initialization code for a transparent upgradeable proxy.
121+
* @dev The returned init code does not include metadata typically appended by the compiler.
122+
*/
123+
function computeUpgradeableProxyInitCode(
124+
address implementation,
125+
address admin
126+
) internal pure returns (bytes memory) {
127+
return abi.encodePacked(type(TransparentUpgradeableProxy).creationCode, abi.encode(implementation, admin, ""));
128+
}
129+
130+
/*
131+
* @notice Returns the predicted address of a contract deployed with CreateX.
132+
*/
133+
function computeCrosschainAddress(
134+
address deployer,
135+
bytes32 initCodeHash,
136+
bytes11 salt
137+
) internal view returns (address) {
138+
return createx.computeCreate2Address(computeProtectedSalt(deployer, salt), initCodeHash);
139+
}
140+
141+
/*
142+
* @notice Returns the predicted address of a `TransparentUpgradeableProxy` deployed with CreateX.
143+
*/
144+
function computeCrosschainUpgradeableProxyAddress(
145+
address implementation,
146+
address admin,
147+
bytes11 salt
148+
) internal view returns (address) {
149+
return computeCrosschainAddress(admin, keccak256(computeUpgradeableProxyInitCode(implementation, admin)), salt);
150+
}
151+
}

0 commit comments

Comments
 (0)