1use alloc::vec::Vec;
4use alloy_eips::eip2718::Encodable2718;
5use alloy_primitives::{Address, B256, Bytes, Log, TxKind, U64, U256, b256};
6use op_alloy_consensus::{TxDeposit, UserDepositSource};
7
8pub const DEPOSIT_EVENT_ABI: &str = "TransactionDeposited(address,address,uint256,bytes)";
10
11pub const DEPOSIT_EVENT_ABI_HASH: B256 =
16 b256!("b3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32");
17
18pub const DEPOSIT_EVENT_VERSION_0: B256 = B256::ZERO;
20
21#[derive(Debug, thiserror::Error, PartialEq, Eq)]
23pub enum DepositError {
24 #[error("Unexpected number of deposit event log topics: {0}")]
26 UnexpectedTopicsLen(usize),
27 #[error("Invalid deposit event selector: {1}, expected {0}")]
30 InvalidSelector(B256, B256),
31 #[error("Incomplete opaqueData slice header (incomplete length): {0}")]
33 IncompleteOpaqueData(usize),
34 #[error("Unaligned log data, expected multiple of 32 bytes, got: {0}")]
36 UnalignedData(usize),
37 #[error("Failed to decode the `from` address of the deposit log topic: {0}")]
39 FromDecode(B256),
40 #[error("Failed to decode the `to` address of the deposit log topic: {0}")]
42 ToDecode(B256),
43 #[error("Invalid u64 opaque data content offset: {0}")]
45 InvalidOpaqueDataOffset(Bytes),
46 #[error("Invalid u64 opaque data content length: {0}")]
48 InvalidOpaqueDataLength(Bytes),
49 #[error("Specified opaque data length {1} exceeds the deposit log event data length {0}")]
52 OpaqueDataOverflow(usize, usize),
53 #[error("Opaque data with padding exceeds the specified data length: {1} > {0}")]
56 PaddedOpaqueDataOverflow(usize, usize),
57 #[error("Invalid deposit version: {0}")]
59 InvalidVersion(B256),
60 #[error("Unexpected opaque data length: {0}")]
62 UnexpectedOpaqueDataLen(usize),
63 #[error("Failed to decode the u128 deposit mint value: {0}")]
65 MintDecode(Bytes),
66 #[error("Failed to decode the u64 deposit gas value: {0}")]
68 GasDecode(Bytes),
69}
70
71pub fn decode_deposit(block_hash: B256, index: usize, log: &Log) -> Result<Bytes, DepositError> {
83 let topics = log.data.topics();
84 if topics.len() != 4 {
85 return Err(DepositError::UnexpectedTopicsLen(topics.len()));
86 }
87 if topics[0] != DEPOSIT_EVENT_ABI_HASH {
88 return Err(DepositError::InvalidSelector(DEPOSIT_EVENT_ABI_HASH, topics[0]));
89 }
90 if log.data.data.len() < 64 {
91 return Err(DepositError::IncompleteOpaqueData(log.data.data.len()));
92 }
93 if log.data.data.len() % 32 != 0 {
94 return Err(DepositError::UnalignedData(log.data.data.len()));
95 }
96
97 let mut from_bytes = [0u8; 20];
99 from_bytes.copy_from_slice(&topics[1].as_slice()[12..]);
100 if topics[1].iter().take(12).any(|&b| b != 0) {
101 return Err(DepositError::FromDecode(topics[1]));
102 }
103
104 let mut to_bytes = [0u8; 20];
106 to_bytes.copy_from_slice(&topics[2].as_slice()[12..]);
107 if topics[2].iter().take(12).any(|&b| b != 0) {
108 return Err(DepositError::ToDecode(topics[2]));
109 }
110
111 let from = Address::from(from_bytes);
112 let to = Address::from(to_bytes);
113 let version = log.data.topics()[3];
114
115 let opaque_content_offset: U64 = U64::try_from_be_slice(&log.data.data[24..32]).ok_or(
132 DepositError::InvalidOpaqueDataOffset(Bytes::copy_from_slice(&log.data.data[24..32])),
133 )?;
134 if opaque_content_offset != U64::from(32) {
135 return Err(DepositError::InvalidOpaqueDataOffset(Bytes::copy_from_slice(
136 &log.data.data[24..32],
137 )));
138 }
139
140 let opaque_content_len =
142 u64::from_be_bytes(log.data.data[56..64].try_into().map_err(|_| {
143 DepositError::InvalidOpaqueDataLength(Bytes::copy_from_slice(&log.data.data[56..64]))
144 })?);
145 if opaque_content_len as usize > log.data.data.len() - 64 {
146 return Err(DepositError::OpaqueDataOverflow(
147 opaque_content_len as usize,
148 log.data.data.len() - 64,
149 ));
150 }
151 let padded_len = opaque_content_len.checked_add(32).ok_or(DepositError::OpaqueDataOverflow(
152 opaque_content_len as usize,
153 log.data.data.len() - 64,
154 ))?;
155 if padded_len as usize <= log.data.data.len() - 64 {
156 return Err(DepositError::PaddedOpaqueDataOverflow(
157 log.data.data.len() - 64,
158 opaque_content_len as usize,
159 ));
160 }
161
162 let opaque_data = &log.data.data[64..64 + opaque_content_len as usize];
165 let source = UserDepositSource::new(block_hash, index as u64);
166
167 let mut deposit_tx = TxDeposit {
168 from,
169 is_system_transaction: false,
170 source_hash: source.source_hash(),
171 ..Default::default()
172 };
173
174 if !version.is_zero() {
176 return Err(DepositError::InvalidVersion(version));
177 }
178
179 unmarshal_deposit_version0(&mut deposit_tx, to, opaque_data)?;
180
181 let mut buffer = Vec::with_capacity(deposit_tx.eip2718_encoded_length());
183 deposit_tx.encode_2718(&mut buffer);
184 Ok(Bytes::from(buffer))
185}
186
187pub(crate) fn unmarshal_deposit_version0(
189 tx: &mut TxDeposit,
190 to: Address,
191 data: &[u8],
192) -> Result<(), DepositError> {
193 if data.len() < 32 + 32 + 8 + 1 {
194 return Err(DepositError::UnexpectedOpaqueDataLen(data.len()));
195 }
196
197 let mut offset = 0;
198
199 let raw_mint: [u8; 16] = data[offset + 16..offset + 32].try_into().map_err(|_| {
200 DepositError::MintDecode(Bytes::copy_from_slice(&data[offset + 16..offset + 32]))
201 })?;
202 tx.mint = u128::from_be_bytes(raw_mint);
203 offset += 32;
204
205 tx.value = U256::from_be_slice(&data[offset..offset + 32]);
207 offset += 32;
208
209 let raw_gas: [u8; 8] = data[offset..offset + 8]
211 .try_into()
212 .map_err(|_| DepositError::GasDecode(Bytes::copy_from_slice(&data[offset..offset + 8])))?;
213 tx.gas_limit = u64::from_be_bytes(raw_gas);
214 offset += 8;
215
216 if data[offset] == 0 {
220 tx.to = TxKind::Call(to);
221 } else {
222 tx.to = TxKind::Create;
223 }
224 offset += 1;
225
226 let tx_data_len = data.len() - offset;
229
230 tx.input = Bytes::copy_from_slice(&data[offset..offset + tx_data_len]);
232
233 Ok(())
234}
235
236#[cfg(test)]
237mod test {
238 use super::*;
239 use alloc::vec;
240 use alloy_primitives::{LogData, address, b256, hex};
241
242 #[test]
243 fn test_decode_deposit_invalid_first_topic() {
244 let log = Log {
245 address: Address::default(),
246 data: LogData::new_unchecked(
247 vec![B256::default(), B256::default(), B256::default(), B256::default()],
248 Bytes::default(),
249 ),
250 };
251 let err: DepositError = decode_deposit(B256::default(), 0, &log).unwrap_err();
252 assert_eq!(err, DepositError::InvalidSelector(DEPOSIT_EVENT_ABI_HASH, B256::default()));
253 }
254
255 #[test]
256 fn test_decode_deposit_incomplete_data() {
257 let log = Log {
258 address: Address::default(),
259 data: LogData::new_unchecked(
260 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
261 Bytes::from(vec![0u8; 63]),
262 ),
263 };
264 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
265 assert_eq!(err, DepositError::IncompleteOpaqueData(63));
266 }
267
268 #[test]
269 fn test_decode_deposit_unaligned_data() {
270 let log = Log {
271 address: Address::default(),
272 data: LogData::new_unchecked(
273 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
274 Bytes::from(vec![0u8; 65]),
275 ),
276 };
277 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
278 assert_eq!(err, DepositError::UnalignedData(65));
279 }
280
281 #[test]
282 fn test_decode_deposit_invalid_from() {
283 let invalid_from =
284 b256!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
285 let log = Log {
286 address: Address::default(),
287 data: LogData::new_unchecked(
288 vec![DEPOSIT_EVENT_ABI_HASH, invalid_from, B256::default(), B256::default()],
289 Bytes::from(vec![0u8; 64]),
290 ),
291 };
292 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
293 assert_eq!(err, DepositError::FromDecode(invalid_from));
294 }
295
296 #[test]
297 fn test_decode_deposit_invalid_to() {
298 let invalid_to = b256!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
299 let log = Log {
300 address: Address::default(),
301 data: LogData::new_unchecked(
302 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), invalid_to, B256::default()],
303 Bytes::from(vec![0u8; 64]),
304 ),
305 };
306 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
307 assert_eq!(err, DepositError::ToDecode(invalid_to));
308 }
309
310 #[test]
311 fn test_decode_deposit_invalid_opaque_data_offset() {
312 let log = Log {
313 address: Address::default(),
314 data: LogData::new_unchecked(
315 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
316 Bytes::from(vec![0u8; 64]),
317 ),
318 };
319 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
320 assert_eq!(err, DepositError::InvalidOpaqueDataOffset(Bytes::from(vec![0u8; 8])));
321 }
322
323 #[test]
324 fn test_decode_deposit_opaque_data_overflow() {
325 let mut data = vec![0u8; 128];
326 let offset: [u8; 8] = U64::from(32).to_be_bytes();
327 data[24..32].copy_from_slice(&offset);
328 let len: [u8; 8] = U64::from(128).to_be_bytes();
331 data[56..64].copy_from_slice(&len);
332 let log = Log {
333 address: Address::default(),
334 data: LogData::new_unchecked(
335 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
336 Bytes::from(data),
337 ),
338 };
339 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
340 assert_eq!(err, DepositError::OpaqueDataOverflow(128, 64));
341 }
342
343 #[test]
344 fn test_decode_deposit_padded_overflow() {
345 let mut data = vec![0u8; 256];
346 let offset: [u8; 8] = U64::from(32).to_be_bytes();
347 data[24..32].copy_from_slice(&offset);
348 let len: [u8; 8] = U64::from(64).to_be_bytes();
349 data[56..64].copy_from_slice(&len);
350 let log = Log {
351 address: Address::default(),
352 data: LogData::new_unchecked(
353 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
354 Bytes::from(data),
355 ),
356 };
357 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
358 assert_eq!(err, DepositError::PaddedOpaqueDataOverflow(192, 64));
359 }
360
361 #[test]
362 fn test_decode_deposit_invalid_version() {
363 let mut data = vec![0u8; 128];
364 let offset: [u8; 8] = U64::from(32).to_be_bytes();
365 data[24..32].copy_from_slice(&offset);
366 let len: [u8; 8] = U64::from(64).to_be_bytes();
367 data[56..64].copy_from_slice(&len);
368 let version = b256!("0000000000000000000000000000000000000000000000000000000000000001");
369 let log = Log {
370 address: Address::default(),
371 data: LogData::new_unchecked(
372 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), version],
373 Bytes::from(data),
374 ),
375 };
376 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
377 assert_eq!(err, DepositError::InvalidVersion(version));
378 }
379
380 #[test]
381 fn test_decode_deposit_empty_succeeds() {
382 let valid_to = b256!("000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
383 let valid_from = b256!("000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
384 let mut data = vec![0u8; 192];
385 let offset: [u8; 8] = U64::from(32).to_be_bytes();
386 data[24..32].copy_from_slice(&offset);
387 let len: [u8; 8] = U64::from(128).to_be_bytes();
388 data[56..64].copy_from_slice(&len);
389 let log = Log {
390 address: Address::default(),
391 data: LogData::new_unchecked(
392 vec![DEPOSIT_EVENT_ABI_HASH, valid_from, valid_to, B256::default()],
393 Bytes::from(data),
394 ),
395 };
396 let tx = decode_deposit(B256::default(), 0, &log).unwrap();
397 let raw_hex = hex!(
398 "7ef887a0ed428e1c45e1d9561b62834e1a2d3015a0caae3bfdc16b4da059ac885b01a14594ffffffffffffffffffffffffffffffffffffffff94bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb80808080b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
399 );
400 let expected = Bytes::from(raw_hex);
401 assert_eq!(tx, expected);
402 }
403
404 #[test]
405 fn test_decode_deposit_full_succeeds() {
406 let mut data = vec![0u8; 192];
407 let offset: [u8; 8] = U64::from(32).to_be_bytes();
408 data[24..32].copy_from_slice(&offset);
409 let len: [u8; 8] = U64::from(128).to_be_bytes();
410 data[56..64].copy_from_slice(&len);
411 let mint: [u8; 16] = 10_u128.to_be_bytes();
413 data[80..96].copy_from_slice(&mint);
414 let value: [u8; 32] = U256::from(100).to_be_bytes();
416 data[96..128].copy_from_slice(&value);
417 let gas: [u8; 8] = 1000_u64.to_be_bytes();
419 data[128..136].copy_from_slice(&gas);
420 data[136] = 1;
422 let from = address!("1111111111111111111111111111111111111111");
423 let mut from_bytes = vec![0u8; 32];
424 from_bytes[12..32].copy_from_slice(from.as_slice());
425 let to = address!("2222222222222222222222222222222222222222");
426 let mut to_bytes = vec![0u8; 32];
427 to_bytes[12..32].copy_from_slice(to.as_slice());
428 let log = Log {
429 address: Address::default(),
430 data: LogData::new_unchecked(
431 vec![
432 DEPOSIT_EVENT_ABI_HASH,
433 B256::from_slice(&from_bytes),
434 B256::from_slice(&to_bytes),
435 B256::default(),
436 ],
437 Bytes::from(data),
438 ),
439 };
440 let tx = decode_deposit(B256::default(), 0, &log).unwrap();
441 let raw_hex = hex!(
442 "7ef875a0ed428e1c45e1d9561b62834e1a2d3015a0caae3bfdc16b4da059ac885b01a145941111111111111111111111111111111111111111800a648203e880b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
443 );
444 let expected = Bytes::from(raw_hex);
445 assert_eq!(tx, expected);
446 }
447
448 #[test]
449 fn test_unmarshal_deposit_version0_invalid_len() {
450 let data = vec![0u8; 72];
451 let mut tx = TxDeposit::default();
452 let to = address!("5555555555555555555555555555555555555555");
453 let err = unmarshal_deposit_version0(&mut tx, to, &data).unwrap_err();
454 assert_eq!(err, DepositError::UnexpectedOpaqueDataLen(72));
455
456 let data = vec![0u8; 73];
458 let mut tx = TxDeposit::default();
459 let to = address!("5555555555555555555555555555555555555555");
460 unmarshal_deposit_version0(&mut tx, to, &data).unwrap();
461 }
462
463 #[test]
464 fn test_unmarshal_deposit_version0() {
465 let mut data = vec![0u8; 192];
466 let offset: [u8; 8] = U64::from(32).to_be_bytes();
467 data[24..32].copy_from_slice(&offset);
468 let len: [u8; 8] = U64::from(128).to_be_bytes();
469 data[56..64].copy_from_slice(&len);
470 let mint: [u8; 16] = 10_u128.to_be_bytes();
472 data[80..96].copy_from_slice(&mint);
473 let value: [u8; 32] = U256::from(100).to_be_bytes();
475 data[96..128].copy_from_slice(&value);
476 let gas: [u8; 8] = 1000_u64.to_be_bytes();
478 data[128..136].copy_from_slice(&gas);
479 data[136] = 1;
481 let mut tx = TxDeposit {
482 from: address!("1111111111111111111111111111111111111111"),
483 to: TxKind::Call(address!("2222222222222222222222222222222222222222")),
484 value: U256::from(100),
485 gas_limit: 1000,
486 mint: 10,
487 ..Default::default()
488 };
489 let to = address!("5555555555555555555555555555555555555555");
490 unmarshal_deposit_version0(&mut tx, to, &data).unwrap();
491 assert_eq!(tx.to, TxKind::Call(address!("5555555555555555555555555555555555555555")));
492 }
493}