1use core::ops::Deref;
2
3#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5use alloy_primitives::{keccak256, Address, PrimitiveSignature, SignatureError, B256, U256, U8};
6use alloy_rlp::{
7 length_of_length, BufMut, Decodable, Encodable, Header, Result as RlpResult, RlpDecodable,
8 RlpEncodable,
9};
10use core::hash::{Hash, Hasher};
11
12#[derive(Debug, Clone, Hash, Eq, PartialEq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub enum RecoveredAuthority {
17 Valid(Address),
19 Invalid,
21}
22
23impl RecoveredAuthority {
24 pub const fn address(&self) -> Option<Address> {
26 match *self {
27 Self::Valid(address) => Some(address),
28 Self::Invalid => None,
29 }
30 }
31
32 pub const fn is_valid(&self) -> bool {
34 matches!(self, Self::Valid(_))
35 }
36
37 pub const fn is_invalid(&self) -> bool {
39 matches!(self, Self::Invalid)
40 }
41}
42
43#[derive(Debug, Clone, Hash, RlpEncodable, RlpDecodable, Eq, PartialEq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
47#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
48pub struct Authorization {
49 #[cfg_attr(feature = "serde", serde(with = "quantity"))]
51 pub chain_id: u64,
52 pub address: Address,
54 #[cfg_attr(feature = "serde", serde(with = "quantity"))]
56 pub nonce: u64,
57}
58
59impl Authorization {
60 pub const fn chain_id(&self) -> u64 {
66 self.chain_id
67 }
68
69 pub const fn address(&self) -> &Address {
71 &self.address
72 }
73
74 pub const fn nonce(&self) -> u64 {
76 self.nonce
77 }
78
79 #[inline]
84 pub fn signature_hash(&self) -> B256 {
85 use super::constants::MAGIC;
86
87 let mut buf = Vec::new();
88 buf.put_u8(MAGIC);
89 self.encode(&mut buf);
90
91 keccak256(buf)
92 }
93
94 pub fn into_signed(self, signature: PrimitiveSignature) -> SignedAuthorization {
96 SignedAuthorization {
97 inner: self,
98 r: signature.r(),
99 s: signature.s(),
100 y_parity: U8::from(signature.v()),
101 }
102 }
103}
104
105#[derive(Debug, Clone, Eq, PartialEq)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
108pub struct SignedAuthorization {
109 #[cfg_attr(feature = "serde", serde(flatten))]
111 inner: Authorization,
112 #[cfg_attr(feature = "serde", serde(rename = "yParity", alias = "v"))]
115 y_parity: U8,
116 r: U256,
118 s: U256,
120}
121
122impl SignedAuthorization {
123 pub fn new_unchecked(inner: Authorization, y_parity: u8, r: U256, s: U256) -> Self {
125 Self { inner, y_parity: U8::from(y_parity), r, s }
126 }
127
128 pub fn signature(&self) -> Result<PrimitiveSignature, SignatureError> {
134 if self.y_parity() <= 1 {
135 Ok(PrimitiveSignature::new(self.r, self.s, self.y_parity() == 1))
136 } else {
137 Err(SignatureError::InvalidParity(self.y_parity() as u64))
138 }
139 }
140
141 pub const fn strip_signature(self) -> Authorization {
143 self.inner
144 }
145
146 pub const fn inner(&self) -> &Authorization {
148 &self.inner
149 }
150
151 pub fn y_parity(&self) -> u8 {
153 self.y_parity.to()
154 }
155
156 pub const fn r(&self) -> U256 {
158 self.r
159 }
160
161 pub const fn s(&self) -> U256 {
163 self.s
164 }
165
166 fn decode_fields(buf: &mut &[u8]) -> RlpResult<Self> {
168 Ok(Self {
169 inner: Authorization {
170 chain_id: Decodable::decode(buf)?,
171 address: Decodable::decode(buf)?,
172 nonce: Decodable::decode(buf)?,
173 },
174 y_parity: Decodable::decode(buf)?,
175 r: Decodable::decode(buf)?,
176 s: Decodable::decode(buf)?,
177 })
178 }
179
180 fn fields_len(&self) -> usize {
182 self.inner.chain_id.length()
183 + self.inner.address.length()
184 + self.inner.nonce.length()
185 + self.y_parity.length()
186 + self.r.length()
187 + self.s.length()
188 }
189}
190
191impl Hash for SignedAuthorization {
192 fn hash<H: Hasher>(&self, state: &mut H) {
193 self.inner.hash(state);
194 self.r.hash(state);
195 self.s.hash(state);
196 self.y_parity.hash(state);
197 }
198}
199
200impl Decodable for SignedAuthorization {
201 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
202 let header = Header::decode(buf)?;
203 if !header.list {
204 return Err(alloy_rlp::Error::UnexpectedString);
205 }
206 let started_len = buf.len();
207
208 let this = Self::decode_fields(buf)?;
209
210 let consumed = started_len - buf.len();
211 if consumed != header.payload_length {
212 return Err(alloy_rlp::Error::ListLengthMismatch {
213 expected: header.payload_length,
214 got: consumed,
215 });
216 }
217
218 Ok(this)
219 }
220}
221
222impl Encodable for SignedAuthorization {
223 fn encode(&self, buf: &mut dyn BufMut) {
224 Header { list: true, payload_length: self.fields_len() }.encode(buf);
225 self.inner.chain_id.encode(buf);
226 self.inner.address.encode(buf);
227 self.inner.nonce.encode(buf);
228 self.y_parity.encode(buf);
229 self.r.encode(buf);
230 self.s.encode(buf);
231 }
232
233 fn length(&self) -> usize {
234 let len = self.fields_len();
235 len + length_of_length(len)
236 }
237}
238
239#[cfg(feature = "k256")]
240impl SignedAuthorization {
241 pub fn recover_authority(&self) -> Result<Address, crate::error::Eip7702Error> {
247 let signature = self.signature()?;
248
249 if signature.s() > crate::constants::SECP256K1N_HALF {
250 return Err(crate::error::Eip7702Error::InvalidSValue(signature.s()));
251 }
252
253 Ok(signature.recover_address_from_prehash(&self.inner.signature_hash())?)
254 }
255
256 pub fn into_recovered(self) -> RecoveredAuthorization {
259 let authority_result = self.recover_authority();
260 let authority =
261 authority_result.map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid);
262
263 RecoveredAuthorization { inner: self.inner, authority }
264 }
265}
266
267impl Deref for SignedAuthorization {
268 type Target = Authorization;
269
270 fn deref(&self) -> &Self::Target {
271 &self.inner
272 }
273}
274
275#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
276impl<'a> arbitrary::Arbitrary<'a> for SignedAuthorization {
277 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
278 use k256::{
279 ecdsa::{signature::hazmat::PrehashSigner, SigningKey},
280 NonZeroScalar,
281 };
282 use rand::{rngs::StdRng, SeedableRng};
283
284 let rng_seed = u.arbitrary::<[u8; 32]>()?;
285 let mut rand_gen = StdRng::from_seed(rng_seed);
286 let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into();
287
288 let inner = u.arbitrary::<Authorization>()?;
289 let signature_hash = inner.signature_hash();
290
291 let (recoverable_sig, recovery_id) =
292 signing_key.sign_prehash(signature_hash.as_ref()).unwrap();
293 let signature =
294 PrimitiveSignature::from_signature_and_parity(recoverable_sig, recovery_id.is_y_odd());
295
296 Ok(inner.into_signed(signature))
297 }
298}
299
300#[derive(Debug, Clone, Hash, Eq, PartialEq)]
302#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
303pub struct RecoveredAuthorization {
304 #[cfg_attr(feature = "serde", serde(flatten))]
305 inner: Authorization,
306 authority: RecoveredAuthority,
309}
310
311impl RecoveredAuthorization {
312 pub const fn new_unchecked(inner: Authorization, authority: RecoveredAuthority) -> Self {
314 Self { inner, authority }
315 }
316
317 pub const fn authority(&self) -> Option<Address> {
319 self.authority.address()
320 }
321
322 pub const fn into_parts(self) -> (Authorization, RecoveredAuthority) {
324 (self.inner, self.authority)
325 }
326}
327
328#[cfg(feature = "k256")]
329impl From<SignedAuthorization> for RecoveredAuthority {
330 fn from(value: SignedAuthorization) -> Self {
331 value.into_recovered().authority
332 }
333}
334
335#[cfg(feature = "k256")]
336impl From<SignedAuthorization> for RecoveredAuthorization {
337 fn from(value: SignedAuthorization) -> Self {
338 value.into_recovered()
339 }
340}
341impl Deref for RecoveredAuthorization {
342 type Target = Authorization;
343
344 fn deref(&self) -> &Self::Target {
345 &self.inner
346 }
347}
348
349#[cfg(feature = "serde")]
350mod quantity {
351 use alloy_primitives::U64;
352 use serde::{Deserialize, Deserializer, Serialize, Serializer};
353
354 pub(crate) fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
356 where
357 S: Serializer,
358 {
359 U64::from(*value).serialize(serializer)
360 }
361
362 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
364 where
365 D: Deserializer<'de>,
366 {
367 U64::deserialize(deserializer).map(|value| value.to())
368 }
369}
370
371#[cfg(test)]
372mod tests {
373 use super::*;
374 use alloy_primitives::hex;
375 use core::str::FromStr;
376
377 fn test_encode_decode_roundtrip(auth: Authorization) {
378 let mut buf = Vec::new();
379 auth.encode(&mut buf);
380 let decoded = Authorization::decode(&mut buf.as_ref()).unwrap();
381 assert_eq!(buf.len(), auth.length());
382 assert_eq!(decoded, auth);
383 }
384
385 #[test]
386 fn test_encode_decode_auth() {
387 test_encode_decode_roundtrip(Authorization {
389 chain_id: 1u64,
390 address: Address::left_padding_from(&[6]),
391 nonce: 1,
392 });
393 }
394
395 #[test]
396 fn test_encode_decode_signed_auth() {
397 let auth =
398 Authorization { chain_id: 1u64, address: Address::left_padding_from(&[6]), nonce: 1 };
399
400 let auth = auth.into_signed(PrimitiveSignature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap());
401 let mut buf = Vec::new();
402 auth.encode(&mut buf);
403
404 let expected = "f85a019400000000000000000000000000000000000000060180a048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804";
405 assert_eq!(hex::encode(&buf), expected);
406
407 let decoded = SignedAuthorization::decode(&mut buf.as_ref()).unwrap();
408 assert_eq!(buf.len(), auth.length());
409 assert_eq!(decoded, auth);
410 }
411
412 #[cfg(feature = "serde")]
413 #[test]
414 fn test_auth_json() {
415 let sig = r#"{"r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05","yParity":"0x1"}"#;
416 let auth =
417 Authorization { chain_id: 1u64, address: Address::left_padding_from(&[6]), nonce: 1 }
418 .into_signed(serde_json::from_str(sig).unwrap());
419 let val = serde_json::to_string(&auth).unwrap();
420 let s = r#"{"chainId":"0x1","address":"0x0000000000000000000000000000000000000006","nonce":"0x1","yParity":"0x1","r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05"}"#;
421 assert_eq!(val, s);
422 }
423
424 #[cfg(all(feature = "arbitrary", feature = "k256"))]
425 #[test]
426 fn test_arbitrary_auth() {
427 use arbitrary::Arbitrary;
428 let mut unstructured = arbitrary::Unstructured::new(b"unstructured auth");
429 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
431 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
432 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
433 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
434 }
435}
436
437#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
439pub(super) mod serde_bincode_compat {
440 use alloc::borrow::Cow;
441 use alloy_primitives::{U256, U8};
442 use serde::{Deserialize, Deserializer, Serialize, Serializer};
443 use serde_with::{DeserializeAs, SerializeAs};
444
445 use crate::Authorization;
446
447 #[derive(Debug, Serialize, Deserialize)]
463 pub struct SignedAuthorization<'a> {
464 inner: Cow<'a, Authorization>,
465 #[serde(rename = "yParity")]
466 y_parity: U8,
467 r: U256,
468 s: U256,
469 }
470
471 impl<'a> From<&'a super::SignedAuthorization> for SignedAuthorization<'a> {
472 fn from(value: &'a super::SignedAuthorization) -> Self {
473 Self {
474 inner: Cow::Borrowed(&value.inner),
475 y_parity: value.y_parity,
476 r: value.r,
477 s: value.s,
478 }
479 }
480 }
481
482 impl<'a> From<SignedAuthorization<'a>> for super::SignedAuthorization {
483 fn from(value: SignedAuthorization<'a>) -> Self {
484 Self {
485 inner: value.inner.into_owned(),
486 y_parity: value.y_parity,
487 r: value.r,
488 s: value.s,
489 }
490 }
491 }
492
493 impl SerializeAs<super::SignedAuthorization> for SignedAuthorization<'_> {
494 fn serialize_as<S>(
495 source: &super::SignedAuthorization,
496 serializer: S,
497 ) -> Result<S::Ok, S::Error>
498 where
499 S: Serializer,
500 {
501 SignedAuthorization::from(source).serialize(serializer)
502 }
503 }
504
505 impl<'de> DeserializeAs<'de, super::SignedAuthorization> for SignedAuthorization<'de> {
506 fn deserialize_as<D>(deserializer: D) -> Result<super::SignedAuthorization, D::Error>
507 where
508 D: Deserializer<'de>,
509 {
510 SignedAuthorization::deserialize(deserializer).map(Into::into)
511 }
512 }
513
514 #[cfg(all(test, feature = "k256"))]
515 mod tests {
516 use arbitrary::Arbitrary;
517 use rand::Rng;
518 use serde::{Deserialize, Serialize};
519 use serde_with::serde_as;
520
521 use super::super::{serde_bincode_compat, SignedAuthorization};
522
523 #[test]
524 fn test_signed_authorization_bincode_roundtrip() {
525 #[serde_as]
526 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
527 struct Data {
528 #[serde_as(as = "serde_bincode_compat::SignedAuthorization")]
529 authorization: SignedAuthorization,
530 }
531
532 let mut bytes = [0u8; 1024];
533 rand::thread_rng().fill(bytes.as_mut_slice());
534 let data = Data {
535 authorization: SignedAuthorization::arbitrary(&mut arbitrary::Unstructured::new(
536 &bytes,
537 ))
538 .unwrap(),
539 };
540
541 let encoded = bincode::serialize(&data).unwrap();
542 let decoded: Data = bincode::deserialize(&encoded).unwrap();
543 assert_eq!(decoded, data);
544 }
545 }
546}