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 let mint = u128::from_be_bytes(raw_mint);
203
204 if mint == 0 {
206 tx.mint = None;
207 } else {
208 tx.mint = Some(mint);
209 }
210 offset += 32;
211
212 tx.value = U256::from_be_slice(&data[offset..offset + 32]);
214 offset += 32;
215
216 let raw_gas: [u8; 8] = data[offset..offset + 8]
218 .try_into()
219 .map_err(|_| DepositError::GasDecode(Bytes::copy_from_slice(&data[offset..offset + 8])))?;
220 tx.gas_limit = u64::from_be_bytes(raw_gas);
221 offset += 8;
222
223 if data[offset] == 0 {
227 tx.to = TxKind::Call(to);
228 } else {
229 tx.to = TxKind::Create;
230 }
231 offset += 1;
232
233 let tx_data_len = data.len() - offset;
236
237 tx.input = Bytes::copy_from_slice(&data[offset..offset + tx_data_len]);
239
240 Ok(())
241}
242
243#[cfg(test)]
244mod test {
245 use super::*;
246 use alloc::vec;
247 use alloy_primitives::{LogData, address, b256, hex};
248
249 #[test]
250 fn test_decode_deposit_invalid_first_topic() {
251 let log = Log {
252 address: Address::default(),
253 data: LogData::new_unchecked(
254 vec![B256::default(), B256::default(), B256::default(), B256::default()],
255 Bytes::default(),
256 ),
257 };
258 let err: DepositError = decode_deposit(B256::default(), 0, &log).unwrap_err();
259 assert_eq!(err, DepositError::InvalidSelector(DEPOSIT_EVENT_ABI_HASH, B256::default()));
260 }
261
262 #[test]
263 fn test_decode_deposit_incomplete_data() {
264 let log = Log {
265 address: Address::default(),
266 data: LogData::new_unchecked(
267 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
268 Bytes::from(vec![0u8; 63]),
269 ),
270 };
271 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
272 assert_eq!(err, DepositError::IncompleteOpaqueData(63));
273 }
274
275 #[test]
276 fn test_decode_deposit_unaligned_data() {
277 let log = Log {
278 address: Address::default(),
279 data: LogData::new_unchecked(
280 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
281 Bytes::from(vec![0u8; 65]),
282 ),
283 };
284 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
285 assert_eq!(err, DepositError::UnalignedData(65));
286 }
287
288 #[test]
289 fn test_decode_deposit_invalid_from() {
290 let invalid_from =
291 b256!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
292 let log = Log {
293 address: Address::default(),
294 data: LogData::new_unchecked(
295 vec![DEPOSIT_EVENT_ABI_HASH, invalid_from, B256::default(), B256::default()],
296 Bytes::from(vec![0u8; 64]),
297 ),
298 };
299 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
300 assert_eq!(err, DepositError::FromDecode(invalid_from));
301 }
302
303 #[test]
304 fn test_decode_deposit_invalid_to() {
305 let invalid_to = b256!("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
306 let log = Log {
307 address: Address::default(),
308 data: LogData::new_unchecked(
309 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), invalid_to, B256::default()],
310 Bytes::from(vec![0u8; 64]),
311 ),
312 };
313 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
314 assert_eq!(err, DepositError::ToDecode(invalid_to));
315 }
316
317 #[test]
318 fn test_decode_deposit_invalid_opaque_data_offset() {
319 let log = Log {
320 address: Address::default(),
321 data: LogData::new_unchecked(
322 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
323 Bytes::from(vec![0u8; 64]),
324 ),
325 };
326 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
327 assert_eq!(err, DepositError::InvalidOpaqueDataOffset(Bytes::from(vec![0u8; 8])));
328 }
329
330 #[test]
331 fn test_decode_deposit_opaque_data_overflow() {
332 let mut data = vec![0u8; 128];
333 let offset: [u8; 8] = U64::from(32).to_be_bytes();
334 data[24..32].copy_from_slice(&offset);
335 let len: [u8; 8] = U64::from(128).to_be_bytes();
338 data[56..64].copy_from_slice(&len);
339 let log = Log {
340 address: Address::default(),
341 data: LogData::new_unchecked(
342 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
343 Bytes::from(data),
344 ),
345 };
346 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
347 assert_eq!(err, DepositError::OpaqueDataOverflow(128, 64));
348 }
349
350 #[test]
351 fn test_decode_deposit_padded_overflow() {
352 let mut data = vec![0u8; 256];
353 let offset: [u8; 8] = U64::from(32).to_be_bytes();
354 data[24..32].copy_from_slice(&offset);
355 let len: [u8; 8] = U64::from(64).to_be_bytes();
356 data[56..64].copy_from_slice(&len);
357 let log = Log {
358 address: Address::default(),
359 data: LogData::new_unchecked(
360 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), B256::default()],
361 Bytes::from(data),
362 ),
363 };
364 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
365 assert_eq!(err, DepositError::PaddedOpaqueDataOverflow(192, 64));
366 }
367
368 #[test]
369 fn test_decode_deposit_invalid_version() {
370 let mut data = vec![0u8; 128];
371 let offset: [u8; 8] = U64::from(32).to_be_bytes();
372 data[24..32].copy_from_slice(&offset);
373 let len: [u8; 8] = U64::from(64).to_be_bytes();
374 data[56..64].copy_from_slice(&len);
375 let version = b256!("0000000000000000000000000000000000000000000000000000000000000001");
376 let log = Log {
377 address: Address::default(),
378 data: LogData::new_unchecked(
379 vec![DEPOSIT_EVENT_ABI_HASH, B256::default(), B256::default(), version],
380 Bytes::from(data),
381 ),
382 };
383 let err = decode_deposit(B256::default(), 0, &log).unwrap_err();
384 assert_eq!(err, DepositError::InvalidVersion(version));
385 }
386
387 #[test]
388 fn test_decode_deposit_empty_succeeds() {
389 let valid_to = b256!("000000000000000000000000bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
390 let valid_from = b256!("000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
391 let mut data = vec![0u8; 192];
392 let offset: [u8; 8] = U64::from(32).to_be_bytes();
393 data[24..32].copy_from_slice(&offset);
394 let len: [u8; 8] = U64::from(128).to_be_bytes();
395 data[56..64].copy_from_slice(&len);
396 let log = Log {
397 address: Address::default(),
398 data: LogData::new_unchecked(
399 vec![DEPOSIT_EVENT_ABI_HASH, valid_from, valid_to, B256::default()],
400 Bytes::from(data),
401 ),
402 };
403 let tx = decode_deposit(B256::default(), 0, &log).unwrap();
404 let raw_hex = hex!(
405 "7ef887a0ed428e1c45e1d9561b62834e1a2d3015a0caae3bfdc16b4da059ac885b01a14594ffffffffffffffffffffffffffffffffffffffff94bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb80808080b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
406 );
407 let expected = Bytes::from(raw_hex);
408 assert_eq!(tx, expected);
409 }
410
411 #[test]
412 fn test_decode_deposit_full_succeeds() {
413 let mut data = vec![0u8; 192];
414 let offset: [u8; 8] = U64::from(32).to_be_bytes();
415 data[24..32].copy_from_slice(&offset);
416 let len: [u8; 8] = U64::from(128).to_be_bytes();
417 data[56..64].copy_from_slice(&len);
418 let mint: [u8; 16] = 10_u128.to_be_bytes();
420 data[80..96].copy_from_slice(&mint);
421 let value: [u8; 32] = U256::from(100).to_be_bytes();
423 data[96..128].copy_from_slice(&value);
424 let gas: [u8; 8] = 1000_u64.to_be_bytes();
426 data[128..136].copy_from_slice(&gas);
427 data[136] = 1;
429 let from = address!("1111111111111111111111111111111111111111");
430 let mut from_bytes = vec![0u8; 32];
431 from_bytes[12..32].copy_from_slice(from.as_slice());
432 let to = address!("2222222222222222222222222222222222222222");
433 let mut to_bytes = vec![0u8; 32];
434 to_bytes[12..32].copy_from_slice(to.as_slice());
435 let log = Log {
436 address: Address::default(),
437 data: LogData::new_unchecked(
438 vec![
439 DEPOSIT_EVENT_ABI_HASH,
440 B256::from_slice(&from_bytes),
441 B256::from_slice(&to_bytes),
442 B256::default(),
443 ],
444 Bytes::from(data),
445 ),
446 };
447 let tx = decode_deposit(B256::default(), 0, &log).unwrap();
448 let raw_hex = hex!(
449 "7ef875a0ed428e1c45e1d9561b62834e1a2d3015a0caae3bfdc16b4da059ac885b01a145941111111111111111111111111111111111111111800a648203e880b700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
450 );
451 let expected = Bytes::from(raw_hex);
452 assert_eq!(tx, expected);
453 }
454
455 #[test]
456 fn test_unmarshal_deposit_version0_invalid_len() {
457 let data = vec![0u8; 72];
458 let mut tx = TxDeposit::default();
459 let to = address!("5555555555555555555555555555555555555555");
460 let err = unmarshal_deposit_version0(&mut tx, to, &data).unwrap_err();
461 assert_eq!(err, DepositError::UnexpectedOpaqueDataLen(72));
462
463 let data = vec![0u8; 73];
465 let mut tx = TxDeposit::default();
466 let to = address!("5555555555555555555555555555555555555555");
467 unmarshal_deposit_version0(&mut tx, to, &data).unwrap();
468 }
469
470 #[test]
471 fn test_unmarshal_deposit_version0() {
472 let mut data = vec![0u8; 192];
473 let offset: [u8; 8] = U64::from(32).to_be_bytes();
474 data[24..32].copy_from_slice(&offset);
475 let len: [u8; 8] = U64::from(128).to_be_bytes();
476 data[56..64].copy_from_slice(&len);
477 let mint: [u8; 16] = 10_u128.to_be_bytes();
479 data[80..96].copy_from_slice(&mint);
480 let value: [u8; 32] = U256::from(100).to_be_bytes();
482 data[96..128].copy_from_slice(&value);
483 let gas: [u8; 8] = 1000_u64.to_be_bytes();
485 data[128..136].copy_from_slice(&gas);
486 data[136] = 1;
488 let mut tx = TxDeposit {
489 from: address!("1111111111111111111111111111111111111111"),
490 to: TxKind::Call(address!("2222222222222222222222222222222222222222")),
491 value: U256::from(100),
492 gas_limit: 1000,
493 mint: Some(10),
494 ..Default::default()
495 };
496 let to = address!("5555555555555555555555555555555555555555");
497 unmarshal_deposit_version0(&mut tx, to, &data).unwrap();
498 assert_eq!(tx.to, TxKind::Call(address!("5555555555555555555555555555555555555555")));
499 }
500}