1use crate::{
2 transaction::{
3 RlpEcdsaDecodableTx, RlpEcdsaEncodableTx, SignableTransaction, TxHashRef, TxHashable,
4 },
5 Transaction,
6};
7use alloy_eips::{
8 eip2718::{Eip2718Error, Eip2718Result},
9 eip2930::AccessList,
10 eip7702::SignedAuthorization,
11 Decodable2718, Encodable2718, Typed2718,
12};
13use alloy_primitives::{Bytes, Sealed, Signature, TxKind, B256, U256};
14use alloy_rlp::BufMut;
15use core::{
16 fmt::Debug,
17 hash::{Hash, Hasher},
18};
19#[cfg(not(feature = "std"))]
20use once_cell::race::OnceBox as OnceLock;
21#[cfg(feature = "std")]
22use std::sync::OnceLock;
23
24#[derive(Debug, Clone)]
29pub struct Signed<T, Sig = Signature> {
30 #[doc(alias = "transaction")]
31 tx: T,
32 signature: Sig,
33 #[doc(alias = "tx_hash", alias = "transaction_hash")]
34 hash: OnceLock<B256>,
35}
36
37impl<T, Sig> Signed<T, Sig> {
38 pub fn new_unchecked(tx: T, signature: Sig, hash: B256) -> Self {
42 let value = OnceLock::new();
43 #[allow(clippy::useless_conversion)]
44 value.get_or_init(|| hash.into());
45 Self { tx, signature, hash: value }
46 }
47
48 pub const fn new_unhashed(tx: T, signature: Sig) -> Self {
50 Self { tx, signature, hash: OnceLock::new() }
51 }
52
53 #[doc(alias = "transaction")]
55 pub const fn tx(&self) -> &T {
56 &self.tx
57 }
58
59 #[doc(hidden)]
66 pub const fn tx_mut(&mut self) -> &mut T {
67 &mut self.tx
68 }
69
70 pub const fn signature(&self) -> &Sig {
72 &self.signature
73 }
74
75 pub fn strip_signature(self) -> T {
77 self.tx
78 }
79
80 pub fn convert<U>(self) -> Signed<U, Sig>
85 where
86 U: From<T>,
87 {
88 self.map(U::from)
89 }
90
91 pub fn try_convert<U>(self) -> Result<Signed<U, Sig>, U::Error>
98 where
99 U: TryFrom<T>,
100 {
101 self.try_map(U::try_from)
102 }
103
104 pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Signed<Tx, Sig> {
109 let Self { tx, signature, hash } = self;
110 Signed { tx: f(tx), signature, hash }
111 }
112
113 pub fn try_map<Tx, E>(self, f: impl FnOnce(T) -> Result<Tx, E>) -> Result<Signed<Tx, Sig>, E> {
118 let Self { tx, signature, hash } = self;
119 Ok(Signed { tx: f(tx)?, signature, hash })
120 }
121}
122
123impl<T: SignableTransaction<Sig>, Sig> Signed<T, Sig> {
124 pub fn signature_hash(&self) -> B256 {
126 self.tx.signature_hash()
127 }
128}
129
130impl<T, Sig> Signed<T, Sig>
131where
132 T: TxHashable<Sig>,
133{
134 #[doc(alias = "tx_hash", alias = "transaction_hash")]
136 pub fn hash(&self) -> &B256 {
137 #[allow(clippy::useless_conversion)]
138 self.hash.get_or_init(|| self.tx.tx_hash(&self.signature).into())
139 }
140}
141
142impl<T> Signed<T>
143where
144 T: RlpEcdsaEncodableTx,
145{
146 pub fn into_parts(self) -> (T, Signature, B256) {
148 let hash = *self.hash();
149 (self.tx, self.signature, hash)
150 }
151
152 pub fn rlp_encoded_length(&self) -> usize {
154 self.tx.rlp_encoded_length_with_signature(&self.signature)
155 }
156
157 pub fn rlp_encode(&self, out: &mut dyn BufMut) {
159 self.tx.rlp_encode_signed(&self.signature, out);
160 }
161
162 pub fn eip2718_encoded_length(&self) -> usize {
164 self.tx.eip2718_encoded_length(&self.signature)
165 }
166
167 pub fn eip2718_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
169 self.tx.eip2718_encode_with_type(&self.signature, ty, out);
170 }
171
172 pub fn eip2718_encode(&self, out: &mut dyn BufMut) {
174 self.tx.eip2718_encode(&self.signature, out);
175 }
176
177 pub fn network_encoded_length(&self) -> usize {
179 self.tx.network_encoded_length(&self.signature)
180 }
181
182 pub fn network_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
184 self.tx.network_encode_with_type(&self.signature, ty, out);
185 }
186
187 pub fn network_encode(&self, out: &mut dyn BufMut) {
189 self.tx.network_encode(&self.signature, out);
190 }
191}
192
193impl<T, Sig> TxHashRef for Signed<T, Sig>
194where
195 T: TxHashable<Sig>,
196{
197 fn tx_hash(&self) -> &B256 {
198 self.hash()
199 }
200}
201
202impl<T> Signed<T>
203where
204 T: RlpEcdsaDecodableTx,
205{
206 pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
208 T::rlp_decode_signed(buf)
209 }
210
211 pub fn eip2718_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
213 T::eip2718_decode_with_type(buf, ty)
214 }
215
216 pub fn eip2718_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
218 T::eip2718_decode(buf)
219 }
220
221 pub fn network_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
223 T::network_decode_with_type(buf, ty)
224 }
225
226 pub fn network_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
228 T::network_decode(buf)
229 }
230}
231
232impl<T, Sig> Hash for Signed<T, Sig>
233where
234 T: TxHashable<Sig> + Hash,
235 Sig: Hash,
236{
237 fn hash<H: Hasher>(&self, state: &mut H) {
238 self.hash().hash(state);
239 self.tx.hash(state);
240 self.signature.hash(state);
241 }
242}
243
244impl<T: TxHashable<Sig> + PartialEq, Sig: PartialEq> PartialEq for Signed<T, Sig> {
245 fn eq(&self, other: &Self) -> bool {
246 self.hash() == other.hash() && self.tx == other.tx && self.signature == other.signature
247 }
248}
249
250impl<T: TxHashable<Sig> + PartialEq, Sig: PartialEq> Eq for Signed<T, Sig> {}
251
252#[cfg(feature = "k256")]
253impl<T: SignableTransaction<Signature>> Signed<T, Signature> {
254 pub fn recover_signer(
256 &self,
257 ) -> Result<alloy_primitives::Address, alloy_primitives::SignatureError> {
258 let sighash = self.tx.signature_hash();
259 self.signature.recover_address_from_prehash(&sighash)
260 }
261
262 pub fn try_into_recovered(
264 self,
265 ) -> Result<crate::transaction::Recovered<T>, alloy_primitives::SignatureError> {
266 let signer = self.recover_signer()?;
267 Ok(crate::transaction::Recovered::new_unchecked(self.tx, signer))
268 }
269
270 pub fn try_to_recovered_ref(
273 &self,
274 ) -> Result<crate::transaction::Recovered<&T>, alloy_primitives::SignatureError> {
275 let signer = self.recover_signer()?;
276 Ok(crate::transaction::Recovered::new_unchecked(&self.tx, signer))
277 }
278}
279
280#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
281impl<'a, T: SignableTransaction<Signature> + arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a>
282 for Signed<T, Signature>
283{
284 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
285 use k256::{
286 ecdsa::{signature::hazmat::PrehashSigner, SigningKey},
287 NonZeroScalar,
288 };
289 use rand::{rngs::StdRng, SeedableRng};
290
291 let rng_seed = u.arbitrary::<[u8; 32]>()?;
292 let mut rand_gen = StdRng::from_seed(rng_seed);
293 let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into();
294
295 let tx = T::arbitrary(u)?;
296
297 let (recoverable_sig, recovery_id) =
298 signing_key.sign_prehash(tx.signature_hash().as_ref()).unwrap();
299 let signature: Signature = (recoverable_sig, recovery_id).into();
300
301 Ok(tx.into_signed(signature))
302 }
303}
304
305impl<T, Sig> Typed2718 for Signed<T, Sig>
306where
307 T: Typed2718,
308{
309 fn ty(&self) -> u8 {
310 self.tx().ty()
311 }
312}
313
314impl<T: Transaction, Sig: Debug + Send + Sync + 'static> Transaction for Signed<T, Sig> {
315 #[inline]
316 fn chain_id(&self) -> Option<u64> {
317 self.tx.chain_id()
318 }
319
320 #[inline]
321 fn nonce(&self) -> u64 {
322 self.tx.nonce()
323 }
324
325 #[inline]
326 fn gas_limit(&self) -> u64 {
327 self.tx.gas_limit()
328 }
329
330 #[inline]
331 fn gas_price(&self) -> Option<u128> {
332 self.tx.gas_price()
333 }
334
335 #[inline]
336 fn max_fee_per_gas(&self) -> u128 {
337 self.tx.max_fee_per_gas()
338 }
339
340 #[inline]
341 fn max_priority_fee_per_gas(&self) -> Option<u128> {
342 self.tx.max_priority_fee_per_gas()
343 }
344
345 #[inline]
346 fn max_fee_per_blob_gas(&self) -> Option<u128> {
347 self.tx.max_fee_per_blob_gas()
348 }
349
350 #[inline]
351 fn priority_fee_or_price(&self) -> u128 {
352 self.tx.priority_fee_or_price()
353 }
354
355 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
356 self.tx.effective_gas_price(base_fee)
357 }
358
359 #[inline]
360 fn is_dynamic_fee(&self) -> bool {
361 self.tx.is_dynamic_fee()
362 }
363
364 #[inline]
365 fn kind(&self) -> TxKind {
366 self.tx.kind()
367 }
368
369 #[inline]
370 fn is_create(&self) -> bool {
371 self.tx.is_create()
372 }
373
374 #[inline]
375 fn value(&self) -> U256 {
376 self.tx.value()
377 }
378
379 #[inline]
380 fn input(&self) -> &Bytes {
381 self.tx.input()
382 }
383
384 #[inline]
385 fn access_list(&self) -> Option<&AccessList> {
386 self.tx.access_list()
387 }
388
389 #[inline]
390 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
391 self.tx.blob_versioned_hashes()
392 }
393
394 #[inline]
395 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
396 self.tx.authorization_list()
397 }
398}
399
400impl<T: Transaction> Transaction for Sealed<T> {
401 #[inline]
402 fn chain_id(&self) -> Option<u64> {
403 self.inner().chain_id()
404 }
405
406 #[inline]
407 fn nonce(&self) -> u64 {
408 self.inner().nonce()
409 }
410
411 #[inline]
412 fn gas_limit(&self) -> u64 {
413 self.inner().gas_limit()
414 }
415
416 #[inline]
417 fn gas_price(&self) -> Option<u128> {
418 self.inner().gas_price()
419 }
420
421 #[inline]
422 fn max_fee_per_gas(&self) -> u128 {
423 self.inner().max_fee_per_gas()
424 }
425
426 #[inline]
427 fn max_priority_fee_per_gas(&self) -> Option<u128> {
428 self.inner().max_priority_fee_per_gas()
429 }
430
431 #[inline]
432 fn max_fee_per_blob_gas(&self) -> Option<u128> {
433 self.inner().max_fee_per_blob_gas()
434 }
435
436 #[inline]
437 fn priority_fee_or_price(&self) -> u128 {
438 self.inner().priority_fee_or_price()
439 }
440
441 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
442 self.inner().effective_gas_price(base_fee)
443 }
444
445 #[inline]
446 fn is_dynamic_fee(&self) -> bool {
447 self.inner().is_dynamic_fee()
448 }
449
450 #[inline]
451 fn kind(&self) -> TxKind {
452 self.inner().kind()
453 }
454
455 #[inline]
456 fn is_create(&self) -> bool {
457 self.inner().is_create()
458 }
459
460 #[inline]
461 fn value(&self) -> U256 {
462 self.inner().value()
463 }
464
465 #[inline]
466 fn input(&self) -> &Bytes {
467 self.inner().input()
468 }
469
470 #[inline]
471 fn access_list(&self) -> Option<&AccessList> {
472 self.inner().access_list()
473 }
474
475 #[inline]
476 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
477 self.inner().blob_versioned_hashes()
478 }
479
480 #[inline]
481 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
482 self.inner().authorization_list()
483 }
484}
485
486#[cfg(any(feature = "secp256k1", feature = "k256"))]
487impl<T> crate::transaction::SignerRecoverable for Signed<T>
488where
489 T: SignableTransaction<Signature>,
490{
491 fn recover_signer(&self) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
492 let signature_hash = self.signature_hash();
493 crate::crypto::secp256k1::recover_signer(self.signature(), signature_hash)
494 }
495
496 fn recover_signer_unchecked(
497 &self,
498 ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
499 let signature_hash = self.signature_hash();
500 crate::crypto::secp256k1::recover_signer_unchecked(self.signature(), signature_hash)
501 }
502
503 fn recover_with_buf(
504 &self,
505 buf: &mut alloc::vec::Vec<u8>,
506 ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
507 buf.clear();
508 self.tx.encode_for_signing(buf);
509 let signature_hash = alloy_primitives::keccak256(buf);
510 crate::crypto::secp256k1::recover_signer(self.signature(), signature_hash)
511 }
512
513 fn recover_unchecked_with_buf(
514 &self,
515 buf: &mut alloc::vec::Vec<u8>,
516 ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
517 buf.clear();
518 self.tx.encode_for_signing(buf);
519 let signature_hash = alloy_primitives::keccak256(buf);
520 crate::crypto::secp256k1::recover_signer_unchecked(self.signature(), signature_hash)
521 }
522}
523
524impl<T> Encodable2718 for Signed<T>
525where
526 T: RlpEcdsaEncodableTx + Typed2718 + Send + Sync,
527{
528 fn encode_2718_len(&self) -> usize {
529 self.eip2718_encoded_length()
530 }
531
532 fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
533 self.eip2718_encode(out)
534 }
535
536 fn trie_hash(&self) -> B256 {
537 *self.hash()
538 }
539}
540
541impl<T> Decodable2718 for Signed<T>
542where
543 T: RlpEcdsaDecodableTx + Typed2718 + Send + Sync,
544{
545 fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
546 let decoded = T::rlp_decode_signed(buf)?;
547
548 if decoded.ty() != ty {
549 return Err(Eip2718Error::UnexpectedType(ty));
550 }
551
552 Ok(decoded)
553 }
554
555 fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
556 let decoded = T::rlp_decode_signed(buf)?;
557
558 if decoded.ty() != 0 {
559 return Err(Eip2718Error::UnexpectedType(0));
560 }
561
562 Ok(decoded)
563 }
564}
565
566#[cfg(feature = "serde")]
567mod serde {
568 use crate::transaction::TxHashable;
569 use alloc::borrow::Cow;
570 use alloy_primitives::B256;
571 use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
572
573 #[derive(Serialize, Deserialize)]
574 struct Signed<'a, T: Clone, Sig: Clone> {
575 #[serde(flatten)]
576 tx: Cow<'a, T>,
577 #[serde(flatten)]
578 signature: Cow<'a, Sig>,
579 hash: Cow<'a, B256>,
580 }
581
582 impl<T, Sig> Serialize for super::Signed<T, Sig>
583 where
584 T: Clone + TxHashable<Sig> + Serialize,
585 Sig: Clone + Serialize,
586 {
587 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
588 where
589 S: Serializer,
590 {
591 Signed {
592 tx: Cow::Borrowed(&self.tx),
593 signature: Cow::Borrowed(&self.signature),
594 hash: Cow::Borrowed(self.hash()),
595 }
596 .serialize(serializer)
597 }
598 }
599
600 impl<'de, T, Sig> Deserialize<'de> for super::Signed<T, Sig>
601 where
602 T: Clone + DeserializeOwned,
603 Sig: Clone + DeserializeOwned,
604 {
605 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
606 where
607 D: Deserializer<'de>,
608 {
609 Signed::<T, Sig>::deserialize(deserializer).map(|value| {
610 Self::new_unchecked(
611 value.tx.into_owned(),
612 value.signature.into_owned(),
613 value.hash.into_owned(),
614 )
615 })
616 }
617 }
618}