1use crate::transaction::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx, SignableTransaction};
2use alloy_eips::eip2718::Eip2718Result;
3use alloy_primitives::{Signature, B256};
4use alloy_rlp::BufMut;
5use core::hash::{Hash, Hasher};
6#[cfg(not(feature = "std"))]
7use once_cell::race::OnceBox as OnceLock;
8#[cfg(feature = "std")]
9use std::sync::OnceLock;
10
11#[derive(Debug, Clone)]
13pub struct Signed<T, Sig = Signature> {
14 #[doc(alias = "transaction")]
15 tx: T,
16 signature: Sig,
17 #[doc(alias = "tx_hash", alias = "transaction_hash")]
18 hash: OnceLock<B256>,
19}
20
21impl<T, Sig> Signed<T, Sig> {
22 pub fn new_unchecked(tx: T, signature: Sig, hash: B256) -> Self {
24 let value = OnceLock::new();
25 #[allow(clippy::useless_conversion)]
26 value.get_or_init(|| hash.into());
27 Self { tx, signature, hash: value }
28 }
29
30 pub const fn new_unhashed(tx: T, signature: Sig) -> Self {
32 Self { tx, signature, hash: OnceLock::new() }
33 }
34
35 #[doc(alias = "transaction")]
37 pub const fn tx(&self) -> &T {
38 &self.tx
39 }
40
41 pub fn tx_mut(&mut self) -> &mut T {
43 &mut self.tx
44 }
45
46 pub const fn signature(&self) -> &Sig {
48 &self.signature
49 }
50
51 pub fn strip_signature(self) -> T {
53 self.tx
54 }
55
56 pub fn convert<U>(self) -> Signed<U, Sig>
61 where
62 U: From<T>,
63 {
64 self.map(U::from)
65 }
66
67 pub fn try_convert<U>(self) -> Result<Signed<U, Sig>, U::Error>
74 where
75 U: TryFrom<T>,
76 {
77 self.try_map(U::try_from)
78 }
79
80 pub fn map<Tx>(self, f: impl FnOnce(T) -> Tx) -> Signed<Tx, Sig> {
85 let Self { tx, signature, hash } = self;
86 Signed { tx: f(tx), signature, hash }
87 }
88
89 pub fn try_map<Tx, E>(self, f: impl FnOnce(T) -> Result<Tx, E>) -> Result<Signed<Tx, Sig>, E> {
94 let Self { tx, signature, hash } = self;
95 Ok(Signed { tx: f(tx)?, signature, hash })
96 }
97}
98
99impl<T: SignableTransaction<Sig>, Sig> Signed<T, Sig> {
100 pub fn signature_hash(&self) -> B256 {
102 self.tx.signature_hash()
103 }
104}
105
106impl<T> Signed<T>
107where
108 T: RlpEcdsaEncodableTx,
109{
110 #[doc(alias = "tx_hash", alias = "transaction_hash")]
112 pub fn hash(&self) -> &B256 {
113 #[allow(clippy::useless_conversion)]
114 self.hash.get_or_init(|| self.tx.tx_hash(&self.signature).into())
115 }
116
117 pub fn into_parts(self) -> (T, Signature, B256) {
119 let hash = *self.hash();
120 (self.tx, self.signature, hash)
121 }
122
123 pub fn rlp_encoded_length(&self) -> usize {
125 self.tx.rlp_encoded_length_with_signature(&self.signature)
126 }
127
128 pub fn rlp_encode(&self, out: &mut dyn BufMut) {
130 self.tx.rlp_encode_signed(&self.signature, out);
131 }
132
133 pub fn eip2718_encoded_length(&self) -> usize {
135 self.tx.eip2718_encoded_length(&self.signature)
136 }
137
138 pub fn eip2718_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
140 self.tx.eip2718_encode_with_type(&self.signature, ty, out);
141 }
142
143 pub fn eip2718_encode(&self, out: &mut dyn BufMut) {
145 self.tx.eip2718_encode(&self.signature, out);
146 }
147
148 pub fn network_encoded_length(&self) -> usize {
150 self.tx.network_encoded_length(&self.signature)
151 }
152
153 pub fn network_encode_with_type(&self, ty: u8, out: &mut dyn BufMut) {
155 self.tx.network_encode_with_type(&self.signature, ty, out);
156 }
157
158 pub fn network_encode(&self, out: &mut dyn BufMut) {
160 self.tx.network_encode(&self.signature, out);
161 }
162}
163
164impl<T> Signed<T>
165where
166 T: RlpEcdsaDecodableTx,
167{
168 pub fn rlp_decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
170 T::rlp_decode_signed(buf)
171 }
172
173 pub fn eip2718_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
175 T::eip2718_decode_with_type(buf, ty)
176 }
177
178 pub fn eip2718_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
180 T::eip2718_decode(buf)
181 }
182
183 pub fn network_decode_with_type(buf: &mut &[u8], ty: u8) -> Eip2718Result<Self> {
185 T::network_decode_with_type(buf, ty)
186 }
187
188 pub fn network_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
190 T::network_decode(buf)
191 }
192}
193
194impl<T> Hash for Signed<T>
195where
196 T: RlpEcdsaDecodableTx + Hash,
197{
198 fn hash<H: Hasher>(&self, state: &mut H) {
199 self.hash().hash(state);
200 self.tx.hash(state);
201 self.signature.hash(state);
202 }
203}
204
205impl<T: RlpEcdsaEncodableTx + PartialEq> PartialEq for Signed<T> {
206 fn eq(&self, other: &Self) -> bool {
207 self.hash() == other.hash() && self.tx == other.tx && self.signature == other.signature
208 }
209}
210
211impl<T: RlpEcdsaEncodableTx + PartialEq> Eq for Signed<T> {}
212
213#[cfg(feature = "k256")]
214impl<T: SignableTransaction<Signature>> Signed<T, Signature> {
215 pub fn recover_signer(
217 &self,
218 ) -> Result<alloy_primitives::Address, alloy_primitives::SignatureError> {
219 let sighash = self.tx.signature_hash();
220 self.signature.recover_address_from_prehash(&sighash)
221 }
222
223 pub fn try_into_recovered(
225 self,
226 ) -> Result<crate::transaction::Recovered<T>, alloy_primitives::SignatureError> {
227 let signer = self.recover_signer()?;
228 Ok(crate::transaction::Recovered::new_unchecked(self.tx, signer))
229 }
230
231 pub fn try_to_recovered_ref(
234 &self,
235 ) -> Result<crate::transaction::Recovered<&T>, alloy_primitives::SignatureError> {
236 let signer = self.recover_signer()?;
237 Ok(crate::transaction::Recovered::new_unchecked(&self.tx, signer))
238 }
239}
240
241#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
242impl<'a, T: SignableTransaction<Signature> + arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a>
243 for Signed<T, Signature>
244{
245 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
246 use k256::{
247 ecdsa::{signature::hazmat::PrehashSigner, SigningKey},
248 NonZeroScalar,
249 };
250 use rand::{rngs::StdRng, SeedableRng};
251
252 let rng_seed = u.arbitrary::<[u8; 32]>()?;
253 let mut rand_gen = StdRng::from_seed(rng_seed);
254 let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into();
255
256 let tx = T::arbitrary(u)?;
257
258 let (recoverable_sig, recovery_id) =
259 signing_key.sign_prehash(tx.signature_hash().as_ref()).unwrap();
260 let signature: Signature = (recoverable_sig, recovery_id).into();
261
262 Ok(tx.into_signed(signature))
263 }
264}
265
266#[cfg(feature = "serde")]
267mod serde {
268 use crate::transaction::RlpEcdsaEncodableTx;
269 use alloc::borrow::Cow;
270 use alloy_primitives::B256;
271 use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
272
273 #[derive(Serialize, Deserialize)]
274 struct Signed<'a, T: Clone, Sig: Clone> {
275 #[serde(flatten)]
276 tx: Cow<'a, T>,
277 #[serde(flatten)]
278 signature: Cow<'a, Sig>,
279 hash: Cow<'a, B256>,
280 }
281
282 impl<T> Serialize for super::Signed<T>
283 where
284 T: Clone + RlpEcdsaEncodableTx + Serialize,
285 {
286 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
287 where
288 S: Serializer,
289 {
290 Signed {
291 tx: Cow::Borrowed(&self.tx),
292 signature: Cow::Borrowed(&self.signature),
293 hash: Cow::Borrowed(self.hash()),
294 }
295 .serialize(serializer)
296 }
297 }
298
299 impl<'de, T, Sig> Deserialize<'de> for super::Signed<T, Sig>
300 where
301 T: Clone + DeserializeOwned,
302 Sig: Clone + DeserializeOwned,
303 {
304 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
305 where
306 D: Deserializer<'de>,
307 {
308 Signed::<T, Sig>::deserialize(deserializer).map(|value| {
309 Self::new_unchecked(
310 value.tx.into_owned(),
311 value.signature.into_owned(),
312 value.hash.into_owned(),
313 )
314 })
315 }
316 }
317}