mod message;
mod signing;
pub use message::{MessageHash, SignatureBytes, SignedMessage, ToSolStruct};
pub use signing::{
RecoverSignerError, SigningError, VerificationError, recover_signer_address, sign, verify,
};
#[cfg(test)]
mod tests {
use alloy::{
primitives::{Signature, address, b256, keccak256},
signers::local::PrivateKeySigner,
sol_types::{Eip712Domain, eip712_domain},
};
use super::{message::SignedMessage, signing, signing::VerificationError};
const EIP712_DOMAIN: Eip712Domain = eip712_domain! {
name: "Test domain",
version: "1",
chain_id: 1,
verifying_contract: address!("a83682bbe91c0d2d48a13fd751b2da8e989fe421"),
salt: b256!("66eb090e6dbb9668c7d32c0ee7ba5e8f08d84385804485d316dd5f5692273593")
};
alloy::sol! {
struct Message {
bytes32 data;
}
}
fn wallet() -> PrivateKeySigner {
PrivateKeySigner::random()
}
#[test]
fn sign_message_with_private_key_signer() {
let signer = wallet();
let domain = EIP712_DOMAIN;
let message = Message {
data: keccak256(b"Hello, world!"),
};
let result = signing::sign(&signer, &domain, message);
assert!(result.is_ok());
}
#[test]
fn recover_signer_from_signed_message() {
let signer = wallet();
let domain = EIP712_DOMAIN;
let message = Message {
data: keccak256(b"Hello, world!"),
};
let signed_message = signing::sign(&signer, &domain, message).unwrap();
let result = signing::recover_signer_address(&domain, &signed_message);
let signer_address = result.expect("recover_signer failed");
assert_eq!(signer_address, signer_address);
}
#[test]
fn recover_signer_should_fail_with_invalid_signature() {
let domain = EIP712_DOMAIN;
let message = Message {
data: keccak256(b"Hello, world!"),
};
let invalid_signature_signed_message = SignedMessage {
message,
signature: Signature::from_scalars_and_parity(
b256!("ca457b3f821e5c03545944e0318868a783d0e6b438c85a82537d52a619decfe2"),
b256!("26a9f36fcf89431476aa556021ee77959dc480fb3458054f26d068b52d525cc4"),
false,
),
};
let result = signing::recover_signer_address(&domain, &invalid_signature_signed_message);
assert!(result.is_err());
}
#[test]
fn verify_signed_message() {
let signer = wallet();
let signer_address = signer.address();
let domain = EIP712_DOMAIN;
let message = Message {
data: keccak256(b"Hello, world!"),
};
let signed_message = signing::sign(&signer, &domain, message).unwrap();
let result = signing::verify(&domain, &signed_message, &signer_address);
assert!(result.is_ok());
}
#[test]
fn signed_message_verification_should_fail_with_invalid_signer() {
let signer = wallet();
let domain = EIP712_DOMAIN;
let message = Message {
data: keccak256(b"Hello, world!"),
};
let signed_message = signing::sign(&signer, &domain, message).unwrap();
let different_signer = wallet();
let different_signer_address = different_signer.address();
let result = signing::verify(&domain, &signed_message, &different_signer_address);
let error = result.expect_err("verify_signature should fail");
if let VerificationError::InvalidSigner { expected, received } = error {
assert_eq!(expected, different_signer_address);
assert_eq!(received, signer.address());
} else {
panic!("unexpected error: {:?}", error);
}
}
}