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