alloy_primitives/signature/
primitive_sig.rs1#![allow(clippy::missing_const_for_fn)] use crate::{hex, normalize_v, signature::SignatureError, uint, B256, U256};
4use alloc::vec::Vec;
5use core::str::FromStr;
6
7#[cfg(feature = "k256")]
8use crate::Address;
9
10const SECP256K1N_ORDER: U256 =
12 uint!(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141_U256);
13
14#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
16pub struct PrimitiveSignature {
17 y_parity: bool,
18 r: U256,
19 s: U256,
20}
21
22impl TryFrom<&[u8]> for PrimitiveSignature {
23 type Error = SignatureError;
24
25 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
29 Self::from_raw(bytes)
30 }
31}
32
33impl FromStr for PrimitiveSignature {
34 type Err = SignatureError;
35
36 fn from_str(s: &str) -> Result<Self, Self::Err> {
37 Self::from_raw_array(&hex::decode_to_array(s)?)
38 }
39}
40
41impl From<&PrimitiveSignature> for [u8; 65] {
42 #[inline]
43 fn from(value: &PrimitiveSignature) -> [u8; 65] {
44 value.as_bytes()
45 }
46}
47
48impl From<PrimitiveSignature> for [u8; 65] {
49 #[inline]
50 fn from(value: PrimitiveSignature) -> [u8; 65] {
51 value.as_bytes()
52 }
53}
54
55impl From<&PrimitiveSignature> for Vec<u8> {
56 #[inline]
57 fn from(value: &PrimitiveSignature) -> Self {
58 value.as_bytes().to_vec()
59 }
60}
61
62impl From<PrimitiveSignature> for Vec<u8> {
63 #[inline]
64 fn from(value: PrimitiveSignature) -> Self {
65 value.as_bytes().to_vec()
66 }
67}
68
69#[cfg(feature = "k256")]
70impl From<(k256::ecdsa::Signature, k256::ecdsa::RecoveryId)> for PrimitiveSignature {
71 fn from(value: (k256::ecdsa::Signature, k256::ecdsa::RecoveryId)) -> Self {
72 Self::from_signature_and_parity(value.0, value.1.is_y_odd())
73 }
74}
75
76#[cfg(feature = "k256")]
77impl TryFrom<PrimitiveSignature> for k256::ecdsa::Signature {
78 type Error = k256::ecdsa::Error;
79
80 fn try_from(value: PrimitiveSignature) -> Result<Self, Self::Error> {
81 value.to_k256()
82 }
83}
84
85#[cfg(feature = "rlp")]
86impl PrimitiveSignature {
87 pub fn decode_rlp_vrs(
91 buf: &mut &[u8],
92 decode_parity: impl FnOnce(&mut &[u8]) -> alloy_rlp::Result<bool>,
93 ) -> Result<Self, alloy_rlp::Error> {
94 use alloy_rlp::Decodable;
95
96 let parity = decode_parity(buf)?;
97 let r = Decodable::decode(buf)?;
98 let s = Decodable::decode(buf)?;
99
100 Ok(Self::new(r, s, parity))
101 }
102}
103
104impl PrimitiveSignature {
105 #[inline]
107 pub fn new(r: U256, s: U256, y_parity: bool) -> Self {
108 Self { r, s, y_parity }
109 }
110
111 #[inline]
116 pub fn from_raw(bytes: &[u8]) -> Result<Self, SignatureError> {
117 Self::from_raw_array(
118 bytes.try_into().map_err(|_| SignatureError::FromBytes("expected exactly 65 bytes"))?,
119 )
120 }
121
122 #[inline]
126 pub fn from_raw_array(bytes: &[u8; 65]) -> Result<Self, SignatureError> {
127 let [bytes @ .., v] = bytes;
128 let v = *v as u64;
129 let Some(parity) = normalize_v(v) else { return Err(SignatureError::InvalidParity(v)) };
130 Ok(Self::from_bytes_and_parity(bytes, parity))
131 }
132
133 #[inline]
139 #[track_caller]
140 pub fn from_bytes_and_parity(bytes: &[u8], parity: bool) -> Self {
141 let (r_bytes, s_bytes) = bytes[..64].split_at(32);
142 let r = U256::from_be_slice(r_bytes);
143 let s = U256::from_be_slice(s_bytes);
144 Self::new(r, s, parity)
145 }
146
147 #[inline]
152 pub fn as_bytes(&self) -> [u8; 65] {
153 let mut sig = [0u8; 65];
154 sig[..32].copy_from_slice(&self.r.to_be_bytes::<32>());
155 sig[32..64].copy_from_slice(&self.s.to_be_bytes::<32>());
156 sig[64] = 27 + self.y_parity as u8;
157 sig
158 }
159
160 pub fn from_erc2098(bytes: &[u8]) -> Self {
171 let (r_bytes, y_and_s_bytes) = bytes[..64].split_at(32);
172 let r = U256::from_be_slice(r_bytes);
173 let y_and_s = U256::from_be_slice(y_and_s_bytes);
174 let y_parity = y_and_s.bit(255);
175 let mut s = y_and_s;
176 s.set_bit(255, false);
177 Self { y_parity, r, s }
178 }
179
180 pub fn as_erc2098(&self) -> [u8; 64] {
187 let normalized = self.normalized_s();
188 let mut sig = [0u8; 64];
192 sig[..32].copy_from_slice(&normalized.r().to_be_bytes::<32>());
193 sig[32..64].copy_from_slice(&normalized.s().to_be_bytes::<32>());
194 debug_assert_eq!(sig[32] >> 7, 0, "top bit of s should be 0");
195 sig[32] |= (normalized.y_parity as u8) << 7;
196 sig
197 }
198
199 #[inline]
201 pub fn with_parity(mut self, v: bool) -> Self {
202 self.y_parity = v;
203 self
204 }
205
206 #[cfg(feature = "k256")]
208 #[deprecated(note = "use `Signature::to_k256` instead")]
209 #[inline]
210 pub fn into_inner(self) -> k256::ecdsa::Signature {
211 self.try_into().expect("signature conversion failed")
212 }
213
214 #[cfg(feature = "k256")]
216 #[inline]
217 pub fn to_k256(self) -> Result<k256::ecdsa::Signature, k256::ecdsa::Error> {
218 k256::ecdsa::Signature::from_scalars(self.r.to_be_bytes(), self.s.to_be_bytes())
219 }
220
221 #[cfg(feature = "k256")]
223 pub fn from_signature_and_parity(sig: k256::ecdsa::Signature, v: bool) -> Self {
224 let r = U256::from_be_slice(sig.r().to_bytes().as_ref());
225 let s = U256::from_be_slice(sig.s().to_bytes().as_ref());
226 Self { y_parity: v, r, s }
227 }
228
229 #[inline]
232 pub fn from_scalars_and_parity(r: B256, s: B256, parity: bool) -> Self {
233 Self::new(U256::from_be_bytes(r.0), U256::from_be_bytes(s.0), parity)
234 }
235
236 #[inline]
243 pub fn normalize_s(&self) -> Option<Self> {
244 let s = self.s();
245 if s > SECP256K1N_ORDER >> 1 {
246 Some(Self { y_parity: !self.y_parity, r: self.r, s: SECP256K1N_ORDER - s })
247 } else {
248 None
249 }
250 }
251
252 #[inline]
259 pub fn normalized_s(self) -> Self {
260 self.normalize_s().unwrap_or(self)
261 }
262
263 #[cfg(feature = "k256")]
265 #[inline]
266 pub fn recid(&self) -> k256::ecdsa::RecoveryId {
267 k256::ecdsa::RecoveryId::new(self.y_parity, false)
268 }
269
270 #[cfg(feature = "k256")]
271 #[doc(hidden)]
272 #[deprecated(note = "use `Signature::recid` instead")]
273 pub fn recovery_id(&self) -> k256::ecdsa::RecoveryId {
274 self.recid()
275 }
276
277 #[cfg(feature = "k256")]
280 #[inline]
281 pub fn recover_address_from_msg<T: AsRef<[u8]>>(
282 &self,
283 msg: T,
284 ) -> Result<Address, SignatureError> {
285 self.recover_from_msg(msg).map(|vk| Address::from_public_key(&vk))
286 }
287
288 #[cfg(feature = "k256")]
290 #[inline]
291 pub fn recover_address_from_prehash(&self, prehash: &B256) -> Result<Address, SignatureError> {
292 self.recover_from_prehash(prehash).map(|vk| Address::from_public_key(&vk))
293 }
294
295 #[cfg(feature = "k256")]
300 #[inline]
301 pub fn recover_from_msg<T: AsRef<[u8]>>(
302 &self,
303 msg: T,
304 ) -> Result<k256::ecdsa::VerifyingKey, SignatureError> {
305 self.recover_from_prehash(&crate::eip191_hash_message(msg))
306 }
307
308 #[cfg(feature = "k256")]
312 #[inline]
313 pub fn recover_from_prehash(
314 &self,
315 prehash: &B256,
316 ) -> Result<k256::ecdsa::VerifyingKey, SignatureError> {
317 let this = self.normalized_s();
318 k256::ecdsa::VerifyingKey::recover_from_prehash(
319 prehash.as_slice(),
320 &this.to_k256()?,
321 this.recid(),
322 )
323 .map_err(Into::into)
324 }
325
326 #[inline]
328 pub fn r(&self) -> U256 {
329 self.r
330 }
331
332 #[inline]
334 pub fn s(&self) -> U256 {
335 self.s
336 }
337
338 #[inline]
340 pub fn v(&self) -> bool {
341 self.y_parity
342 }
343
344 #[cfg(feature = "rlp")]
346 pub fn rlp_rs_len(&self) -> usize {
347 alloy_rlp::Encodable::length(&self.r) + alloy_rlp::Encodable::length(&self.s)
348 }
349
350 #[cfg(feature = "rlp")]
352 pub fn write_rlp_rs(&self, out: &mut dyn alloy_rlp::BufMut) {
353 alloy_rlp::Encodable::encode(&self.r, out);
354 alloy_rlp::Encodable::encode(&self.s, out);
355 }
356
357 #[cfg(feature = "rlp")]
359 pub fn write_rlp_vrs(&self, out: &mut dyn alloy_rlp::BufMut, v: impl alloy_rlp::Encodable) {
360 v.encode(out);
361 self.write_rlp_rs(out);
362 }
363
364 #[doc(hidden)]
365 #[inline(always)]
366 pub fn test_signature() -> Self {
367 Self::from_scalars_and_parity(
368 b256!("0x840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565"),
369 b256!("0x25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1"),
370 false,
371 )
372 }
373}
374
375#[cfg(feature = "arbitrary")]
376impl<'a> arbitrary::Arbitrary<'a> for PrimitiveSignature {
377 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
378 Ok(Self::new(u.arbitrary()?, u.arbitrary()?, u.arbitrary()?))
379 }
380}
381
382#[cfg(feature = "arbitrary")]
383impl proptest::arbitrary::Arbitrary for PrimitiveSignature {
384 type Parameters = ();
385 type Strategy = proptest::strategy::Map<
386 <(U256, U256, bool) as proptest::arbitrary::Arbitrary>::Strategy,
387 fn((U256, U256, bool)) -> Self,
388 >;
389
390 fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
391 use proptest::strategy::Strategy;
392 proptest::arbitrary::any::<(U256, U256, bool)>()
393 .prop_map(|(r, s, y_parity)| Self::new(r, s, y_parity))
394 }
395}
396
397#[cfg(feature = "serde")]
398mod signature_serde {
399 use super::PrimitiveSignature;
400 use crate::{normalize_v, U256, U64};
401 use serde::{Deserialize, Deserializer, Serialize};
402
403 #[derive(Serialize, Deserialize)]
404 struct HumanReadableRepr {
405 r: U256,
406 s: U256,
407 #[serde(rename = "yParity")]
408 y_parity: Option<U64>,
409 #[serde(default, skip_serializing_if = "Option::is_none")]
410 v: Option<U64>,
411 }
412
413 type NonHumanReadableRepr = (U256, U256, U64);
414
415 impl Serialize for PrimitiveSignature {
416 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
417 where
418 S: serde::Serializer,
419 {
420 if serializer.is_human_readable() {
422 HumanReadableRepr {
423 y_parity: Some(U64::from(self.y_parity as u64)),
424 v: Some(U64::from(self.y_parity as u64)),
425 r: self.r,
426 s: self.s,
427 }
428 .serialize(serializer)
429 } else {
430 let repr: NonHumanReadableRepr = (self.r, self.s, U64::from(self.y_parity as u64));
431 repr.serialize(serializer)
432 }
433 }
434 }
435
436 impl<'de> Deserialize<'de> for PrimitiveSignature {
437 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
438 where
439 D: Deserializer<'de>,
440 {
441 let (y_parity, v, r, s) = if deserializer.is_human_readable() {
442 let HumanReadableRepr { y_parity, v, r, s } = <_>::deserialize(deserializer)?;
443 (y_parity, v, r, s)
444 } else {
445 let (r, s, y_parity) = NonHumanReadableRepr::deserialize(deserializer)?;
446 (Some(y_parity), None, r, s)
447 };
448
449 let y_parity = if let Some(y_parity) = y_parity {
451 match y_parity.to::<u64>() {
452 0 => false,
453 1 => true,
454 _ => return Err(serde::de::Error::custom("invalid yParity")),
455 }
456 } else if let Some(v) = v {
457 normalize_v(v.to()).ok_or(serde::de::Error::custom("invalid v"))?
458 } else {
459 return Err(serde::de::Error::custom("missing `yParity` or `v`"));
460 };
461
462 Ok(Self::new(r, s, y_parity))
463 }
464 }
465}
466
467#[cfg(test)]
468mod tests {
469 use super::*;
470 use core::str::FromStr;
471
472 #[cfg(feature = "rlp")]
473 use alloy_rlp::{Decodable, Encodable};
474
475 #[test]
476 #[cfg(feature = "k256")]
477 fn can_recover_tx_sender_not_normalized() {
478 let sig = PrimitiveSignature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap();
479 let hash = b256!("0x5eb4f5a33c621f32a8622d5f943b6b102994dfe4e5aebbefe69bb1b2aa0fc93e");
480 let expected = address!("0x0f65fe9276bc9a24ae7083ae28e2660ef72df99e");
481 assert_eq!(sig.recover_address_from_prehash(&hash).unwrap(), expected);
482 }
483
484 #[test]
485 #[cfg(feature = "k256")]
486 fn recover_web3_signature() {
487 let sig = PrimitiveSignature::from_str(
490 "b91467e570a6466aa9e9876cbcd013baba02900b8979d43fe208a4a4f339f5fd6007e74cd82e037b800186422fc2da167c747ef045e5d18a5f5d4300f8e1a0291c"
491 ).expect("could not parse signature");
492 let expected = address!("0x2c7536E3605D9C16a7a3D7b1898e529396a65c23");
493 assert_eq!(sig.recover_address_from_msg("Some data").unwrap(), expected);
494 }
495
496 #[test]
497 fn signature_from_str() {
498 let s1 = PrimitiveSignature::from_str(
499 "0xaa231fbe0ed2b5418e6ba7c19bee2522852955ec50996c02a2fe3e71d30ddaf1645baf4823fea7cb4fcc7150842493847cfb6a6d63ab93e8ee928ee3f61f503500"
500 ).expect("could not parse 0x-prefixed signature");
501
502 let s2 = PrimitiveSignature::from_str(
503 "aa231fbe0ed2b5418e6ba7c19bee2522852955ec50996c02a2fe3e71d30ddaf1645baf4823fea7cb4fcc7150842493847cfb6a6d63ab93e8ee928ee3f61f503500"
504 ).expect("could not parse non-prefixed signature");
505
506 assert_eq!(s1, s2);
507 }
508
509 #[cfg(feature = "serde")]
510 #[test]
511 fn deserialize_with_parity() {
512 let raw_signature_with_y_parity = serde_json::json!({
513 "r": "0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0",
514 "s": "0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05",
515 "v": "0x1",
516 "yParity": "0x1"
517 });
518
519 let signature: PrimitiveSignature =
520 serde_json::from_value(raw_signature_with_y_parity).unwrap();
521
522 let expected = PrimitiveSignature::new(
523 U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0")
524 .unwrap(),
525 U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05")
526 .unwrap(),
527 true,
528 );
529
530 assert_eq!(signature, expected);
531 }
532
533 #[cfg(feature = "serde")]
534 #[test]
535 fn serialize_both_parity() {
536 let signature = PrimitiveSignature::new(
538 U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0")
539 .unwrap(),
540 U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05")
541 .unwrap(),
542 true,
543 );
544
545 let serialized = serde_json::to_string(&signature).unwrap();
546 assert_eq!(
547 serialized,
548 r#"{"r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05","yParity":"0x1","v":"0x1"}"#
549 );
550 }
551
552 #[cfg(feature = "serde")]
553 #[test]
554 fn serialize_v_only() {
555 let signature = PrimitiveSignature::new(
557 U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0")
558 .unwrap(),
559 U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05")
560 .unwrap(),
561 true,
562 );
563
564 let expected = r#"{"r":"0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0","s":"0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05","yParity":"0x1","v":"0x1"}"#;
565
566 let serialized = serde_json::to_string(&signature).unwrap();
567 assert_eq!(serialized, expected);
568 }
569
570 #[cfg(feature = "serde")]
571 #[test]
572 fn bincode_roundtrip() {
573 let signature = PrimitiveSignature::new(
574 U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0")
575 .unwrap(),
576 U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05")
577 .unwrap(),
578 true,
579 );
580
581 let bin = bincode::serialize(&signature).unwrap();
582 assert_eq!(bincode::deserialize::<PrimitiveSignature>(&bin).unwrap(), signature);
583 }
584
585 #[cfg(feature = "rlp")]
586 #[test]
587 fn signature_rlp_encode() {
588 let sig = PrimitiveSignature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap();
590
591 let mut buf = vec![];
593
594 sig.write_rlp_vrs(&mut buf, sig.v());
596
597 let expected = "80a048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804";
599
600 assert_eq!(hex::encode(&buf), expected);
602 }
603
604 #[cfg(feature = "rlp")]
605 #[test]
606 fn signature_rlp_length() {
607 let sig = PrimitiveSignature::from_str("48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c8041b").unwrap();
609
610 assert_eq!(sig.rlp_rs_len() + sig.v().length(), 67);
612 }
613
614 #[cfg(feature = "rlp")]
615 #[test]
616 fn rlp_vrs_len() {
617 let signature = PrimitiveSignature::test_signature();
618 assert_eq!(67, signature.rlp_rs_len() + 1);
619 }
620
621 #[cfg(feature = "rlp")]
622 #[test]
623 fn encode_and_decode() {
624 let signature = PrimitiveSignature::test_signature();
625
626 let mut encoded = Vec::new();
627 signature.write_rlp_vrs(&mut encoded, signature.v());
628 assert_eq!(encoded.len(), signature.rlp_rs_len() + signature.v().length());
629 let decoded = PrimitiveSignature::decode_rlp_vrs(&mut &*encoded, bool::decode).unwrap();
630 assert_eq!(signature, decoded);
631 }
632
633 #[test]
634 fn as_bytes() {
635 let signature = PrimitiveSignature::new(
636 U256::from_str(
637 "18515461264373351373200002665853028612451056578545711640558177340181847433846",
638 )
639 .unwrap(),
640 U256::from_str(
641 "46948507304638947509940763649030358759909902576025900602547168820602576006531",
642 )
643 .unwrap(),
644 false,
645 );
646
647 let expected = hex!("0x28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa63627667cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d831b");
648 assert_eq!(signature.as_bytes(), expected);
649 }
650
651 #[test]
652 fn as_erc2098_y_false() {
653 let signature = PrimitiveSignature::new(
654 U256::from_str(
655 "47323457007453657207889730243826965761922296599680473886588287015755652701072",
656 )
657 .unwrap(),
658 U256::from_str(
659 "57228803202727131502949358313456071280488184270258293674242124340113824882788",
660 )
661 .unwrap(),
662 false,
663 );
664
665 let expected = hex!("0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b907e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064");
666 assert_eq!(signature.as_erc2098(), expected);
667 }
668
669 #[test]
670 fn as_erc2098_y_true() {
671 let signature = PrimitiveSignature::new(
672 U256::from_str("0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76")
673 .unwrap(),
674 U256::from_str("0x139c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793")
675 .unwrap(),
676 true,
677 );
678
679 let expected = hex!("0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793");
680 assert_eq!(signature.as_erc2098(), expected);
681 }
682
683 #[test]
684 fn from_erc2098_y_false() {
685 let expected = PrimitiveSignature::new(
686 U256::from_str(
687 "47323457007453657207889730243826965761922296599680473886588287015755652701072",
688 )
689 .unwrap(),
690 U256::from_str(
691 "57228803202727131502949358313456071280488184270258293674242124340113824882788",
692 )
693 .unwrap(),
694 false,
695 );
696
697 assert_eq!(
698 PrimitiveSignature::from_erc2098(
699 &hex!("0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b907e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064")
700 ),
701 expected
702 );
703 }
704
705 #[test]
706 fn from_erc2098_y_true() {
707 let expected = PrimitiveSignature::new(
708 U256::from_str("0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76")
709 .unwrap(),
710 U256::from_str("0x139c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793")
711 .unwrap(),
712 true,
713 );
714
715 assert_eq!(
716 PrimitiveSignature::from_erc2098(
717 &hex!("0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793")
718 ),
719 expected
720 );
721 }
722}