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