-
Couldn't load subscription status.
- Fork 113
feat(l2): shared bridge router contract #4834
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
base: main
Are you sure you want to change the base?
Changes from 22 commits
caea86a
2284147
d3d9985
a023188
109e234
508438a
f104f5c
018a63d
dfdb8ea
a57fead
21ad3aa
1901b1d
1a99f05
c220c3e
d937327
19df3e8
81fbdd9
78011eb
0bc4f04
b102114
f127bdc
d55eed5
12bc29c
29cac02
b5b3a76
f3f083a
41b8adc
7c826ed
af2558d
0b35959
17e3f1f
02d20de
54010cf
36463e3
c806c25
8f95d80
089e3df
486e5aa
df9c9d8
425dbd7
9be647d
5329629
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 |
|---|---|---|
|
|
@@ -333,6 +333,23 @@ pub struct DeployerOptions { | |
| help = "The L1 address of the L2 native token (e.g., USDC, USDT, DAI, etc. Use address(0) for ETH)" | ||
| )] | ||
| pub native_token_l1_address: Address, | ||
| #[arg( | ||
| long = "router.deploy", | ||
| default_value = "false", | ||
| env = "ETHREX_SHARED_BRIDGE_DEPLOY_ROUTER", | ||
| help_heading = "Deployer options", | ||
| help = "If set, the deployer will deploy the shared bridge router contract. Default to false", | ||
| conflicts_with = "router" | ||
| )] | ||
| pub deploy_router: bool, | ||
| #[arg( | ||
| long = "router.address", | ||
| value_name = "ADDRESS", | ||
| env = "ETHREX_SHARED_BRIDGE_ROUTER_ADDRESS", | ||
| help_heading = "Deployer options", | ||
| help = "The address of the shared bridge router" | ||
| )] | ||
| pub router: Option<Address>, | ||
| } | ||
|
|
||
| impl Default for DeployerOptions { | ||
|
|
@@ -416,6 +433,8 @@ impl Default for DeployerOptions { | |
| inclusion_max_wait: 3000, | ||
| use_compiled_genesis: true, | ||
| native_token_l1_address: H160::zero(), | ||
| router: None, | ||
| deploy_router: false, | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -459,6 +478,13 @@ pub enum DeployerError { | |
| Genesis, | ||
| } | ||
|
|
||
| /// Bytecode of the Router contract. | ||
| /// This is generated by the [build script](./build.rs). | ||
| const ROUTER_BYTECODE: &[u8] = include_bytes!(concat!( | ||
| env!("OUT_DIR"), | ||
| "/contracts/solc_out/Router.bytecode" | ||
| )); | ||
|
|
||
| /// Bytecode of the OnChainProposer contract. | ||
| /// This is generated by the [build script](./build.rs). | ||
| const ON_CHAIN_PROPOSER_BYTECODE: &[u8] = include_bytes!(concat!( | ||
|
|
@@ -495,12 +521,14 @@ const SP1_VERIFIER_BYTECODE: &[u8] = include_bytes!(concat!( | |
| )); | ||
|
|
||
| const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE_BASED: &str = "initialize(bool,address,address,address,address,address,bytes32,bytes32,bytes32,address,uint256)"; | ||
| const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE: &str = "initialize(bool,address,address,address,address,address,bytes32,bytes32,bytes32,address[],uint256)"; | ||
| const INITIALIZE_ON_CHAIN_PROPOSER_SIGNATURE: &str = "initialize(bool,address,address,address,address,address,bytes32,bytes32,bytes32,address[],uint256,address)"; | ||
|
|
||
| const INITIALIZE_BRIDGE_ADDRESS_SIGNATURE: &str = "initializeBridgeAddress(address)"; | ||
| const TRANSFER_OWNERSHIP_SIGNATURE: &str = "transferOwnership(address)"; | ||
| const ACCEPT_OWNERSHIP_SIGNATURE: &str = "acceptOwnership()"; | ||
| const BRIDGE_INITIALIZER_SIGNATURE: &str = "initialize(address,address,uint256,address)"; | ||
| const BRIDGE_INITIALIZER_SIGNATURE: &str = "initialize(address,address,uint256,address,address)"; | ||
| const ROUTER_INITIALIZER_SIGNATURE: &str = "initialize(address)"; | ||
| const ROUTER_REGISTER_SIGNATURE: &str = "register(uint256,address,address)"; | ||
|
|
||
| // deposit(uint256 _amount, address _l2Recipient) | ||
| const NATIVE_TOKEN_DEPOSIT_SIGNATURE: &str = "deposit(uint256,address)"; | ||
|
|
@@ -517,6 +545,7 @@ pub struct ContractAddresses { | |
| pub tdx_verifier_address: Address, | ||
| pub sequencer_registry_address: Address, | ||
| pub aligned_aggregator_address: Address, | ||
| pub router: Option<Address>, | ||
| } | ||
|
|
||
| pub async fn deploy_l1_contracts( | ||
|
|
@@ -535,9 +564,32 @@ pub async fn deploy_l1_contracts( | |
| Some(opts.maximum_allowed_max_fee_per_blob_gas), | ||
| )?; | ||
|
|
||
| let genesis: Genesis = if opts.use_compiled_genesis { | ||
| serde_json::from_str(LOCAL_DEVNETL2_GENESIS_CONTENTS).map_err(|_| DeployerError::Genesis)? | ||
| } else { | ||
| read_genesis_file( | ||
| opts.genesis_l2_path | ||
| .to_str() | ||
| .ok_or(DeployerError::FailedToGetStringFromPath)?, | ||
| ) | ||
| }; | ||
|
|
||
| let contract_addresses = deploy_contracts(ð_client, &opts, &signer).await?; | ||
|
|
||
| initialize_contracts(contract_addresses, ð_client, &opts, &signer).await?; | ||
| initialize_contracts(contract_addresses, ð_client, &opts, &genesis, &signer).await?; | ||
|
|
||
| if contract_addresses.router.is_some() | ||
| && register_chain( | ||
| ð_client, | ||
| contract_addresses, | ||
| genesis.config.chain_id, | ||
| &signer, | ||
| ) | ||
| .await | ||
| .is_err() | ||
| { | ||
| warn!("Could not register chain in shared bridge router"); | ||
| } | ||
|
||
|
|
||
| if opts.deposit_rich { | ||
| if opts.native_token_l1_address != Address::zero() { | ||
|
|
@@ -573,8 +625,6 @@ async fn deploy_contracts( | |
| ) -> Result<ContractAddresses, DeployerError> { | ||
| trace!("Deploying contracts"); | ||
|
|
||
| info!("Deploying OnChainProposer"); | ||
|
|
||
| let salt = if opts.randomize_contract_deployment { | ||
| H256::random().as_bytes().to_vec() | ||
| } else { | ||
|
|
@@ -584,6 +634,31 @@ async fn deploy_contracts( | |
| .to_vec() | ||
| }; | ||
|
|
||
| let deployed_router = if opts.deploy_router { | ||
| info!("Deploying Router"); | ||
|
|
||
| let bytecode = ROUTER_BYTECODE.to_vec(); | ||
| if bytecode.is_empty() { | ||
| return Err(DeployerError::BytecodeNotFound); | ||
| } | ||
|
|
||
| let router_deployment = | ||
| deploy_with_proxy_from_bytecode(deployer, eth_client, &bytecode, &salt).await?; | ||
| info!( | ||
| "Router deployed:\n Proxy -> address={:#x}, tx_hash={:#x}\n Impl -> address={:#x}, tx_hash={:#x}", | ||
| router_deployment.proxy_address, | ||
| router_deployment.proxy_tx_hash, | ||
| router_deployment.implementation_address, | ||
| router_deployment.implementation_tx_hash, | ||
| ); | ||
|
|
||
| Some(router_deployment.proxy_address) | ||
| } else { | ||
| None | ||
| }; | ||
|
|
||
| info!("Deploying OnChainProposer"); | ||
|
|
||
| trace!("Attempting to deploy OnChainProposer contract"); | ||
| let bytecode = if opts.deploy_based_contracts { | ||
| ON_CHAIN_PROPOSER_BASED_BYTECODE.to_vec() | ||
|
|
@@ -696,6 +771,7 @@ async fn deploy_contracts( | |
| tdx_verifier_address, | ||
| sequencer_registry_address: sequencer_registry_deployment.proxy_address, | ||
| aligned_aggregator_address: opts.aligned_aggregator_address, | ||
| router: opts.router.or(deployed_router), | ||
ManuelBilbao marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }) | ||
| } | ||
|
|
||
|
|
@@ -755,22 +831,13 @@ async fn initialize_contracts( | |
| contract_addresses: ContractAddresses, | ||
| eth_client: &EthClient, | ||
| opts: &DeployerOptions, | ||
| genesis: &Genesis, | ||
| initializer: &Signer, | ||
| ) -> Result<(), DeployerError> { | ||
| trace!("Initializing contracts"); | ||
|
|
||
| trace!(committer_l1_address = %opts.committer_l1_address, "Using committer L1 address for OnChainProposer initialization"); | ||
|
|
||
| let genesis: Genesis = if opts.use_compiled_genesis { | ||
| serde_json::from_str(LOCAL_DEVNETL2_GENESIS_CONTENTS).map_err(|_| DeployerError::Genesis)? | ||
| } else { | ||
| read_genesis_file( | ||
| opts.genesis_l2_path | ||
| .to_str() | ||
| .ok_or(DeployerError::FailedToGetStringFromPath)?, | ||
| ) | ||
| }; | ||
|
|
||
| let sp1_vk = read_vk(&opts.sp1_vk_path); | ||
| let risc0_vk = read_vk(&opts.risc0_vk_path); | ||
|
|
||
|
|
@@ -834,6 +901,22 @@ async fn initialize_contracts( | |
| }; | ||
| info!(tx_hash = %format!("{initialize_tx_hash:#x}"), "SequencerRegistry initialized"); | ||
| } else { | ||
| if let Some(router) = contract_addresses.router | ||
| && opts.deploy_router | ||
| { | ||
| let calldata_values = vec![Value::Address(deployer_address)]; | ||
| let router_initialization_calldata = | ||
| encode_calldata(ROUTER_INITIALIZER_SIGNATURE, &calldata_values)?; | ||
| let initialize_tx_hash = initialize_contract( | ||
| router, | ||
| router_initialization_calldata, | ||
| initializer, | ||
| eth_client, | ||
| ) | ||
| .await?; | ||
| info!(tx_hash = %format!("{initialize_tx_hash:#x}"), "Router initialized"); | ||
| } | ||
|
|
||
| // Initialize only OnChainProposer without Based config | ||
| let calldata_values = vec![ | ||
| Value::Bool(opts.validium), | ||
|
|
@@ -850,6 +933,7 @@ async fn initialize_contracts( | |
| Value::Address(opts.proof_sender_l1_address), | ||
| ]), | ||
| Value::Uint(genesis.config.chain_id.into()), | ||
| Value::Address(contract_addresses.router.unwrap_or_default()), | ||
| ]; | ||
| trace!(calldata_values = ?calldata_values, "OnChainProposer initialization calldata values"); | ||
| let on_chain_proposer_initialization_calldata = | ||
|
|
@@ -936,6 +1020,7 @@ async fn initialize_contracts( | |
| Value::Address(contract_addresses.on_chain_proposer_address), | ||
| Value::Uint(opts.inclusion_max_wait.into()), | ||
| Value::Address(opts.native_token_l1_address), | ||
| Value::Address(contract_addresses.router.unwrap_or_default()), | ||
| ]; | ||
| let bridge_initialization_calldata = | ||
| encode_calldata(BRIDGE_INITIALIZER_SIGNATURE, &calldata_values)?; | ||
|
|
@@ -954,6 +1039,36 @@ async fn initialize_contracts( | |
| Ok(()) | ||
| } | ||
|
|
||
| async fn register_chain( | ||
| eth_client: &EthClient, | ||
| contract_addresses: ContractAddresses, | ||
| chain_id: u64, | ||
| deployer: &Signer, | ||
| ) -> Result<(), DeployerError> { | ||
| let params = vec![ | ||
| Value::Uint(U256::from(chain_id)), | ||
| Value::Address(contract_addresses.on_chain_proposer_address), | ||
| Value::Address(contract_addresses.bridge_address), | ||
| ]; | ||
|
|
||
| ethrex_l2_sdk::call_contract( | ||
| eth_client, | ||
| deployer, | ||
| contract_addresses | ||
| .router | ||
| .ok_or(DeployerError::InternalError( | ||
| "Router address is None. This is a bug.".to_string(), | ||
| ))?, | ||
| ROUTER_REGISTER_SIGNATURE, | ||
| params, | ||
| ) | ||
| .await?; | ||
|
|
||
| info!(chain_id, "Chain registered"); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| async fn make_deposits( | ||
| bridge: Address, | ||
| eth_client: &EthClient, | ||
|
|
@@ -1199,6 +1314,11 @@ fn write_contract_addresses_to_env( | |
| "ETHREX_NATIVE_TOKEN_L1_ADDRESS={:#x}", | ||
| native_token_l1_address | ||
| )?; | ||
| writeln!( | ||
| writer, | ||
| "ETHREX_SHARED_BRIDGE_ROUTER_ADDRESS={:#x}", | ||
| contract_addresses.router.unwrap_or_default() | ||
| )?; | ||
| trace!(?env_file_path, "Contract addresses written to .env"); | ||
| Ok(()) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| pub mod batch; | ||
| pub mod fee_config; | ||
| pub mod l2_to_l2_message; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| use bytes::Bytes; | ||
| use ethereum_types::{Address, U256}; | ||
| use ethrex_rlp::{decode::RLPDecode, structs::Decoder}; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| #[derive(Debug, Clone, Default, Serialize, Deserialize)] | ||
| /// Represents a message from the L2 to another L2 | ||
| pub struct L2toL2Message { | ||
| /// Chain id of the destination chain | ||
| pub chain_id: U256, | ||
| /// Address that originated the transaction | ||
| pub from: Address, | ||
| /// Address of the recipient in the destination chain | ||
| pub to: Address, | ||
| /// Amount of ETH to send to the recipient | ||
| pub value: U256, | ||
| /// Gas limit for the transaction execution in the destination chain | ||
| pub gas_limit: U256, | ||
| /// Calldata for the transaction in the destination chain | ||
| pub data: Bytes, | ||
| } | ||
|
|
||
| impl RLPDecode for L2toL2Message { | ||
| fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), ethrex_rlp::error::RLPDecodeError> { | ||
| let decoder = Decoder::new(rlp)?; | ||
|
|
||
| let (chain_id, decoder) = decoder.decode_field("chain_id")?; | ||
| let (from, decoder) = decoder.decode_field("from")?; | ||
| let (to, decoder) = decoder.decode_field("to")?; | ||
| let (value, decoder) = decoder.decode_field("value")?; | ||
| let (gas_limit, decoder) = decoder.decode_field("gas_limit")?; | ||
| let (data, decoder) = decoder.decode_field("data")?; | ||
|
|
||
| Ok(( | ||
| L2toL2Message { | ||
| chain_id, | ||
| from, | ||
| to, | ||
| value, | ||
| gas_limit, | ||
| data, | ||
| }, | ||
| decoder.finish()?, | ||
| )) | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.