use alloy::primitives::{Address, FixedBytes, eip191_hash_message};
use alloy::providers::ProviderBuilder;
use alloy::sol;
use siwx::{SiwxError, SiwxMessage};
use crate::{format_message, parse_address};
const EIP1271_MAGIC: FixedBytes<4> = FixedBytes([0x16, 0x26, 0xBA, 0x7E]);
sol! {
#[sol(rpc)]
contract IERC1271 {
function isValidSignature(bytes32 hash, bytes signature) external view returns (bytes4 magicValue);
}
}
#[derive(Debug)]
pub struct Eip1271Verifier {
rpc_url: String,
}
impl Eip1271Verifier {
#[must_use]
pub fn new(rpc_url: impl Into<String>) -> Self {
Self {
rpc_url: rpc_url.into(),
}
}
pub(crate) async fn verify_inner(
&self,
message: &SiwxMessage,
signature: &[u8],
) -> Result<(), SiwxError> {
let contract_addr: Address = parse_address(&message.address)?;
let text = format_message(message);
let hash = eip191_hash_message(text.as_bytes());
let provider = ProviderBuilder::new()
.connect(&self.rpc_url)
.await
.map_err(|e| SiwxError::VerificationFailed(format!("RPC connect failed: {e}")))?;
let contract = IERC1271::new(contract_addr, provider);
let magic: FixedBytes<4> = contract
.isValidSignature(hash, signature.to_vec().into())
.call()
.await
.map_err(|e| {
SiwxError::VerificationFailed(format!("isValidSignature call failed: {e}"))
})?;
if magic != EIP1271_MAGIC {
return Err(SiwxError::VerificationFailed(format!(
"EIP-1271 magic mismatch: expected {EIP1271_MAGIC}, got {magic}"
)));
}
Ok(())
}
}
impl siwx::Verifier for Eip1271Verifier {
async fn verify(&self, message: &SiwxMessage, signature: &[u8]) -> Result<(), SiwxError> {
self.verify_inner(message, signature).await
}
}