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