diff --git a/cli/src/commands/l2.rs b/cli/src/commands/l2.rs index 3bd9d5d..0f72808 100644 --- a/cli/src/commands/l2.rs +++ b/cli/src/commands/l2.rs @@ -364,18 +364,19 @@ impl Command { private_key, rpc_url, } => { + let from = get_address_from_secret_key(&private_key)?; + + let client = EthClient::new(&rpc_url)?; + if explorer_url { todo!("Display transaction URL in the explorer") } if token_address.is_some() { + // withdraw_erc20(..) todo!("Handle ERC20 withdrawals") } - let from = get_address_from_secret_key(&private_key)?; - - let client = EthClient::new(&rpc_url)?; - let tx_hash = withdraw(amount, from, private_key, &client, nonce).await?; println!("Withdrawal sent: {tx_hash:#x}"); diff --git a/sdk/src/l2/constants.rs b/sdk/src/l2/constants.rs index 993f08b..37b980d 100644 --- a/sdk/src/l2/constants.rs +++ b/sdk/src/l2/constants.rs @@ -11,4 +11,9 @@ pub const COMMON_BRIDGE_L2_ADDRESS: Address = H160([ pub const L2_WITHDRAW_SIGNATURE: &str = "withdraw(address)"; +pub const L2_WITHDRAW_SIGNATURE_ERC20: &str = "withdrawERC20(address,address,address,uint256)"; + +pub const CLAIM_WITHDRAWAL_ERC20_SIGNATURE: &str = + "claimWithdrawalERC20(address,address,uint256,uint256,uint256,bytes32[])"; + // Function Selectors diff --git a/sdk/src/l2/withdraw.rs b/sdk/src/l2/withdraw.rs index 8e7a968..a9b079c 100644 --- a/sdk/src/l2/withdraw.rs +++ b/sdk/src/l2/withdraw.rs @@ -1,8 +1,14 @@ use crate::{ calldata::{Value, encode_calldata}, - client::{EthClient, EthClientError, Overrides, eth::L1MessageProof}, + client::{ + EthClient, EthClientError, Overrides, + eth::{L1MessageProof, get_address_from_secret_key}, + }, l2::{ - constants::{COMMON_BRIDGE_L2_ADDRESS, L2_WITHDRAW_SIGNATURE}, + constants::{ + CLAIM_WITHDRAWAL_ERC20_SIGNATURE, COMMON_BRIDGE_L2_ADDRESS, L2_WITHDRAW_SIGNATURE, + L2_WITHDRAW_SIGNATURE_ERC20, + }, merkle_tree::merkle_proof, }, }; @@ -42,6 +48,36 @@ pub async fn withdraw( .await } +pub async fn withdraw_erc20( + amount: U256, + from: Address, + from_pk: SecretKey, + token_l1: Address, + token_l2: Address, + l2_client: &EthClient, +) -> Result { + let data = [ + Value::Address(token_l1), + Value::Address(token_l2), + Value::Address(from), + Value::Uint(amount), + ]; + let withdraw_data = encode_calldata(L2_WITHDRAW_SIGNATURE_ERC20, &data) + .expect("Failed to encode calldata for withdraw ERC20"); + let withdraw_transaction = l2_client + .build_eip1559_transaction( + COMMON_BRIDGE_L2_ADDRESS, + from, + Bytes::from(withdraw_data), + Default::default(), + ) + .await?; + + l2_client + .send_eip1559_transaction(&withdraw_transaction, &from_pk) + .await +} + pub async fn claim_withdraw( amount: U256, from: Address, @@ -92,6 +128,57 @@ pub async fn claim_withdraw( .await } +pub async fn claim_erc20withdraw( + token_l1: Address, + token_l2: Address, + amount: U256, + from_pk: SecretKey, + eth_client: &EthClient, + message_proof: &L1MessageProof, + bridge_address: Address, +) -> Result { + let from = get_address_from_secret_key(&from_pk)?; + let calldata_values = vec![ + Value::Address(token_l1), + Value::Address(token_l2), + Value::Uint(amount), + Value::Uint(U256::from(message_proof.batch_number)), + Value::Uint(message_proof.message_id), + Value::Array( + message_proof + .merkle_proof + .clone() + .into_iter() + .map(|v| Value::FixedBytes(Bytes::copy_from_slice(v.as_bytes()))) + .collect(), + ), + ]; + + let claim_withdrawal_data = + encode_calldata(CLAIM_WITHDRAWAL_ERC20_SIGNATURE, &calldata_values)?; + + println!( + "Claiming withdrawal with calldata: {}", + hex::encode(&claim_withdrawal_data) + ); + + let claim_tx = eth_client + .build_eip1559_transaction( + bridge_address, + from, + claim_withdrawal_data.into(), + Overrides { + from: Some(from), + ..Default::default() + }, + ) + .await?; + + eth_client + .send_eip1559_transaction(&claim_tx, &from_pk) + .await +} + /// Returns the formatted hash of the withdrawal transaction, /// or None if the transaction is not a withdrawal. /// The hash is computed as keccak256(to || value || tx_hash)