1use std::collections::BTreeMap;
2use std::sync::LazyLock;
3
4use bytes::Bytes;
5use ethereum_types::{Address, H256};
6use ethrex_common::types::balance_diff::{AssetDiff, BalanceDiff};
7use ethrex_common::utils::keccak;
8use ethrex_common::{H160, U256, types::Receipt};
9
10use serde::{Deserialize, Serialize};
11use tracing::warn;
12pub const MESSENGER_ADDRESS: Address = H160([
13 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14 0x00, 0x00, 0xff, 0xfe,
15]);
16
17pub static L1MESSAGE_EVENT_SELECTOR: LazyLock<H256> =
18 LazyLock::new(|| keccak("L1Message(address,bytes32,uint256)".as_bytes()));
19
20pub static L2MESSAGE_EVENT_SELECTOR: LazyLock<H256> = LazyLock::new(|| {
22 keccak("L2Message(uint256,address,address,uint256,uint256,uint256,bytes)".as_bytes())
23});
24
25pub static CROSSCHAIN_MINT_ERC20_SELECTOR: [u8; 4] = [0xf0, 0x26, 0x31, 0x95];
27
28pub const BRIDGE_ADDRESS: Address = H160([
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30 0x00, 0x00, 0xff, 0xff,
31]);
32
33#[derive(Serialize, Deserialize, Debug)]
34pub struct L1MessageProof {
35 pub batch_number: u64,
36 pub message_id: U256,
37 pub message_hash: H256,
38 pub merkle_proof: Vec<H256>,
39}
40
41#[derive(Debug, Clone, Default, Serialize, Deserialize)]
42pub struct L1Message {
44 pub from: Address,
46 pub data_hash: H256,
48 pub message_id: U256,
50}
51
52impl L1Message {
53 pub fn encode(&self) -> Vec<u8> {
54 let mut bytes = Vec::new();
55 bytes.extend_from_slice(&self.from.to_fixed_bytes());
56 bytes.extend_from_slice(&self.data_hash.0);
57 bytes.extend_from_slice(&self.message_id.to_big_endian());
58 bytes
59 }
60}
61
62pub fn get_l1_message_hash(msg: &L1Message) -> H256 {
63 keccak(msg.encode())
64}
65
66pub fn get_l2_message_hash(msg: &L2Message) -> H256 {
67 keccak(msg.encode())
68}
69
70pub fn get_block_l1_messages(receipts: &[Receipt]) -> Vec<L1Message> {
71 receipts
72 .iter()
73 .flat_map(|receipt| {
74 receipt
75 .logs
76 .iter()
77 .filter(|log| {
78 log.address == MESSENGER_ADDRESS
79 && log.topics.contains(&L1MESSAGE_EVENT_SELECTOR)
80 })
81 .flat_map(|log| -> Option<L1Message> {
82 Some(L1Message {
83 from: Address::from_slice(&log.topics.get(1)?.0[12..32]),
84 data_hash: *log.topics.get(2)?,
85 message_id: U256::from_big_endian(&log.topics.get(3)?.to_fixed_bytes()),
86 })
87 })
88 })
89 .collect()
90}
91
92#[derive(Debug, Clone, Default, Serialize, Deserialize)]
93pub struct L2Message {
95 pub dest_chain_id: U256,
97 pub source_chain_id: u64,
99 pub from: Address,
101 pub to: Address,
103 pub value: U256,
105 pub gas_limit: U256,
107 pub tx_id: U256,
109 pub data: Bytes,
111}
112
113impl L2Message {
114 pub fn encode(&self) -> Vec<u8> {
115 [
116 U256::from(self.source_chain_id).to_big_endian().as_ref(),
117 self.from.as_bytes(),
118 self.to.as_bytes(),
119 &self.tx_id.to_big_endian(),
120 &self.value.to_big_endian(),
121 &self.gas_limit.to_big_endian(),
122 keccak(&self.data).as_bytes(),
123 ]
124 .concat()
125 }
126 pub fn from_log(log: ðrex_common::types::Log, source_chain_id: u64) -> Option<L2Message> {
127 let dest_chain_id = U256::from_big_endian(&log.topics.get(1)?.0);
128 let from = H256::from_slice(log.data.get(0..32)?);
129 let from = Address::from_slice(&from.as_fixed_bytes()[12..]);
130 let to = H256::from_slice(log.data.get(32..64)?);
131 let to = Address::from_slice(&to.as_fixed_bytes()[12..]);
132 let value = U256::from_big_endian(log.data.get(64..96)?);
133 let gas_limit = U256::from_big_endian(log.data.get(96..128)?);
134 let tx_id = U256::from_big_endian(log.data.get(128..160)?);
135 let calldata_len = U256::from_big_endian(log.data.get(192..224)?);
137 let calldata = log
138 .data
139 .get(224..224 + usize::try_from(calldata_len).ok()?)?;
140
141 Some(L2Message {
142 dest_chain_id,
143 source_chain_id,
144 from,
145 to,
146 value,
147 gas_limit,
148 tx_id,
149 data: Bytes::copy_from_slice(calldata),
150 })
151 }
152}
153
154pub fn get_block_l2_out_messages(receipts: &[Receipt], source_chain_id: u64) -> Vec<L2Message> {
155 receipts
156 .iter()
157 .flat_map(|receipt| {
158 receipt
159 .logs
160 .iter()
161 .filter(|log| {
162 log.address == MESSENGER_ADDRESS
163 && log.topics.first() == Some(&*L2MESSAGE_EVENT_SELECTOR)
164 && log.topics.len() >= 2 })
166 .filter_map(|log| L2Message::from_log(log, source_chain_id))
167 })
168 .collect()
169}
170
171pub fn get_balance_diffs(messages: &[L2Message]) -> Vec<BalanceDiff> {
172 let mut balance_diffs: BTreeMap<U256, BalanceDiff> = BTreeMap::new();
173 for message in messages {
174 let mut offset = 4;
175 let (value, value_per_token_decoded) = if let Some(selector) = message.data.get(..4)
176 && *selector == CROSSCHAIN_MINT_ERC20_SELECTOR
177 {
178 let Some(token_l1) = message.data.get(offset + 12..offset + 32) else {
179 warn!("Failed to decode token_l1 from crosschainMintERC20 message");
180 continue;
181 };
182 offset += 32;
183 let Some(token_src_l2) = message.data.get(offset + 12..offset + 32) else {
184 warn!("Failed to decode token_src_l2 from crosschainMintERC20 message");
185 continue;
186 };
187 offset += 32;
188 let Some(token_dst_l2) = message.data.get(offset + 12..offset + 32) else {
189 warn!("Failed to decode token_dst_l2 from crosschainMintERC20 message");
190 continue;
191 };
192 offset += 32;
193 offset += 32; let Some(value_bytes) = message.data.get(offset..offset + 32) else {
195 warn!("Failed to decode value from crosschainMintERC20 message");
196 continue;
197 };
198 (
199 U256::zero(),
200 Some(AssetDiff {
201 token_l1: Address::from_slice(token_l1),
202 token_src_l2: Address::from_slice(token_src_l2),
203 token_dst_l2: Address::from_slice(token_dst_l2),
204 value: U256::from_big_endian(value_bytes),
205 }),
206 )
207 } else {
208 let mut value = message.value;
209 if message.to == BRIDGE_ADDRESS && message.from == BRIDGE_ADDRESS {
210 value = U256::zero();
212 }
213 (value, None)
214 };
215 let entry = balance_diffs
216 .entry(message.dest_chain_id)
217 .or_insert(BalanceDiff {
218 chain_id: message.dest_chain_id,
219 value: U256::zero(),
220 value_per_token: Vec::new(),
221 message_hashes: Vec::new(),
222 });
223 if let Some(value_per_token_decoded) = value_per_token_decoded {
224 if let Some(existing) = entry.value_per_token.iter_mut().find(|v| {
225 v.token_l1 == value_per_token_decoded.token_l1
226 && v.token_src_l2 == value_per_token_decoded.token_src_l2
227 && v.token_dst_l2 == value_per_token_decoded.token_dst_l2
228 }) {
229 existing.value += value_per_token_decoded.value;
230 } else {
231 entry.value_per_token.push(value_per_token_decoded);
232 }
233 }
234 entry.value += value;
235 entry.message_hashes.push(get_l2_message_hash(message));
236 }
237 balance_diffs.into_values().collect()
238}