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