1use crate::{
2    transaction::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx, SignableTransaction},
3    Transaction,
4};
5use alloy_eips::{
6    eip2718::Eip2718Result, eip2930::AccessList, eip7702::SignedAuthorization, Typed2718,
7};
8use alloy_primitives::{Bytes, Sealed, Signature, TxKind, B256, U256};
9use alloy_rlp::BufMut;
10use core::hash::{Hash, Hasher};
11#[cfg(not(feature = "std"))]
12use once_cell::race::OnceBox as OnceLock;
13#[cfg(feature = "std")]
14use std::sync::OnceLock;
15
16#[derive(Debug, Clone)]
18pub struct Signed<T, Sig = Signature> {
19    #[doc(alias = "transaction")]
20    tx: T,
21    signature: Sig,
22    #[doc(alias = "tx_hash", alias = "transaction_hash")]
23    hash: OnceLock<B256>,
24}
25
26impl<T, Sig> Signed<T, Sig> {
27    pub fn new_unchecked(tx: T, signature: Sig, hash: B256) -> Self {
29        let value = OnceLock::new();
30        #[allow(clippy::useless_conversion)]
31        value.get_or_init(|| hash.into());
32        Self { tx, signature, hash: value }
33    }
34
35    pub const fn new_unhashed(tx: T, signature: Sig) -> Self {
37        Self { tx, signature, hash: OnceLock::new() }
38    }
39
40    #[doc(alias = "transaction")]
42    pub const fn tx(&self) -> &T {
43        &self.tx
44    }
45
46    pub const fn tx_mut(&mut self) -> &mut T {
48        &mut self.tx
49    }
50
51    pub const fn signature(&self) -> &Sig {
53        &self.signature
54    }
55
56    pub fn strip_signature(self) -> T {
58        self.tx
59    }
60
61    pub fn convert<U>(self) -> Signed<U, Sig>
66    where
67        U: From<T>,
68    {
69        self.map(U::from)
70    }
71
72    pub fn try_convert<U>(self) -> Result<Signed<U, Sig>, U::Error>
79    where
80        U: TryFrom<T>,
81    {
82        self.try_map(U::try_from)
83    }
84
85    pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Signed<Tx, Sig> {
90        let Self { tx, signature, hash } = self;
91        Signed { tx: f(tx), signature, hash }
92    }
93
94    pub fn try_map<Tx, E>(self, f: impl FnOnce(T) -> Result<Tx, E>) -> Result<Signed<Tx, Sig>, E> {
99        let Self { tx, signature, hash } = self;
100        Ok(Signed { tx: f(tx)?, signature, hash })
101    }
102}
103
104impl<T: SignableTransaction<Sig>, Sig> Signed<T, Sig> {
105    pub fn signature_hash(&self) -> B256 {
107        self.tx.signature_hash()
108    }
109}
110
111impl<T> Signed<T>
112where
113    T: RlpEcdsaEncodableTx,
114{
115    #[doc(alias = "tx_hash", alias = "transaction_hash")]
117    pub fn hash(&self) -> &B256 {
118        #[allow(clippy::useless_conversion)]
119        self.hash.get_or_init(|| self.tx.tx_hash(&self.signature).into())
120    }
121
122    pub fn into_parts(self) -> (T, Signature, B256) {
124        let hash = *self.hash();
125        (self.tx, self.signature, hash)
126    }
127
128    pub fn rlp_encoded_length(&self) -> usize {
130        self.tx.rlp_encoded_length_with_signature(&self.signature)
131    }
132
133    pub fn rlp_encode(&self, out: &mut dyn BufMut) {
135        self.tx.rlp_encode_signed(&self.signature, out);
136    }
137
138    pub fn eip2718_encoded_length(&self) -> usize {
140        self.tx.eip2718_encoded_length(&self.signature)
141    }
142
143    pub fn eip2718_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
145        self.tx.eip2718_encode_with_type(&self.signature, ty, out);
146    }
147
148    pub fn eip2718_encode(&self, out: &mut dyn BufMut) {
150        self.tx.eip2718_encode(&self.signature, out);
151    }
152
153    pub fn network_encoded_length(&self) -> usize {
155        self.tx.network_encoded_length(&self.signature)
156    }
157
158    pub fn network_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
160        self.tx.network_encode_with_type(&self.signature, ty, out);
161    }
162
163    pub fn network_encode(&self, out: &mut dyn BufMut) {
165        self.tx.network_encode(&self.signature, out);
166    }
167}
168
169impl<T> Signed<T>
170where
171    T: RlpEcdsaDecodableTx,
172{
173    pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
175        T::rlp_decode_signed(buf)
176    }
177
178    pub fn eip2718_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
180        T::eip2718_decode_with_type(buf, ty)
181    }
182
183    pub fn eip2718_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
185        T::eip2718_decode(buf)
186    }
187
188    pub fn network_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
190        T::network_decode_with_type(buf, ty)
191    }
192
193    pub fn network_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
195        T::network_decode(buf)
196    }
197}
198
199impl<T> Hash for Signed<T>
200where
201    T: RlpEcdsaDecodableTx + Hash,
202{
203    fn hash<H: Hasher>(&self, state: &mut H) {
204        self.hash().hash(state);
205        self.tx.hash(state);
206        self.signature.hash(state);
207    }
208}
209
210impl<T: RlpEcdsaEncodableTx + PartialEq> PartialEq for Signed<T> {
211    fn eq(&self, other: &Self) -> bool {
212        self.hash() == other.hash() && self.tx == other.tx && self.signature == other.signature
213    }
214}
215
216impl<T: RlpEcdsaEncodableTx + PartialEq> Eq for Signed<T> {}
217
218#[cfg(feature = "k256")]
219impl<T: SignableTransaction<Signature>> Signed<T, Signature> {
220    pub fn recover_signer(
222        &self,
223    ) -> Result<alloy_primitives::Address, alloy_primitives::SignatureError> {
224        let sighash = self.tx.signature_hash();
225        self.signature.recover_address_from_prehash(&sighash)
226    }
227
228    pub fn try_into_recovered(
230        self,
231    ) -> Result<crate::transaction::Recovered<T>, alloy_primitives::SignatureError> {
232        let signer = self.recover_signer()?;
233        Ok(crate::transaction::Recovered::new_unchecked(self.tx, signer))
234    }
235
236    pub fn try_to_recovered_ref(
239        &self,
240    ) -> Result<crate::transaction::Recovered<&T>, alloy_primitives::SignatureError> {
241        let signer = self.recover_signer()?;
242        Ok(crate::transaction::Recovered::new_unchecked(&self.tx, signer))
243    }
244}
245
246#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
247impl<'a, T: SignableTransaction<Signature> + arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a>
248    for Signed<T, Signature>
249{
250    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
251        use k256::{
252            ecdsa::{signature::hazmat::PrehashSigner, SigningKey},
253            NonZeroScalar,
254        };
255        use rand::{rngs::StdRng, SeedableRng};
256
257        let rng_seed = u.arbitrary::<[u8; 32]>()?;
258        let mut rand_gen = StdRng::from_seed(rng_seed);
259        let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into();
260
261        let tx = T::arbitrary(u)?;
262
263        let (recoverable_sig, recovery_id) =
264            signing_key.sign_prehash(tx.signature_hash().as_ref()).unwrap();
265        let signature: Signature = (recoverable_sig, recovery_id).into();
266
267        Ok(tx.into_signed(signature))
268    }
269}
270
271impl<T> Typed2718 for Signed<T>
272where
273    T: Typed2718,
274{
275    fn ty(&self) -> u8 {
276        self.tx().ty()
277    }
278}
279
280impl<T: Transaction> Transaction for Signed<T> {
281    #[inline]
282    fn chain_id(&self) -> Option<u64> {
283        self.tx.chain_id()
284    }
285
286    #[inline]
287    fn nonce(&self) -> u64 {
288        self.tx.nonce()
289    }
290
291    #[inline]
292    fn gas_limit(&self) -> u64 {
293        self.tx.gas_limit()
294    }
295
296    #[inline]
297    fn gas_price(&self) -> Option<u128> {
298        self.tx.gas_price()
299    }
300
301    #[inline]
302    fn max_fee_per_gas(&self) -> u128 {
303        self.tx.max_fee_per_gas()
304    }
305
306    #[inline]
307    fn max_priority_fee_per_gas(&self) -> Option<u128> {
308        self.tx.max_priority_fee_per_gas()
309    }
310
311    #[inline]
312    fn max_fee_per_blob_gas(&self) -> Option<u128> {
313        self.tx.max_fee_per_blob_gas()
314    }
315
316    #[inline]
317    fn priority_fee_or_price(&self) -> u128 {
318        self.tx.priority_fee_or_price()
319    }
320
321    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
322        self.tx.effective_gas_price(base_fee)
323    }
324
325    #[inline]
326    fn is_dynamic_fee(&self) -> bool {
327        self.tx.is_dynamic_fee()
328    }
329
330    #[inline]
331    fn kind(&self) -> TxKind {
332        self.tx.kind()
333    }
334
335    #[inline]
336    fn is_create(&self) -> bool {
337        self.tx.is_create()
338    }
339
340    #[inline]
341    fn value(&self) -> U256 {
342        self.tx.value()
343    }
344
345    #[inline]
346    fn input(&self) -> &Bytes {
347        self.tx.input()
348    }
349
350    #[inline]
351    fn access_list(&self) -> Option<&AccessList> {
352        self.tx.access_list()
353    }
354
355    #[inline]
356    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
357        self.tx.blob_versioned_hashes()
358    }
359
360    #[inline]
361    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
362        self.tx.authorization_list()
363    }
364}
365
366impl<T: Transaction> Transaction for Sealed<T> {
367    #[inline]
368    fn chain_id(&self) -> Option<u64> {
369        self.inner().chain_id()
370    }
371
372    #[inline]
373    fn nonce(&self) -> u64 {
374        self.inner().nonce()
375    }
376
377    #[inline]
378    fn gas_limit(&self) -> u64 {
379        self.inner().gas_limit()
380    }
381
382    #[inline]
383    fn gas_price(&self) -> Option<u128> {
384        self.inner().gas_price()
385    }
386
387    #[inline]
388    fn max_fee_per_gas(&self) -> u128 {
389        self.inner().max_fee_per_gas()
390    }
391
392    #[inline]
393    fn max_priority_fee_per_gas(&self) -> Option<u128> {
394        self.inner().max_priority_fee_per_gas()
395    }
396
397    #[inline]
398    fn max_fee_per_blob_gas(&self) -> Option<u128> {
399        self.inner().max_fee_per_blob_gas()
400    }
401
402    #[inline]
403    fn priority_fee_or_price(&self) -> u128 {
404        self.inner().priority_fee_or_price()
405    }
406
407    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
408        self.inner().effective_gas_price(base_fee)
409    }
410
411    #[inline]
412    fn is_dynamic_fee(&self) -> bool {
413        self.inner().is_dynamic_fee()
414    }
415
416    #[inline]
417    fn kind(&self) -> TxKind {
418        self.inner().kind()
419    }
420
421    #[inline]
422    fn is_create(&self) -> bool {
423        self.inner().is_create()
424    }
425
426    #[inline]
427    fn value(&self) -> U256 {
428        self.inner().value()
429    }
430
431    #[inline]
432    fn input(&self) -> &Bytes {
433        self.inner().input()
434    }
435
436    #[inline]
437    fn access_list(&self) -> Option<&AccessList> {
438        self.inner().access_list()
439    }
440
441    #[inline]
442    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
443        self.inner().blob_versioned_hashes()
444    }
445
446    #[inline]
447    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
448        self.inner().authorization_list()
449    }
450}
451
452#[cfg(any(feature = "secp256k1", feature = "k256"))]
453impl<T> crate::transaction::SignerRecoverable for Signed<T>
454where
455    T: SignableTransaction<Signature>,
456{
457    fn recover_signer(&self) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
458        let signature_hash = self.signature_hash();
459        crate::crypto::secp256k1::recover_signer(self.signature(), signature_hash)
460    }
461
462    fn recover_signer_unchecked(
463        &self,
464    ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
465        let signature_hash = self.signature_hash();
466        crate::crypto::secp256k1::recover_signer_unchecked(self.signature(), signature_hash)
467    }
468
469    fn recover_unchecked_with_buf(
470        &self,
471        buf: &mut alloc::vec::Vec<u8>,
472    ) -> Result<alloy_primitives::Address, crate::crypto::RecoveryError> {
473        buf.clear();
474        self.tx.encode_for_signing(buf);
475        let signature_hash = alloy_primitives::keccak256(buf);
476        crate::crypto::secp256k1::recover_signer_unchecked(self.signature(), signature_hash)
477    }
478}
479
480#[cfg(feature = "serde")]
481mod serde {
482    use crate::transaction::RlpEcdsaEncodableTx;
483    use alloc::borrow::Cow;
484    use alloy_primitives::B256;
485    use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
486
487    #[derive(Serialize, Deserialize)]
488    struct Signed<'a, T: Clone, Sig: Clone> {
489        #[serde(flatten)]
490        tx: Cow<'a, T>,
491        #[serde(flatten)]
492        signature: Cow<'a, Sig>,
493        hash: Cow<'a, B256>,
494    }
495
496    impl<T> Serialize for super::Signed<T>
497    where
498        T: Clone + RlpEcdsaEncodableTx + Serialize,
499    {
500        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
501        where
502            S: Serializer,
503        {
504            Signed {
505                tx: Cow::Borrowed(&self.tx),
506                signature: Cow::Borrowed(&self.signature),
507                hash: Cow::Borrowed(self.hash()),
508            }
509            .serialize(serializer)
510        }
511    }
512
513    impl<'de, T, Sig> Deserialize<'de> for super::Signed<T, Sig>
514    where
515        T: Clone + DeserializeOwned,
516        Sig: Clone + DeserializeOwned,
517    {
518        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
519        where
520            D: Deserializer<'de>,
521        {
522            Signed::<T, Sig>::deserialize(deserializer).map(|value| {
523                Self::new_unchecked(
524                    value.tx.into_owned(),
525                    value.signature.into_owned(),
526                    value.hash.into_owned(),
527                )
528            })
529        }
530    }
531}