thegraph_core/signed_message/
signing.rs1use alloy::{
2 primitives::{Address, SignatureError},
3 signers::{
4 Error as SignerError, SignerSync, UnsupportedSignerOperation,
5 k256::ecdsa::Error as EcdsaError,
6 },
7 sol_types::{Eip712Domain, SolStruct},
8};
9
10use super::message::{SignedMessage, ToSolStruct};
11
12#[derive(Debug, thiserror::Error)]
14pub enum SigningError {
15 #[error("operation `{0}` is not supported by the signer")]
17 UnsupportedOperation(UnsupportedSignerOperation),
18
19 #[error(transparent)]
21 Ecdsa(#[from] EcdsaError),
22
23 #[error(transparent)]
25 Other(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
26}
27
28#[derive(Debug, thiserror::Error)]
30#[error(transparent)]
31pub struct RecoverSignerError(#[from] SignatureError);
32
33#[derive(Debug, thiserror::Error)]
35pub enum VerificationError {
36 #[error(transparent)]
38 SignatureError(#[from] SignatureError),
39
40 #[error("expected signer `{expected}` but received `{received}`")]
42 InvalidSigner {
43 expected: Address,
45 received: Address,
47 },
48}
49
50pub fn sign<S, M, MSol>(
56 signer: &S,
57 domain: &Eip712Domain,
58 message: M,
59) -> Result<SignedMessage<M>, SigningError>
60where
61 S: SignerSync,
62 M: ToSolStruct<MSol>,
63 MSol: SolStruct,
64{
65 let message_sol = message.to_sol_struct();
66 let signature = signer
67 .sign_typed_data_sync(&message_sol, domain)
68 .map_err(|err| match err {
69 SignerError::UnsupportedOperation(err) => SigningError::UnsupportedOperation(err),
70 SignerError::TransactionChainIdMismatch { .. } => {
71 unreachable!("sign_typed_data_sync should not return TransactionChainIdMismatch")
72 }
73 SignerError::DynAbiError(_) => {
74 unreachable!("sign_typed_data_sync should not return DynAbiError")
75 }
76 SignerError::Ecdsa(err) => SigningError::Ecdsa(err),
77 SignerError::HexError(_) => {
78 unreachable!("sign_typed_data_sync should not return HexError")
79 }
80 SignerError::SignatureError(_) => {
81 unreachable!("sign_typed_data_sync should not return SignatureError")
82 }
83 SignerError::Other(err) => SigningError::Other(err),
84 })?;
85 Ok(SignedMessage { message, signature })
86}
87
88pub fn recover_signer_address<M, MSol>(
92 domain: &Eip712Domain,
93 signed_message: &SignedMessage<M>,
94) -> Result<Address, RecoverSignerError>
95where
96 M: ToSolStruct<MSol>,
97 MSol: SolStruct,
98{
99 let message_sol = signed_message.message.to_sol_struct();
100 let recovery_message_hash = message_sol.eip712_signing_hash(domain);
101 let recovered_address = signed_message
102 .signature
103 .recover_address_from_prehash(&recovery_message_hash)?;
104 Ok(recovered_address)
105}
106
107pub fn verify<M, MSol>(
114 domain: &Eip712Domain,
115 signed_message: &SignedMessage<M>,
116 expected_address: &Address,
117) -> Result<(), VerificationError>
118where
119 M: ToSolStruct<MSol>,
120 MSol: SolStruct,
121{
122 let recovered_address =
123 recover_signer_address(domain, signed_message).map_err(|RecoverSignerError(err)| err)?;
124
125 if recovered_address != *expected_address {
126 Err(VerificationError::InvalidSigner {
127 expected: expected_address.to_owned(),
128 received: recovered_address,
129 })
130 } else {
131 Ok(())
132 }
133}