use alloy::{
primitives::{Address, SignatureError},
signers::{
Error as SignerError, SignerSync, UnsupportedSignerOperation,
k256::ecdsa::Error as EcdsaError,
},
sol_types::{Eip712Domain, SolStruct},
};
use super::message::{SignedMessage, ToSolStruct};
#[derive(Debug, thiserror::Error)]
pub enum SigningError {
#[error("operation `{0}` is not supported by the signer")]
UnsupportedOperation(UnsupportedSignerOperation),
#[error(transparent)]
Ecdsa(#[from] EcdsaError),
#[error(transparent)]
Other(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
}
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct RecoverSignerError(#[from] SignatureError);
#[derive(Debug, thiserror::Error)]
pub enum VerificationError {
#[error(transparent)]
SignatureError(#[from] SignatureError),
#[error("expected signer `{expected}` but received `{received}`")]
InvalidSigner {
expected: Address,
received: Address,
},
}
pub fn sign<S, M, MSol>(
signer: &S,
domain: &Eip712Domain,
message: M,
) -> Result<SignedMessage<M>, SigningError>
where
S: SignerSync,
M: ToSolStruct<MSol>,
MSol: SolStruct,
{
let message_sol = message.to_sol_struct();
let signature = signer
.sign_typed_data_sync(&message_sol, domain)
.map_err(|err| match err {
SignerError::UnsupportedOperation(err) => SigningError::UnsupportedOperation(err),
SignerError::TransactionChainIdMismatch { .. } => {
unreachable!("sign_typed_data_sync should not return TransactionChainIdMismatch")
}
SignerError::DynAbiError(_) => {
unreachable!("sign_typed_data_sync should not return DynAbiError")
}
SignerError::Ecdsa(err) => SigningError::Ecdsa(err),
SignerError::HexError(_) => {
unreachable!("sign_typed_data_sync should not return HexError")
}
SignerError::SignatureError(_) => {
unreachable!("sign_typed_data_sync should not return SignatureError")
}
SignerError::Other(err) => SigningError::Other(err),
})?;
Ok(SignedMessage { message, signature })
}
pub fn recover_signer_address<M, MSol>(
domain: &Eip712Domain,
signed_message: &SignedMessage<M>,
) -> Result<Address, RecoverSignerError>
where
M: ToSolStruct<MSol>,
MSol: SolStruct,
{
let message_sol = signed_message.message.to_sol_struct();
let recovery_message_hash = message_sol.eip712_signing_hash(domain);
let recovered_address = signed_message
.signature
.recover_address_from_prehash(&recovery_message_hash)?;
Ok(recovered_address)
}
pub fn verify<M, MSol>(
domain: &Eip712Domain,
signed_message: &SignedMessage<M>,
expected_address: &Address,
) -> Result<(), VerificationError>
where
M: ToSolStruct<MSol>,
MSol: SolStruct,
{
let recovered_address =
recover_signer_address(domain, signed_message).map_err(|RecoverSignerError(err)| err)?;
if recovered_address != *expected_address {
Err(VerificationError::InvalidSigner {
expected: expected_address.to_owned(),
received: recovered_address,
})
} else {
Ok(())
}
}