1use super::*;
21use alloc::vec::Vec;
22use rlp::{Decodable, Encodable};
23
24impl TransactionUnsigned {
25 pub fn unsigned_payload(&self) -> Vec<u8> {
27 use TransactionUnsigned::*;
28 let mut s = rlp::RlpStream::new();
29 match self {
30 Transaction7702Unsigned(ref tx) => {
31 s.append(&tx.r#type.value());
32 s.append(tx);
33 },
34 Transaction2930Unsigned(ref tx) => {
35 s.append(&tx.r#type.value());
36 s.append(tx);
37 },
38 Transaction1559Unsigned(ref tx) => {
39 s.append(&tx.r#type.value());
40 s.append(tx);
41 },
42 Transaction4844Unsigned(ref tx) => {
43 s.append(&tx.r#type.value());
44 s.append(tx);
45 },
46 TransactionLegacyUnsigned(ref tx) => {
47 s.append(tx);
48 },
49 }
50
51 s.out().to_vec()
52 }
53}
54
55impl TransactionSigned {
56 pub fn unsigned(self) -> TransactionUnsigned {
58 use TransactionSigned::*;
59 use TransactionUnsigned::*;
60 match self {
61 Transaction7702Signed(tx) => Transaction7702Unsigned(tx.transaction_7702_unsigned),
62 Transaction2930Signed(tx) => Transaction2930Unsigned(tx.transaction_2930_unsigned),
63 Transaction1559Signed(tx) => Transaction1559Unsigned(tx.transaction_1559_unsigned),
64 Transaction4844Signed(tx) => Transaction4844Unsigned(tx.transaction_4844_unsigned),
65 TransactionLegacySigned(tx) =>
66 TransactionLegacyUnsigned(tx.transaction_legacy_unsigned),
67 }
68 }
69
70 pub fn signed_payload(&self) -> Vec<u8> {
72 use TransactionSigned::*;
73 let mut s = rlp::RlpStream::new();
74 match self {
75 Transaction7702Signed(ref tx) => {
76 s.append(&tx.transaction_7702_unsigned.r#type.value());
77 s.append(tx);
78 },
79 Transaction2930Signed(ref tx) => {
80 s.append(&tx.transaction_2930_unsigned.r#type.value());
81 s.append(tx);
82 },
83 Transaction1559Signed(ref tx) => {
84 s.append(&tx.transaction_1559_unsigned.r#type.value());
85 s.append(tx);
86 },
87 Transaction4844Signed(ref tx) => {
88 s.append(&tx.transaction_4844_unsigned.r#type.value());
89 s.append(tx);
90 },
91 TransactionLegacySigned(ref tx) => {
92 s.append(tx);
93 },
94 }
95
96 s.out().to_vec()
97 }
98
99 pub fn decode(data: &[u8]) -> Result<Self, rlp::DecoderError> {
101 if data.is_empty() {
102 return Err(rlp::DecoderError::RlpIsTooShort);
103 }
104 let first_byte = data[0];
105
106 if first_byte <= 0x7f {
108 match first_byte {
109 TYPE_EIP2930 => rlp::decode::<Transaction2930Signed>(&data[1..]).map(Into::into),
110 TYPE_EIP1559 => rlp::decode::<Transaction1559Signed>(&data[1..]).map(Into::into),
111 TYPE_EIP4844 => rlp::decode::<Transaction4844Signed>(&data[1..]).map(Into::into),
112 TYPE_EIP7702 => rlp::decode::<Transaction7702Signed>(&data[1..]).map(Into::into),
113 _ => Err(rlp::DecoderError::Custom("Unknown transaction type")),
114 }
115 } else {
116 rlp::decode::<TransactionLegacySigned>(data).map(Into::into)
117 }
118 }
119}
120
121impl TransactionUnsigned {
122 pub fn dummy_signed_payload(self) -> Vec<u8> {
124 const DUMMY_SIGNATURE: [u8; 65] = [1u8; 65];
125 self.with_signature(DUMMY_SIGNATURE).signed_payload()
126 }
127}
128
129impl Encodable for TransactionLegacyUnsigned {
131 fn rlp_append(&self, s: &mut rlp::RlpStream) {
132 if let Some(chain_id) = self.chain_id {
133 s.begin_list(9);
134 s.append(&self.nonce);
135 s.append(&self.gas_price);
136 s.append(&self.gas);
137 match self.to {
138 Some(ref to) => s.append(to),
139 None => s.append_empty_data(),
140 };
141 s.append(&self.value);
142 s.append(&self.input.0);
143 s.append(&chain_id);
144 s.append(&0u8);
145 s.append(&0u8);
146 } else {
147 s.begin_list(6);
148 s.append(&self.nonce);
149 s.append(&self.gas_price);
150 s.append(&self.gas);
151 match self.to {
152 Some(ref to) => s.append(to),
153 None => s.append_empty_data(),
154 };
155 s.append(&self.value);
156 s.append(&self.input.0);
157 }
158 }
159}
160
161impl Decodable for TransactionLegacyUnsigned {
162 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
163 Ok(TransactionLegacyUnsigned {
164 nonce: rlp.val_at(0)?,
165 gas_price: rlp.val_at(1)?,
166 gas: rlp.val_at(2)?,
167 to: {
168 let to = rlp.at(3)?;
169 if to.is_empty() {
170 None
171 } else {
172 Some(to.as_val()?)
173 }
174 },
175 value: rlp.val_at(4)?,
176 input: Bytes(rlp.val_at(5)?),
177 chain_id: rlp.val_at(6).ok(),
178 ..Default::default()
179 })
180 }
181}
182
183impl Encodable for TransactionLegacySigned {
184 fn rlp_append(&self, s: &mut rlp::RlpStream) {
185 let tx = &self.transaction_legacy_unsigned;
186
187 s.begin_list(9);
188 s.append(&tx.nonce);
189 s.append(&tx.gas_price);
190 s.append(&tx.gas);
191 match tx.to {
192 Some(ref to) => s.append(to),
193 None => s.append_empty_data(),
194 };
195 s.append(&tx.value);
196 s.append(&tx.input.0);
197
198 s.append(&self.v);
199 s.append(&self.r);
200 s.append(&self.s);
201 }
202}
203
204impl Encodable for AccessListEntry {
205 fn rlp_append(&self, s: &mut rlp::RlpStream) {
206 s.begin_list(2);
207 s.append(&self.address);
208 s.append_list(&self.storage_keys);
209 }
210}
211
212impl Decodable for AccessListEntry {
213 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
214 Ok(AccessListEntry { address: rlp.val_at(0)?, storage_keys: rlp.list_at(1)? })
215 }
216}
217
218impl Encodable for AuthorizationListEntry {
219 fn rlp_append(&self, s: &mut rlp::RlpStream) {
220 s.begin_list(6);
221 s.append(&self.chain_id);
222 s.append(&self.address);
223 s.append(&self.nonce);
224 s.append(&self.y_parity);
225 s.append(&self.r);
226 s.append(&self.s);
227 }
228}
229
230impl Decodable for AuthorizationListEntry {
231 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
232 Ok(AuthorizationListEntry {
233 chain_id: rlp.val_at(0)?,
234 address: rlp.val_at(1)?,
235 nonce: rlp.val_at(2)?,
236 y_parity: rlp.val_at(3)?,
237 r: rlp.val_at(4)?,
238 s: rlp.val_at(5)?,
239 })
240 }
241}
242
243impl Encodable for Transaction1559Unsigned {
245 fn rlp_append(&self, s: &mut rlp::RlpStream) {
246 s.begin_list(9);
247 s.append(&self.chain_id);
248 s.append(&self.nonce);
249 s.append(&self.max_priority_fee_per_gas);
250 s.append(&self.max_fee_per_gas);
251 s.append(&self.gas);
252 match self.to {
253 Some(ref to) => s.append(to),
254 None => s.append_empty_data(),
255 };
256 s.append(&self.value);
257 s.append(&self.input.0);
258 s.append_list(&self.access_list);
259 }
260}
261
262impl Encodable for Transaction1559Signed {
264 fn rlp_append(&self, s: &mut rlp::RlpStream) {
265 let tx = &self.transaction_1559_unsigned;
266 s.begin_list(12);
267 s.append(&tx.chain_id);
268 s.append(&tx.nonce);
269 s.append(&tx.max_priority_fee_per_gas);
270 s.append(&tx.max_fee_per_gas);
271 s.append(&tx.gas);
272 match tx.to {
273 Some(ref to) => s.append(to),
274 None => s.append_empty_data(),
275 };
276 s.append(&tx.value);
277 s.append(&tx.input.0);
278 s.append_list(&tx.access_list);
279
280 s.append(&self.y_parity);
281 s.append(&self.r);
282 s.append(&self.s);
283 }
284}
285
286impl Decodable for Transaction1559Signed {
287 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
288 Ok(Transaction1559Signed {
289 transaction_1559_unsigned: {
290 Transaction1559Unsigned {
291 chain_id: rlp.val_at(0)?,
292 nonce: rlp.val_at(1)?,
293 max_priority_fee_per_gas: rlp.val_at(2)?,
294 max_fee_per_gas: rlp.val_at(3)?,
295 gas: rlp.val_at(4)?,
296 to: {
297 let to = rlp.at(5)?;
298 if to.is_empty() {
299 None
300 } else {
301 Some(to.as_val()?)
302 }
303 },
304 value: rlp.val_at(6)?,
305 input: Bytes(rlp.val_at(7)?),
306 access_list: rlp.list_at(8)?,
307 ..Default::default()
308 }
309 },
310 y_parity: rlp.val_at(9)?,
311 r: rlp.val_at(10)?,
312 s: rlp.val_at(11)?,
313 ..Default::default()
314 })
315 }
316}
317
318impl Encodable for Transaction2930Unsigned {
320 fn rlp_append(&self, s: &mut rlp::RlpStream) {
321 s.begin_list(8);
322 s.append(&self.chain_id);
323 s.append(&self.nonce);
324 s.append(&self.gas_price);
325 s.append(&self.gas);
326 match self.to {
327 Some(ref to) => s.append(to),
328 None => s.append_empty_data(),
329 };
330 s.append(&self.value);
331 s.append(&self.input.0);
332 s.append_list(&self.access_list);
333 }
334}
335
336impl Encodable for Transaction2930Signed {
338 fn rlp_append(&self, s: &mut rlp::RlpStream) {
339 let tx = &self.transaction_2930_unsigned;
340 s.begin_list(11);
341 s.append(&tx.chain_id);
342 s.append(&tx.nonce);
343 s.append(&tx.gas_price);
344 s.append(&tx.gas);
345 match tx.to {
346 Some(ref to) => s.append(to),
347 None => s.append_empty_data(),
348 };
349 s.append(&tx.value);
350 s.append(&tx.input.0);
351 s.append_list(&tx.access_list);
352 s.append(&self.y_parity);
353 s.append(&self.r);
354 s.append(&self.s);
355 }
356}
357
358impl Decodable for Transaction2930Signed {
359 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
360 Ok(Transaction2930Signed {
361 transaction_2930_unsigned: {
362 Transaction2930Unsigned {
363 chain_id: rlp.val_at(0)?,
364 nonce: rlp.val_at(1)?,
365 gas_price: rlp.val_at(2)?,
366 gas: rlp.val_at(3)?,
367 to: {
368 let to = rlp.at(4)?;
369 if to.is_empty() {
370 None
371 } else {
372 Some(to.as_val()?)
373 }
374 },
375 value: rlp.val_at(5)?,
376 input: Bytes(rlp.val_at(6)?),
377 access_list: rlp.list_at(7)?,
378 ..Default::default()
379 }
380 },
381 y_parity: rlp.val_at(8)?,
382 r: rlp.val_at(9)?,
383 s: rlp.val_at(10)?,
384 ..Default::default()
385 })
386 }
387}
388
389impl Encodable for Transaction7702Unsigned {
391 fn rlp_append(&self, s: &mut rlp::RlpStream) {
392 s.begin_list(10);
393 s.append(&self.chain_id);
394 s.append(&self.nonce);
395 s.append(&self.max_priority_fee_per_gas);
396 s.append(&self.max_fee_per_gas);
397 s.append(&self.gas);
398 s.append(&self.to);
399 s.append(&self.value);
400 s.append(&self.input.0);
401 s.append_list(&self.access_list);
402 s.append_list(&self.authorization_list);
403 }
404}
405
406impl Decodable for Transaction7702Signed {
407 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
408 Ok(Transaction7702Signed {
409 transaction_7702_unsigned: {
410 Transaction7702Unsigned {
411 chain_id: rlp.val_at(0)?,
412 nonce: rlp.val_at(1)?,
413 max_priority_fee_per_gas: rlp.val_at(2)?,
414 max_fee_per_gas: rlp.val_at(3)?,
415 gas_price: rlp.val_at(4)?,
416 gas: rlp.val_at(5)?,
417 to: rlp.val_at(6)?,
418 value: rlp.val_at(7)?,
419 input: Bytes(rlp.val_at(8)?),
420 access_list: rlp.list_at(9)?,
421 authorization_list: rlp.list_at(10)?,
422 r#type: Default::default(),
423 }
424 },
425 y_parity: rlp.val_at(11)?,
426 r: rlp.val_at(12)?,
427 s: rlp.val_at(13)?,
428 v: None,
429 })
430 }
431}
432
433impl Encodable for Transaction4844Unsigned {
434 fn rlp_append(&self, s: &mut rlp::RlpStream) {
435 s.begin_list(11);
436 s.append(&self.chain_id);
437 s.append(&self.nonce);
438 s.append(&self.max_priority_fee_per_gas);
439 s.append(&self.max_fee_per_gas);
440 s.append(&self.gas);
441 s.append(&self.to);
442 s.append(&self.value);
443 s.append(&self.input.0);
444 s.append_list(&self.access_list);
445 s.append(&self.max_fee_per_blob_gas);
446 s.append_list(&self.blob_versioned_hashes);
447 }
448}
449
450impl Encodable for Transaction7702Signed {
452 fn rlp_append(&self, s: &mut rlp::RlpStream) {
453 let tx = &self.transaction_7702_unsigned;
454 s.begin_list(13);
455 s.append(&tx.chain_id);
456 s.append(&tx.nonce);
457 s.append(&tx.max_priority_fee_per_gas);
458 s.append(&tx.max_fee_per_gas);
459 s.append(&tx.gas);
460 s.append(&tx.to);
461 s.append(&tx.value);
462 s.append(&tx.input.0);
463 s.append_list(&tx.access_list);
464 s.append_list(&tx.authorization_list);
465 s.append(&self.y_parity);
466 s.append(&self.r);
467 s.append(&self.s);
468 }
469}
470
471impl Encodable for Transaction4844Signed {
473 fn rlp_append(&self, s: &mut rlp::RlpStream) {
474 let tx = &self.transaction_4844_unsigned;
475 s.begin_list(14);
476 s.append(&tx.chain_id);
477 s.append(&tx.nonce);
478 s.append(&tx.max_priority_fee_per_gas);
479 s.append(&tx.max_fee_per_gas);
480 s.append(&tx.gas);
481 s.append(&tx.to);
482 s.append(&tx.value);
483 s.append(&tx.input.0);
484 s.append_list(&tx.access_list);
485 s.append(&tx.max_fee_per_blob_gas);
486 s.append_list(&tx.blob_versioned_hashes);
487 s.append(&self.y_parity);
488 s.append(&self.r);
489 s.append(&self.s);
490 }
491}
492
493impl Decodable for Transaction4844Signed {
494 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
495 Ok(Transaction4844Signed {
496 transaction_4844_unsigned: {
497 Transaction4844Unsigned {
498 chain_id: rlp.val_at(0)?,
499 nonce: rlp.val_at(1)?,
500 max_priority_fee_per_gas: rlp.val_at(2)?,
501 max_fee_per_gas: rlp.val_at(3)?,
502 gas: rlp.val_at(4)?,
503 to: rlp.val_at(5)?,
504 value: rlp.val_at(6)?,
505 input: Bytes(rlp.val_at(7)?),
506 access_list: rlp.list_at(8)?,
507 max_fee_per_blob_gas: rlp.val_at(9)?,
508 blob_versioned_hashes: rlp.list_at(10)?,
509 ..Default::default()
510 }
511 },
512 y_parity: rlp.val_at(11)?,
513 r: rlp.val_at(12)?,
514 s: rlp.val_at(13)?,
515 })
516 }
517}
518
519impl Decodable for TransactionLegacySigned {
521 fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
522 let v: U256 = rlp.val_at(6)?;
523
524 let extract_chain_id = |v: U256| {
525 if v.ge(&35u32.into()) {
526 Some((v - 35) / 2)
527 } else {
528 None
529 }
530 };
531
532 Ok(TransactionLegacySigned {
533 transaction_legacy_unsigned: {
534 TransactionLegacyUnsigned {
535 nonce: rlp.val_at(0)?,
536 gas_price: rlp.val_at(1)?,
537 gas: rlp.val_at(2)?,
538 to: {
539 let to = rlp.at(3)?;
540 if to.is_empty() {
541 None
542 } else {
543 Some(to.as_val()?)
544 }
545 },
546 value: rlp.val_at(4)?,
547 input: Bytes(rlp.val_at(5)?),
548 chain_id: extract_chain_id(v).map(|v| v.into()),
549 r#type: TypeLegacy {},
550 }
551 },
552 v,
553 r: rlp.val_at(7)?,
554 s: rlp.val_at(8)?,
555 })
556 }
557}
558
559#[cfg(test)]
560mod test {
561 use super::*;
562
563 #[test]
564 fn encode_decode_tx_works() {
565 let txs = [
566 (
568 "f86080808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d87808025a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
569 r#"
570 {
571 "chainId": "0x1",
572 "gas": "0x1e241",
573 "gasPrice": "0x0",
574 "input": "0x",
575 "nonce": "0x0",
576 "to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
577 "type": "0x0",
578 "value": "0x0",
579 "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
580 "s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
581 "v": "0x25"
582 }
583 "#
584 ),
585 (
587 "01f89b0180808301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
588 r#"
589 {
590 "accessList": [
591 {
592 "address": "0x0000000000000000000000000000000000000001",
593 "storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
594 }
595 ],
596 "chainId": "0x1",
597 "gas": "0x1e241",
598 "gasPrice": "0x0",
599 "input": "0x",
600 "nonce": "0x0",
601 "to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
602 "type": "0x1",
603 "value": "0x0",
604 "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
605 "s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
606 "yParity": "0x0"
607 }
608 "#
609 ),
610 (
612 "02f89c018080018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
613 r#"
614 {
615 "accessList": [
616 {
617 "address": "0x0000000000000000000000000000000000000001",
618 "storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
619 }
620 ],
621 "chainId": "0x1",
622 "gas": "0x1e241",
623 "gasPrice": "0x0",
624 "input": "0x",
625 "maxFeePerGas": "0x1",
626 "maxPriorityFeePerGas": "0x0",
627 "nonce": "0x0",
628 "to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
629 "type": "0x2",
630 "value": "0x0",
631 "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
632 "s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
633 "yParity": "0x0"
634
635 }
636 "#
637 ),
638 (
640
641 "03f8bf018002018301e24194095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f7940000000000000000000000000000000000000001e1a0000000000000000000000000000000000000000000000000000000000000000080e1a0000000000000000000000000000000000000000000000000000000000000000080a0fe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0a06de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
642 r#"
643 {
644 "accessList": [
645 {
646 "address": "0x0000000000000000000000000000000000000001",
647 "storageKeys": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
648 }
649 ],
650 "blobVersionedHashes": ["0x0000000000000000000000000000000000000000000000000000000000000000"],
651 "chainId": "0x1",
652 "gas": "0x1e241",
653 "input": "0x",
654 "maxFeePerBlobGas": "0x0",
655 "maxFeePerGas": "0x1",
656 "maxPriorityFeePerGas": "0x2",
657 "nonce": "0x0",
658 "to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
659 "type": "0x3",
660 "value": "0x0",
661 "r": "0xfe38ca4e44a30002ac54af7cf922a6ac2ba11b7d22f548e8ecb3f51f41cb31b0",
662 "s": "0x6de6a5cbae13c0c856e33acf021b51819636cfc009d39eafb9f606d546e305a8",
663 "yParity": "0x0"
664 }
665 "#
666 )
667 ];
668
669 for (tx, json) in txs {
670 let raw_tx = alloy_core::hex::decode(tx).unwrap();
671 let tx = TransactionSigned::decode(&raw_tx).unwrap();
672 assert_eq!(tx.signed_payload(), raw_tx);
673 let expected_tx = serde_json::from_str(json).unwrap();
674 assert_eq!(tx, expected_tx);
675 }
676 }
677
678 #[test]
679 fn dummy_signed_payload_works() {
680 let tx: TransactionUnsigned = TransactionLegacyUnsigned {
681 chain_id: Some(596.into()),
682 gas: U256::from(21000),
683 nonce: U256::from(1),
684 gas_price: U256::from("0x640000006a"),
685 to: Some(Account::from(subxt_signer::eth::dev::baltathar()).address()),
686 value: U256::from(123123),
687 input: Bytes(vec![]),
688 r#type: TypeLegacy,
689 }
690 .into();
691
692 let dummy_signed_payload = tx.clone().dummy_signed_payload();
693 let payload = Account::default().sign_transaction(tx).signed_payload();
694 assert_eq!(dummy_signed_payload.len(), payload.len());
695 }
696}