-
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 24 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,32 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {BytecodeDeployer} from 'contracts/BytecodeDeployer.sol'; | ||
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 deployment address:', _l1Factory.L2_ADAPTER()); | ||
console.log('L2 USDC proxy deployed deployment address:', _l1Factory.L2_USDC_PROXY()); | ||
console.log('L2 USDC implementation deployed deployment address:', _l1Factory.L2_USDC_IMPLEMENTATION()); | ||
console.log('L2OpUSDCFactory deployed at:', _l1Factory.L2_FACTORY()); | ||
|
||
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_CROSS_DOMAIN_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_CROSS_DOMAIN_MESSENGER, MIN_GAS_LIMIT); | ||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {BytecodeDeployer} from 'contracts/BytecodeDeployer.sol'; | ||
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 deployment address:', _l1Factory.L2_ADAPTER()); | ||
console.log('L2 USDC proxy deployed deployment address:', _l1Factory.L2_USDC_PROXY()); | ||
console.log('L2 USDC implementation deployed deployment address:', _l1Factory.L2_USDC_IMPLEMENTATION()); | ||
console.log('L2OpUSDCFactory deployed at:', _l1Factory.L2_FACTORY()); | ||
|
||
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'; | ||
|
||
contract FactoryDeployBase is Script { | ||
address public constant L1_CROSS_DOMAIN_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_CROSS_DOMAIN_MESSENGER, MIN_GAS_LIMIT); | ||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {L1OpUSDCBridgeAdapter} from 'contracts/L1OpUSDCBridgeAdapter.sol'; | ||
import {L2OpUSDCBridgeAdapter} from 'contracts/L2OpUSDCBridgeAdapter.sol'; | ||
import {Script} from 'forge-std/Script.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
import {ICrossDomainMessenger} from 'interfaces/external/ICrossDomainMessenger.sol'; | ||
|
||
contract FactoryDeployOp is Script { | ||
address public constant L1_CROSS_DOMAIN_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_CROSS_DOMAIN_MESSENGER, MIN_GAS_LIMIT); | ||
vm.stopBroadcast(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
contract BytecodeDeployer { | ||
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. Can we add natspec here describing that this is a wrapper contract etc Also I think it would be good if this was inside 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. yes! |
||
/** | ||
* @notice Deploys the contract with the given bytecode | ||
* @param _bytecode The bytecode to be assigned to the contract | ||
*/ | ||
constructor(bytes memory _bytecode) { | ||
// TODO: add some check or will revert if the bytecode is invalid? | ||
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. It will never revert thr contract will be unusuable, you can deploy an address qhatever |
||
assembly { | ||
let _dataStart := add(_bytecode, 32) | ||
let _dataEnd := sub(msize(), _dataStart) | ||
return(_dataStart, _dataEnd) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.25; | ||
|
||
import {BytecodeDeployer} from 'contracts/BytecodeDeployer.sol'; | ||
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. Unused import |
||
import {USDC_PROXY_BYTECODE} from 'contracts/utils/USDCCreationCode.sol'; | ||
|
||
import {L1OpUSDCBridgeAdapter} from 'contracts/L1OpUSDCBridgeAdapter.sol'; | ||
|
||
import {ERC1967Proxy} from '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol'; | ||
import {UpgradeManager} from 'contracts/UpgradeManager.sol'; | ||
import {AddressAliasHelper} from 'contracts/utils/AddressAliasHelper.sol'; | ||
import {IL1OpUSDCBridgeAdapter} from 'interfaces/IL1OpUSDCBridgeAdapter.sol'; | ||
import {IL1OpUSDCFactory} from 'interfaces/IL1OpUSDCFactory.sol'; | ||
|
||
import {L2OpUSDCFactory} from 'contracts/L2OpUSDCFactory.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` contract on L1, the | ||
* `L2OpUSDCBridgeAdapter` and USDC proxy and implementation contracts on L2 on a single transaction. | ||
*/ | ||
contract L1OpUSDCFactory is IL1OpUSDCFactory { | ||
uint256 internal constant _ZERO_VALUE = 0; | ||
|
||
address internal constant _ZERO_ADDRESS = address(0); | ||
|
||
bool internal constant _IS_CREATION = true; | ||
|
||
address public constant L2_MESSENGER = 0x4200000000000000000000000000000000000007; | ||
|
||
address public immutable ALIASED_SELF = AddressAliasHelper.applyL1ToL2Alias(address(this)); | ||
|
||
IL1OpUSDCBridgeAdapter public immutable L1_ADAPTER; | ||
|
||
IUpgradeManager public immutable UPGRADE_MANAGER; | ||
|
||
address public immutable L2_FACTORY; | ||
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. Why do we need to save the l2 factory? This contract should be useless after its deployed anyway 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. True |
||
|
||
address public immutable L2_ADAPTER; | ||
|
||
address public immutable L2_USDC_PROXY; | ||
|
||
address public immutable L2_USDC_IMPLEMENTATION; | ||
|
||
constructor(address _usdc, address _owner) { | ||
// Calculate l1 adapter | ||
uint256 _thisNonceFirstTx = 1; | ||
L1_ADAPTER = IL1OpUSDCBridgeAdapter(_precalculateCreateAddress(address(this), _thisNonceFirstTx)); | ||
|
||
/// NOTE: When the `msg.sender` is not a contract, the first nonce is 0, otherwise it is 1. The aliased address this | ||
/// won't have any code on the L2, so its first nonce is 0. | ||
// Calculate l2 factory address | ||
uint256 _aliasedAddressFirstNonce = 0; | ||
hexshire marked this conversation as resolved.
Show resolved
Hide resolved
|
||
L2_FACTORY = _precalculateCreateAddress(ALIASED_SELF, _aliasedAddressFirstNonce); | ||
// Calculate the l2 deployments using the l2 factory | ||
uint256 _l2FactoryFirstNonce = 1; | ||
L2_USDC_IMPLEMENTATION = _precalculateCreateAddress(L2_FACTORY, _l2FactoryFirstNonce); | ||
uint256 _l2FactorySecondNonce = 2; | ||
L2_USDC_PROXY = _precalculateCreateAddress(L2_FACTORY, _l2FactorySecondNonce); | ||
uint256 _l2FactoryThirdNonce = 3; | ||
L2_ADAPTER = _precalculateCreateAddress(L2_FACTORY, _l2FactoryThirdNonce); | ||
|
||
// 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, address(UPGRADE_MANAGER), address(this)); | ||
emit L1AdapterDeployed(address(L1_ADAPTER)); | ||
|
||
// Deploy the upgrade manager implementation | ||
address _upgradeManagerImplementation = address(new UpgradeManager(address(L1_ADAPTER))); | ||
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. How are we going to set the owner of the upgrade manager? Maybe a constructor param? 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 set on the line 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. Oh i am blind not enough coffee |
||
// 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)); | ||
} | ||
|
||
/** | ||
* @inheritdoc IL1OpUSDCFactory | ||
*/ | ||
function deployL2UsdcAndAdapter(address _l1Messenger, uint32 _minGasLimit) external { | ||
// Get the l2 usdc proxy init code | ||
bytes memory _usdcProxyCArgs = abi.encode(L2_USDC_IMPLEMENTATION); | ||
bytes memory _usdcProxyInitCode = bytes.concat(USDC_PROXY_BYTECODE, _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 of USDC will be always the same, I think is better to just encode the implementation address as unique argument and deploy by the giving init code and not the bytecode. |
||
|
||
// Get the bytecode of the l2 usdc implementation and the l2 adapter | ||
IUpgradeManager.Implementation memory _l2UsdcImplementation = UPGRADE_MANAGER.bridgedUSDCImplementation(); | ||
bytes memory _l2UsdcImplementationBytecode = _l2UsdcImplementation.implementation.code; | ||
|
||
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. | ||
* @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)); | ||
} | ||
// TODO: Needs check, but the following could be removed | ||
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 since the following lines could be removed, but need to double-check it. Just an optimization |
||
// In the case of `_nonce > 0x7f` and `_nonce <= type(uint8).max`, we have the following encoding scheme | ||
// (the same calculation can be carried over for higher _nonce bytes): | ||
// 0xda = 0xc0 (short RLP prefix) + 0x1a (= the bytes length of: 0x94 + address + 0x84 + _nonce, in hex), | ||
// 0x94 = 0x80 + 0x14 (= the bytes length of an address, 20 bytes, in hex), | ||
// 0x84 = 0x80 + 0x04 (= the bytes length of the _nonce, 4 bytes, in hex). | ||
else if (_nonce <= type(uint8).max) { | ||
data = abi.encodePacked(bytes1(0xd7), len, _deployer, bytes1(0x81), uint8(_nonce)); | ||
} else if (_nonce <= type(uint16).max) { | ||
data = abi.encodePacked(bytes1(0xd8), len, _deployer, bytes1(0x82), uint16(_nonce)); | ||
} else if (_nonce <= type(uint24).max) { | ||
data = abi.encodePacked(bytes1(0xd9), len, _deployer, bytes1(0x83), uint24(_nonce)); | ||
} else if (_nonce <= type(uint32).max) { | ||
data = abi.encodePacked(bytes1(0xda), len, _deployer, bytes1(0x84), uint32(_nonce)); | ||
} else if (_nonce <= type(uint40).max) { | ||
data = abi.encodePacked(bytes1(0xdb), len, _deployer, bytes1(0x85), uint40(_nonce)); | ||
} else if (_nonce <= type(uint48).max) { | ||
data = abi.encodePacked(bytes1(0xdc), len, _deployer, bytes1(0x86), uint48(_nonce)); | ||
} else if (_nonce <= type(uint56).max) { | ||
data = abi.encodePacked(bytes1(0xdd), len, _deployer, bytes1(0x87), uint56(_nonce)); | ||
} else { | ||
data = abi.encodePacked(bytes1(0xde), len, _deployer, bytes1(0x88), uint64(_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