1use core::ops::Deref;
2
3#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5use alloy_primitives::{keccak256, Address, Signature, 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 pub chain_id: U256,
51 pub address: Address,
53 #[cfg_attr(feature = "serde", serde(with = "quantity"))]
55 pub nonce: u64,
56}
57
58impl Authorization {
59 pub const fn chain_id(&self) -> &U256 {
65 &self.chain_id
66 }
67
68 pub const fn address(&self) -> &Address {
70 &self.address
71 }
72
73 pub const fn nonce(&self) -> u64 {
75 self.nonce
76 }
77
78 #[inline]
83 pub fn signature_hash(&self) -> B256 {
84 use super::constants::MAGIC;
85
86 let mut buf = Vec::new();
87 buf.put_u8(MAGIC);
88 self.encode(&mut buf);
89
90 keccak256(buf)
91 }
92
93 pub fn into_signed(self, signature: Signature) -> SignedAuthorization {
95 SignedAuthorization {
96 inner: self,
97 r: signature.r(),
98 s: signature.s(),
99 y_parity: U8::from(signature.v()),
100 }
101 }
102}
103
104#[derive(Debug, Clone, Eq, PartialEq)]
106#[cfg_attr(feature = "serde", derive(serde::Serialize))]
107pub struct SignedAuthorization {
108 #[cfg_attr(feature = "serde", serde(flatten))]
110 inner: Authorization,
111 #[cfg_attr(feature = "serde", serde(rename = "yParity", alias = "v"))]
114 y_parity: U8,
115 r: U256,
117 s: U256,
119}
120
121impl SignedAuthorization {
122 pub fn new_unchecked(inner: Authorization, y_parity: u8, r: U256, s: U256) -> Self {
124 Self { inner, y_parity: U8::from(y_parity), r, s }
125 }
126
127 pub fn signature(&self) -> Result<Signature, SignatureError> {
133 if self.y_parity() <= 1 {
134 Ok(Signature::new(self.r, self.s, self.y_parity() == 1))
135 } else {
136 Err(SignatureError::InvalidParity(self.y_parity() as u64))
137 }
138 }
139
140 pub const fn strip_signature(self) -> Authorization {
142 self.inner
143 }
144
145 pub const fn inner(&self) -> &Authorization {
147 &self.inner
148 }
149
150 pub fn y_parity(&self) -> u8 {
152 self.y_parity.to()
153 }
154
155 pub const fn r(&self) -> U256 {
157 self.r
158 }
159
160 pub const fn s(&self) -> U256 {
162 self.s
163 }
164
165 fn decode_fields(buf: &mut &[u8]) -> RlpResult<Self> {
167 Ok(Self {
168 inner: Authorization {
169 chain_id: Decodable::decode(buf)?,
170 address: Decodable::decode(buf)?,
171 nonce: Decodable::decode(buf)?,
172 },
173 y_parity: Decodable::decode(buf)?,
174 r: Decodable::decode(buf)?,
175 s: Decodable::decode(buf)?,
176 })
177 }
178
179 fn fields_len(&self) -> usize {
181 self.inner.chain_id.length()
182 + self.inner.address.length()
183 + self.inner.nonce.length()
184 + self.y_parity.length()
185 + self.r.length()
186 + self.s.length()
187 }
188}
189
190impl Hash for SignedAuthorization {
191 fn hash<H: Hasher>(&self, state: &mut H) {
192 self.inner.hash(state);
193 self.r.hash(state);
194 self.s.hash(state);
195 self.y_parity.hash(state);
196 }
197}
198
199impl Decodable for SignedAuthorization {
200 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
201 let header = Header::decode(buf)?;
202 if !header.list {
203 return Err(alloy_rlp::Error::UnexpectedString);
204 }
205 let started_len = buf.len();
206
207 let this = Self::decode_fields(buf)?;
208
209 let consumed = started_len - buf.len();
210 if consumed != header.payload_length {
211 return Err(alloy_rlp::Error::ListLengthMismatch {
212 expected: header.payload_length,
213 got: consumed,
214 });
215 }
216
217 Ok(this)
218 }
219}
220
221impl Encodable for SignedAuthorization {
222 fn encode(&self, buf: &mut dyn BufMut) {
223 Header { list: true, payload_length: self.fields_len() }.encode(buf);
224 self.inner.chain_id.encode(buf);
225 self.inner.address.encode(buf);
226 self.inner.nonce.encode(buf);
227 self.y_parity.encode(buf);
228 self.r.encode(buf);
229 self.s.encode(buf);
230 }
231
232 fn length(&self) -> usize {
233 let len = self.fields_len();
234 len + length_of_length(len)
235 }
236}
237
238#[cfg(feature = "k256")]
239impl SignedAuthorization {
240 pub fn recover_authority(&self) -> Result<Address, crate::error::Eip7702Error> {
246 let signature = self.signature()?;
247
248 if signature.s() > crate::constants::SECP256K1N_HALF {
249 return Err(crate::error::Eip7702Error::InvalidSValue(signature.s()));
250 }
251
252 Ok(signature.recover_address_from_prehash(&self.inner.signature_hash())?)
253 }
254
255 pub fn into_recovered(self) -> RecoveredAuthorization {
258 let authority_result = self.recover_authority();
259 let authority =
260 authority_result.map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid);
261
262 RecoveredAuthorization { inner: self.inner, authority }
263 }
264}
265
266impl Deref for SignedAuthorization {
267 type Target = Authorization;
268
269 fn deref(&self) -> &Self::Target {
270 &self.inner
271 }
272}
273
274#[cfg(all(any(test, feature = "arbitrary"), feature = "k256"))]
275impl<'a> arbitrary::Arbitrary<'a> for SignedAuthorization {
276 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
277 use k256::{
278 ecdsa::{signature::hazmat::PrehashSigner, SigningKey},
279 NonZeroScalar,
280 };
281 use rand::{rngs::StdRng, SeedableRng};
282
283 let rng_seed = u.arbitrary::<[u8; 32]>()?;
284 let mut rand_gen = StdRng::from_seed(rng_seed);
285 let signing_key: SigningKey = NonZeroScalar::random(&mut rand_gen).into();
286
287 let inner = u.arbitrary::<Authorization>()?;
288 let signature_hash = inner.signature_hash();
289
290 let (recoverable_sig, recovery_id) =
291 signing_key.sign_prehash(signature_hash.as_ref()).unwrap();
292 let signature =
293 Signature::from_signature_and_parity(recoverable_sig, recovery_id.is_y_odd());
294
295 Ok(inner.into_signed(signature))
296 }
297}
298
299#[cfg(feature = "serde")]
300impl<'de> serde::Deserialize<'de> for SignedAuthorization {
301 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
302 where
303 D: serde::de::Deserializer<'de>,
304 {
305 #[derive(serde::Deserialize)]
306 struct Helper {
307 #[cfg_attr(feature = "serde", serde(flatten))]
308 inner: Authorization,
309 r: U256,
310 s: U256,
311 #[serde(rename = "yParity")]
312 y_parity: Option<U8>,
313 v: Option<U8>,
314 }
315
316 let Helper { inner, r, s, y_parity, v } = Helper::deserialize(deserializer)?;
317
318 let y_parity =
320 y_parity.or(v).ok_or_else(|| serde::de::Error::custom("missing `yParity` or `v`"))?;
321
322 Ok(Self { inner, r, s, y_parity })
323 }
324}
325
326#[derive(Debug, Clone, Hash, Eq, PartialEq)]
328#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
329pub struct RecoveredAuthorization {
330 #[cfg_attr(feature = "serde", serde(flatten))]
331 inner: Authorization,
332 authority: RecoveredAuthority,
335}
336
337impl RecoveredAuthorization {
338 pub const fn new_unchecked(inner: Authorization, authority: RecoveredAuthority) -> Self {
340 Self { inner, authority }
341 }
342
343 pub const fn authority(&self) -> Option<Address> {
345 self.authority.address()
346 }
347
348 pub const fn into_parts(self) -> (Authorization, RecoveredAuthority) {
350 (self.inner, self.authority)
351 }
352}
353
354#[cfg(feature = "k256")]
355impl From<SignedAuthorization> for RecoveredAuthority {
356 fn from(value: SignedAuthorization) -> Self {
357 value.into_recovered().authority
358 }
359}
360
361#[cfg(feature = "k256")]
362impl From<SignedAuthorization> for RecoveredAuthorization {
363 fn from(value: SignedAuthorization) -> Self {
364 value.into_recovered()
365 }
366}
367impl Deref for RecoveredAuthorization {
368 type Target = Authorization;
369
370 fn deref(&self) -> &Self::Target {
371 &self.inner
372 }
373}
374
375#[cfg(feature = "serde")]
376mod quantity {
377 use alloy_primitives::U64;
378 use serde::{Deserialize, Deserializer, Serialize, Serializer};
379
380 pub(crate) fn serialize<S>(value: &u64, serializer: S) -> Result<S::Ok, S::Error>
382 where
383 S: Serializer,
384 {
385 U64::from(*value).serialize(serializer)
386 }
387
388 pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
390 where
391 D: Deserializer<'de>,
392 {
393 U64::deserialize(deserializer).map(|value| value.to())
394 }
395}
396
397#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
399pub(super) mod serde_bincode_compat {
400 use crate::Authorization;
401 use alloc::borrow::Cow;
402 use alloy_primitives::{U256, U8};
403 use serde::{Deserialize, Deserializer, Serialize, Serializer};
404 use serde_with::{DeserializeAs, SerializeAs};
405
406 #[derive(Debug, Serialize, Deserialize)]
422 pub struct SignedAuthorization<'a> {
423 inner: Cow<'a, Authorization>,
424 #[serde(rename = "yParity")]
425 y_parity: U8,
426 r: U256,
427 s: U256,
428 }
429
430 impl<'a> From<&'a super::SignedAuthorization> for SignedAuthorization<'a> {
431 fn from(value: &'a super::SignedAuthorization) -> Self {
432 Self {
433 inner: Cow::Borrowed(&value.inner),
434 y_parity: value.y_parity,
435 r: value.r,
436 s: value.s,
437 }
438 }
439 }
440
441 impl<'a> From<SignedAuthorization<'a>> for super::SignedAuthorization {
442 fn from(value: SignedAuthorization<'a>) -> Self {
443 Self {
444 inner: value.inner.into_owned(),
445 y_parity: value.y_parity,
446 r: value.r,
447 s: value.s,
448 }
449 }
450 }
451
452 impl SerializeAs<super::SignedAuthorization> for SignedAuthorization<'_> {
453 fn serialize_as<S>(
454 source: &super::SignedAuthorization,
455 serializer: S,
456 ) -> Result<S::Ok, S::Error>
457 where
458 S: Serializer,
459 {
460 SignedAuthorization::from(source).serialize(serializer)
461 }
462 }
463
464 impl<'de> DeserializeAs<'de, super::SignedAuthorization> for SignedAuthorization<'de> {
465 fn deserialize_as<D>(deserializer: D) -> Result<super::SignedAuthorization, D::Error>
466 where
467 D: Deserializer<'de>,
468 {
469 SignedAuthorization::deserialize(deserializer).map(Into::into)
470 }
471 }
472
473 #[cfg(all(test, feature = "k256"))]
474 mod tests {
475 use arbitrary::Arbitrary;
476 use rand::Rng;
477 use serde::{Deserialize, Serialize};
478 use serde_with::serde_as;
479
480 use super::super::{serde_bincode_compat, SignedAuthorization};
481
482 #[test]
483 fn test_signed_authorization_bincode_roundtrip() {
484 #[serde_as]
485 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
486 struct Data {
487 #[serde_as(as = "serde_bincode_compat::SignedAuthorization")]
488 authorization: SignedAuthorization,
489 }
490
491 let mut bytes = [0u8; 1024];
492 rand::thread_rng().fill(bytes.as_mut_slice());
493 let data = Data {
494 authorization: SignedAuthorization::arbitrary(&mut arbitrary::Unstructured::new(
495 &bytes,
496 ))
497 .unwrap(),
498 };
499
500 let encoded = bincode::serialize(&data).unwrap();
501 let decoded: Data = bincode::deserialize(&encoded).unwrap();
502 assert_eq!(decoded, data);
503 }
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510 use alloy_primitives::hex;
511 use core::str::FromStr;
512
513 fn test_encode_decode_roundtrip(auth: Authorization) {
514 let mut buf = Vec::new();
515 auth.encode(&mut buf);
516 let decoded = Authorization::decode(&mut buf.as_ref()).unwrap();
517 assert_eq!(buf.len(), auth.length());
518 assert_eq!(decoded, auth);
519 }
520
521 #[test]
522 fn test_encode_decode_auth() {
523 test_encode_decode_roundtrip(Authorization {
525 chain_id: U256::from(1),
526 address: Address::left_padding_from(&[6]),
527 nonce: 1,
528 });
529 }
530
531 #[test]
532 fn test_encode_decode_signed_auth() {
533 let auth = Authorization {
534 chain_id: U256::from(1),
535 address: Address::left_padding_from(&[6]),
536 nonce: 1,
537 };
538
539 let auth = auth.into_signed(Signature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap());
540 let mut buf = Vec::new();
541 auth.encode(&mut buf);
542
543 let expected = "f85a019400000000000000000000000000000000000000060180a048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804";
544 assert_eq!(hex::encode(&buf), expected);
545
546 let decoded = SignedAuthorization::decode(&mut buf.as_ref()).unwrap();
547 assert_eq!(buf.len(), auth.length());
548 assert_eq!(decoded, auth);
549 }
550
551 #[cfg(feature = "serde")]
552 #[test]
553 fn test_auth_json() {
554 let sig = r#"{"r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05","yParity":"0x1"}"#;
555 let auth = Authorization {
556 chain_id: U256::from(1),
557 address: Address::left_padding_from(&[6]),
558 nonce: 1,
559 }
560 .into_signed(serde_json::from_str(sig).unwrap());
561 let val = serde_json::to_string(&auth).unwrap();
562 let s = r#"{"chainId":"0x1","address":"0x0000000000000000000000000000000000000006","nonce":"0x1","yParity":"0x1","r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05"}"#;
563 assert_eq!(val, s);
564 }
565
566 #[cfg(all(feature = "arbitrary", feature = "k256"))]
567 #[test]
568 fn test_arbitrary_auth() {
569 use arbitrary::Arbitrary;
570 let mut unstructured = arbitrary::Unstructured::new(b"unstructured auth");
571 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
573 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
574 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
575 let _auth = SignedAuthorization::arbitrary(&mut unstructured).unwrap();
576 }
577
578 #[test]
579 #[cfg(feature = "serde")]
580 fn deserde_signed_auth_with_duplicate_fields() {
581 let s = r#"{
582 "chainId": "0x2105",
583 "address": "0x000000004F43C49e93C970E84001853a70923B03",
584 "nonce": "0x0",
585 "r": "0xb3fdb76993ec6787313ab8b54129200032dfb9ce683fa9f7693129421e6a3185",
586 "s": "0x210b3350107a5687b532a346a90e7cc9a799b995743e2b79698bedba7bd779ae",
587 "v": "0x1b",
588 "yParity": "0x0"
589 }"#;
590
591 let auth: SignedAuthorization = serde_json::from_str(s).unwrap();
592 assert!(auth.y_parity.is_zero());
593 }
594}