1use crate::constants::CROSS_L2_INBOX_ADDRESS;
7use alloc::{vec, vec::Vec};
8use alloy_primitives::{Bytes, Log, keccak256};
9use alloy_sol_types::{SolEvent, sol};
10use derive_more::{AsRef, From};
11use op_alloy_consensus::OpReceiptEnvelope;
12
13sol! {
14 #[derive(Default, Debug, PartialEq, Eq)]
16 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17 struct MessageIdentifier {
18 address origin;
19 uint256 blockNumber;
20 uint256 logIndex;
21 uint256 timestamp;
22 #[cfg_attr(feature = "serde", serde(rename = "chainID"))]
23 uint256 chainId;
24 }
25
26 #[derive(Default, Debug, PartialEq, Eq)]
34 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35 event ExecutingMessage(bytes32 indexed payloadHash, MessageIdentifier identifier);
36
37 function executeMessage(
42 MessageIdentifier calldata _id,
43 address _target,
44 bytes calldata _message
45 ) external;
46}
47
48#[derive(Debug, Clone, From, AsRef, PartialEq, Eq)]
50pub struct RawMessagePayload(Bytes);
51
52impl From<&Log> for RawMessagePayload {
53 fn from(log: &Log) -> Self {
54 let mut data = vec![0u8; log.topics().len() * 32 + log.data.data.len()];
55 for (i, topic) in log.topics().iter().enumerate() {
56 data[i * 32..(i + 1) * 32].copy_from_slice(topic.as_ref());
57 }
58 data[(log.topics().len() * 32)..].copy_from_slice(log.data.data.as_ref());
59 data.into()
60 }
61}
62
63impl From<Vec<u8>> for RawMessagePayload {
64 fn from(data: Vec<u8>) -> Self {
65 Self(Bytes::from(data))
66 }
67}
68
69impl From<executeMessageCall> for ExecutingMessage {
70 fn from(call: executeMessageCall) -> Self {
71 Self { identifier: call._id, payloadHash: keccak256(call._message.as_ref()) }
72 }
73}
74
75#[derive(Debug)]
78pub struct EnrichedExecutingMessage {
79 pub inner: ExecutingMessage,
81 pub executing_chain_id: u64,
83 pub executing_timestamp: u64,
85}
86
87impl EnrichedExecutingMessage {
88 pub const fn new(
90 inner: ExecutingMessage,
91 executing_chain_id: u64,
92 executing_timestamp: u64,
93 ) -> Self {
94 Self { inner, executing_chain_id, executing_timestamp }
95 }
96}
97
98pub fn extract_executing_messages(receipts: &[OpReceiptEnvelope]) -> Vec<ExecutingMessage> {
104 receipts.iter().fold(Vec::new(), |mut acc, envelope| {
105 let executing_messages = envelope.logs().iter().filter_map(parse_log_to_executing_message);
106
107 acc.extend(executing_messages);
108 acc
109 })
110}
111
112pub fn parse_logs_to_executing_msgs<'a>(
116 logs: impl Iterator<Item = &'a Log>,
117) -> impl Iterator<Item = Option<ExecutingMessage>> {
118 logs.map(parse_log_to_executing_message)
119}
120
121pub fn parse_log_to_executing_message(log: &Log) -> Option<ExecutingMessage> {
126 (log.address == CROSS_L2_INBOX_ADDRESS && log.topics().len() == 2)
127 .then(|| ExecutingMessage::decode_log_data(&log.data, true).ok())
128 .flatten()
129}