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