-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: proxies and custom factory #23
Changes from 41 commits
a262f7c
dcf3621
d84629a
2376741
a77a13d
60b49cc
834b949
c9dcfa7
cdd0f21
513c911
7f61441
d85e5b0
2b669e7
bcef49d
268b1b1
b486001
2034e74
361e86c
06683fa
0060052
d2bf283
8dcd139
8ec77da
955c22e
ebb10ec
a243828
6910875
5d753aa
868e513
86ab7f3
5326503
7a600ce
95cfbf7
85ab68a
dc6bdb2
3846152
558b7fb
bee19b8
6429925
dc0527a
d2b40f4
c65580e
6dd2afb
6b90c67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
MAINNET_RPC= | ||
MAINNET_DEPLOYER_PK= | ||
L1_FACTORY_MAINNET= | ||
|
||
SEPOLIA_RPC= | ||
SEPOLIA_DEPLOYER_PK= | ||
L1_FACTORY_SEPOLIA= | ||
|
||
ETHERSCAN_API_KEY= |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,15 +8,13 @@ number_underscore = 'thousands' | |
multiline_func_header = 'params_first' | ||
sort_imports = true | ||
|
||
[profile.default] | ||
solc_version = '0.8.25' | ||
libs = ['node_modules'] | ||
optimizer_runs = 10_000 | ||
|
||
[profile.optimized] | ||
via_ir = true | ||
out = 'out-via-ir' | ||
|
||
[profile.default.optimizer_details] | ||
yul = false | ||
Comment on lines
+15
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I needed this becasue the |
||
|
||
[profile.test] | ||
via_ir = true | ||
out = 'out-via-ir' | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {L1OpUSDCFactory} from 'contracts/L1OpUSDCFactory.sol'; | ||
import {Script} from 'forge-std/Script.sol'; | ||
import {console} from 'forge-std/Test.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
|
||
contract FactoryDeploy is Script { | ||
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; | ||
address public deployer = vm.rememberKey(vm.envUint('MAINNET_DEPLOYER_PK')); | ||
|
||
function run() public { | ||
vm.startBroadcast(deployer); | ||
|
||
console.log('Deploying L1OpUSDCFactory ...'); | ||
IL1OpUSDCFactory _l1Factory = new L1OpUSDCFactory(USDC, deployer); | ||
console.log('L1OpUSDCFactory deployed at:', address(_l1Factory)); | ||
|
||
console.log('L1OpUSDCBridgeAdapter deployed at:', address(_l1Factory.L1_ADAPTER())); | ||
console.log('L1 UpgradeManager deployed at:', address(_l1Factory.UPGRADE_MANAGER())); | ||
console.log('-----'); | ||
console.log('aliased L1 factory deployment address:', _l1Factory.ALIASED_SELF()); | ||
console.log('L2OpUSDCBridgeAdapter impl deployment address:', _l1Factory.L2_ADAPTER_IMPLEMENTATION()); | ||
console.log('L2OpUSDCBridgeAdapter proxy deployment address:', _l1Factory.L2_ADAPTER_PROXY()); | ||
console.log('L2 USDC proxy deployed deployment address:', _l1Factory.L2_USDC_PROXY()); | ||
console.log('L2 USDC implementation deployed deployment address:', _l1Factory.L2_USDC_IMPLEMENTATION()); | ||
|
||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {Script} from 'forge-std/Script.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
import {ICrossDomainMessenger} from 'interfaces/external/ICrossDomainMessenger.sol'; | ||
import {IOptimismPortal} from 'interfaces/external/IOptimismPortal.sol'; | ||
|
||
contract FactoryDeployMainnet is Script { | ||
address public constant L1_MESSENGER = 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1; | ||
uint32 public constant MIN_GAS_LIMIT = 8_000_000; | ||
IL1OpUSDCFactory public immutable L1_FACTORY = IL1OpUSDCFactory(vm.envAddress('L1_FACTORY_MAINNET')); | ||
address public deployer = vm.rememberKey(vm.envUint('MAINNET_DEPLOYER_PK')); | ||
|
||
function run() public { | ||
vm.startBroadcast(deployer); | ||
// Deploy the L2 contracts | ||
L1_FACTORY.deployL2UsdcAndAdapter(L1_MESSENGER, MIN_GAS_LIMIT); | ||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {L1OpUSDCFactory} from 'contracts/L1OpUSDCFactory.sol'; | ||
import {Script} from 'forge-std/Script.sol'; | ||
import {console} from 'forge-std/Test.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
|
||
contract FactoryDeploy is Script { | ||
address public constant USDC = 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238; | ||
address public deployer = vm.rememberKey(vm.envUint('SEPOLIA_DEPLOYER_PK')); | ||
|
||
function run() public { | ||
vm.startBroadcast(deployer); | ||
|
||
console.log('Deploying L1OpUSDCFactory ...'); | ||
IL1OpUSDCFactory _l1Factory = new L1OpUSDCFactory(USDC, deployer); | ||
console.log('L1OpUSDCFactory deployed at:', address(_l1Factory)); | ||
|
||
console.log('L1OpUSDCBridgeAdapter deployed at:', address(_l1Factory.L1_ADAPTER())); | ||
console.log('L1 UpgradeManager deployed at:', address(_l1Factory.UPGRADE_MANAGER())); | ||
console.log('-----'); | ||
console.log('aliased L1 factory deployment address:', _l1Factory.ALIASED_SELF()); | ||
console.log('L2OpUSDCBridgeAdapter impl deployment address:', _l1Factory.L2_ADAPTER_IMPLEMENTATION()); | ||
console.log('L2OpUSDCBridgeAdapter proxy deployment address:', _l1Factory.L2_ADAPTER_PROXY()); | ||
console.log('L2 USDC proxy deployed deployment address:', _l1Factory.L2_USDC_PROXY()); | ||
console.log('L2 USDC implementation deployed deployment address:', _l1Factory.L2_USDC_IMPLEMENTATION()); | ||
|
||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {Script} from 'forge-std/Script.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
|
||
contract FactoryDeployBase is Script { | ||
address public constant L1_MESSENGER = 0xC34855F4De64F1840e5686e64278da901e261f20; | ||
uint32 public constant MIN_GAS_LIMIT = 12_000_000; | ||
IL1OpUSDCFactory public immutable L1_FACTORY = IL1OpUSDCFactory(vm.envAddress('L1_FACTORY_SEPOLIA')); | ||
|
||
address public deployer = vm.rememberKey(vm.envUint('SEPOLIA_DEPLOYER_PK')); | ||
|
||
function run() public { | ||
vm.startBroadcast(deployer); | ||
// Deploy the L2 contracts | ||
L1_FACTORY.deployL2UsdcAndAdapter(L1_MESSENGER, MIN_GAS_LIMIT); | ||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {Script} from 'forge-std/Script.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
|
||
contract FactoryDeployOp is Script { | ||
address public constant L1_MESSENGER = 0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef; | ||
uint32 public constant MIN_GAS_LIMIT = 12_000_000; | ||
IL1OpUSDCFactory public immutable L1_FACTORY = IL1OpUSDCFactory(vm.envAddress('L1_FACTORY_SEPOLIA')); | ||
|
||
address public deployer = vm.rememberKey(vm.envUint('SEPOLIA_DEPLOYER_PK')); | ||
|
||
function run() public { | ||
vm.startBroadcast(deployer); | ||
// Deploy the L2 contracts | ||
L1_FACTORY.deployL2UsdcAndAdapter(L1_MESSENGER, MIN_GAS_LIMIT); | ||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol'; | ||
import {L1OpUSDCBridgeAdapter} from 'contracts/L1OpUSDCBridgeAdapter.sol'; | ||
import {L2OpUSDCFactory} from 'contracts/L2OpUSDCFactory.sol'; | ||
import {UpgradeManager} from 'contracts/UpgradeManager.sol'; | ||
import {AddressAliasHelper} from 'contracts/utils/AddressAliasHelper.sol'; | ||
import {USDC_PROXY_CREATION_CODE} from 'contracts/utils/USDCProxyCreationCode.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
import {IUpgradeManager} from 'interfaces/IUpgradeManager.sol'; | ||
import {ICrossDomainMessenger} from 'interfaces/external/ICrossDomainMessenger.sol'; | ||
import {IOptimismPortal} from 'interfaces/external/IOptimismPortal.sol'; | ||
|
||
/** | ||
* @title L1OpUSDCFactory | ||
* @notice Factory contract to deploy and setup the `L1OpUSDCBridgeAdapter` and `UpgradeManager` contracts on L1, and | ||
* L2OpUSDCFactory on L2 - setting up the L2 deployments on a single transaction. | ||
*/ | ||
contract L1OpUSDCFactory is IL1OpUSDCFactory { | ||
/// @notice Zero value constant to be used on portal interaction | ||
uint256 internal constant _ZERO_VALUE = 0; | ||
|
||
/// @notice Zero address constant to be used on portal interaction | ||
address internal constant _ZERO_ADDRESS = address(0); | ||
|
||
/// @notice Flag to indicate that the tx represents a contract creation when interacting with the portal | ||
bool internal constant _IS_CREATION = true; | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
address public constant L2_MESSENGER = 0x4200000000000000000000000000000000000007; | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
address public immutable ALIASED_SELF = AddressAliasHelper.applyL1ToL2Alias(address(this)); | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
address public immutable L1_ADAPTER; | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
IUpgradeManager public immutable UPGRADE_MANAGER; | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
address public immutable L2_ADAPTER_IMPLEMENTATION; | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
address public immutable L2_ADAPTER_PROXY; | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
address public immutable L2_USDC_PROXY; | ||
|
||
/// @inheritdoc IL1OpUSDCFactory | ||
address public immutable L2_USDC_IMPLEMENTATION; | ||
|
||
/** | ||
* @notice Constructs the L1 factory contract, deploys the L1 adapter and the upgrade manager and precalculates the | ||
* addresses of the L2 deployments | ||
* @param _usdc The address of the USDC contract | ||
* @param _owner The owner of the upgrade manager | ||
*/ | ||
constructor(address _usdc, address _owner) { | ||
// Calculate L1 adapter | ||
uint256 _thisNonceFirstTx = 1; | ||
L1_ADAPTER = _precalculateCreateAddress(address(this), _thisNonceFirstTx); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For these wouldnt There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree! |
||
|
||
// Calculate L2 factory address | ||
uint256 _aliasedAddressFirstNonce = 0; | ||
hexshire marked this conversation as resolved.
Show resolved
Hide resolved
|
||
address _l2Factory = _precalculateCreateAddress(ALIASED_SELF, _aliasedAddressFirstNonce); | ||
// Calculate the L2 deployments using the L2 factory | ||
uint256 _l2FactoryFirstNonce = 1; | ||
L2_USDC_IMPLEMENTATION = _precalculateCreateAddress(_l2Factory, _l2FactoryFirstNonce); | ||
uint256 _l2FactorySecondNonce = 2; | ||
L2_USDC_PROXY = _precalculateCreateAddress(_l2Factory, _l2FactorySecondNonce); | ||
uint256 _l2FactoryThirdNonce = 3; | ||
L2_ADAPTER_IMPLEMENTATION = _precalculateCreateAddress(_l2Factory, _l2FactoryThirdNonce); | ||
uint256 _l2FactoryFourthNonce = 4; | ||
L2_ADAPTER_PROXY = _precalculateCreateAddress(_l2Factory, _l2FactoryFourthNonce); | ||
|
||
// Calculate the upgrade manager using 3 as nonce since first the L1 adapter and its implementation will be deployed | ||
uint256 _thisNonceThirdTx = 3; | ||
UPGRADE_MANAGER = IUpgradeManager(_precalculateCreateAddress(address(this), _thisNonceThirdTx)); | ||
|
||
// Deploy the L1 adapter | ||
new L1OpUSDCBridgeAdapter(_usdc, L2_ADAPTER_PROXY, address(UPGRADE_MANAGER), address(this)); | ||
emit L1AdapterDeployed(L1_ADAPTER); | ||
|
||
// Deploy the upgrade manager implementation | ||
address _upgradeManagerImplementation = address(new UpgradeManager(L1_ADAPTER)); | ||
// Deploy and initialize the upgrade manager proxy | ||
bytes memory _initializeTx = abi.encodeWithSelector(IUpgradeManager.initialize.selector, _owner); | ||
UpgradeManager(address(new ERC1967Proxy(address(_upgradeManagerImplementation), _initializeTx))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is a good idea to check this when we'll be verifying the contracts. If etherscan set the name to |
||
emit UpgradeManagerDeployed(address(UPGRADE_MANAGER)); | ||
} | ||
|
||
/** | ||
* @notice Sends the L2 factory creation tx along with the L2 deployments to be done on it through the portal | ||
* @param _l1Messenger The address of the L1 messenger for the L2 Op chain | ||
* @param _minGasLimit The minimum gas limit for the L2 deployment | ||
*/ | ||
function deployL2UsdcAndAdapter(address _l1Messenger, uint32 _minGasLimit) external { | ||
// TODO: When using `CREATE2` to deploy on L2, we'll need to deploy the proxies with the 0 address as implementation | ||
// and then upgrade them with the actual implementation. This is because the `CREATE2` opcode is dependant on the | ||
// creation code and a different implementation would result in a different address. | ||
// Get the L2 usdc proxy init code | ||
bytes memory _usdcProxyCArgs = abi.encode(L2_USDC_IMPLEMENTATION); | ||
bytes memory _usdcProxyInitCode = bytes.concat(USDC_PROXY_CREATION_CODE, _usdcProxyCArgs); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the proxy USDC will never change, we can just get the proper creation code w/o sending the bytecode There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mm actually we should do this in a 2 step process, deploy with a zero address implementation and update it in a new call, this is because of this scenario:
We should deploy with address(0) and then set the implementation address in a seperate call and not do it in the constructor as this could change the deployed address There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also applies to our L2 adapter proxy as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 Will take into consideration when doing the |
||
|
||
// Get the bytecode of the L2 usdc implementation | ||
IUpgradeManager.Implementation memory _l2UsdcImplementation = UPGRADE_MANAGER.bridgedUSDCImplementation(); | ||
bytes memory _l2UsdcImplementationBytecode = _l2UsdcImplementation.implementation.code; | ||
// Get the bytecode of the he L2 adapter | ||
IUpgradeManager.Implementation memory _l2AdapterImplementation = UPGRADE_MANAGER.l2AdapterImplementation(); | ||
bytes memory _l2AdapterBytecode = _l2AdapterImplementation.implementation.code; | ||
|
||
// Get the L2 factory init code | ||
bytes memory _l2FactoryCreationCode = type(L2OpUSDCFactory).creationCode; | ||
bytes memory _l2FactoryCArgs = abi.encode( | ||
_usdcProxyInitCode, | ||
_l2UsdcImplementationBytecode, | ||
_l2UsdcImplementation.initTxs, | ||
_l2AdapterBytecode, | ||
_l2AdapterImplementation.initTxs | ||
); | ||
bytes memory _l2FactoryInitCode = bytes.concat(_l2FactoryCreationCode, _l2FactoryCArgs); | ||
|
||
// Deploy L2 op usdc factory through portal | ||
IOptimismPortal _portal = ICrossDomainMessenger(_l1Messenger).portal(); | ||
_portal.depositTransaction(_ZERO_ADDRESS, _ZERO_VALUE, _minGasLimit, _IS_CREATION, _l2FactoryInitCode); | ||
} | ||
|
||
/** | ||
* @notice Precalculates the address of a contract that will be deployed thorugh `CREATE` opcode | ||
* @dev It only works if the for nonces between 0 and 127, which is enough for this use case | ||
* @param _deployer The deployer address | ||
* @param _nonce The next nonce of the deployer address | ||
* @return _precalculatedAddress The address where the contract will be stored | ||
*/ | ||
function _precalculateCreateAddress( | ||
address _deployer, | ||
uint256 _nonce | ||
) internal pure returns (address _precalculatedAddress) { | ||
bytes memory data; | ||
bytes1 len = bytes1(0x94); | ||
|
||
// The integer zero is treated as an empty byte string and therefore has only one length prefix, | ||
// 0x80, which is calculated via 0x80 + 0. | ||
if (_nonce == 0x00) { | ||
data = abi.encodePacked(bytes1(0xd6), len, _deployer, bytes1(0x80)); | ||
} | ||
// A one-byte integer in the [0x00, 0x7f] range uses its own value as a length prefix, there is no | ||
// additional "0x80 + length" prefix that precedes it. | ||
else if (_nonce <= 0x7f) { | ||
data = abi.encodePacked(bytes1(0xd6), len, _deployer, uint8(_nonce)); | ||
} | ||
|
||
_precalculatedAddress = address(uint160(uint256(keccak256(data)))); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After I added this to the toml:
forge build
asked me (with a warning) to runforge config --fix
. This was removed after running that command