use alloy_primitives::{Address, B256, Bytes, Signature, hex};
use alloy_sol_types::{SolStruct, SolType};
use super::Eip3009Payment;
use super::contract::Sig6492;
use crate::exact::TransferWithAuthorization;
const EIP6492_MAGIC_SUFFIX: [u8; 32] =
hex!("6492649264926492649264926492649264926492649264926492649264926492");
#[derive(Debug, Clone)]
pub(super) struct SignedMessage {
pub address: Address,
pub hash: B256,
pub signature: StructuredSignature,
}
impl SignedMessage {
pub(super) fn extract(
payment: &Eip3009Payment,
domain: &alloy_sol_types::Eip712Domain,
) -> Result<Self, StructuredSignatureFormatError> {
let transfer_with_authorization = TransferWithAuthorization {
from: payment.from,
to: payment.to,
value: payment.value,
validAfter: alloy_primitives::U256::from(payment.valid_after.as_secs()),
validBefore: alloy_primitives::U256::from(payment.valid_before.as_secs()),
nonce: payment.nonce,
};
let eip712_hash = transfer_with_authorization.eip712_signing_hash(domain);
let structured_signature: StructuredSignature = StructuredSignature::try_from_bytes(
payment.signature.clone(),
payment.from,
&eip712_hash,
)?;
let signed_message = Self {
address: payment.from,
hash: eip712_hash,
signature: structured_signature,
};
Ok(signed_message)
}
}
#[derive(Debug, Clone)]
pub(super) enum StructuredSignature {
EIP6492 {
factory: Address,
factory_calldata: Bytes,
inner: Bytes,
original: Bytes,
},
Eoa(Signature),
EIP1271(Bytes),
}
#[derive(Debug, thiserror::Error)]
pub enum StructuredSignatureFormatError {
#[error(transparent)]
InvalidEIP6492Format(alloy_sol_types::Error),
}
#[allow(
clippy::indexing_slicing,
reason = "bounds checked by len() >= 32 guard"
)]
fn decode_eip6492(
bytes: Bytes,
) -> Result<Option<StructuredSignature>, StructuredSignatureFormatError> {
let has_suffix = bytes.len() >= 32 && bytes[bytes.len() - 32..] == EIP6492_MAGIC_SUFFIX;
if !has_suffix {
return Ok(None);
}
let body = &bytes[..bytes.len() - 32];
let sig6492 = Sig6492::abi_decode_params(body)
.map_err(StructuredSignatureFormatError::InvalidEIP6492Format)?;
Ok(Some(StructuredSignature::EIP6492 {
factory: sig6492.factory,
factory_calldata: sig6492.factoryCalldata,
inner: sig6492.innerSig,
original: bytes,
}))
}
impl StructuredSignature {
pub(super) fn try_from_bytes(
bytes: Bytes,
expected_signer: Address,
prehash: &B256,
) -> Result<Self, StructuredSignatureFormatError> {
if let Some(eip6492) = decode_eip6492(bytes.clone())? {
return Ok(eip6492);
}
let eoa_signature = if bytes.len() == 65 {
Signature::from_raw(&bytes)
.ok()
.map(Signature::normalized_s)
} else if bytes.len() == 64 {
Some(Signature::from_erc2098(&bytes).normalized_s())
} else {
None
};
let signature = match eoa_signature {
None => Self::EIP1271(bytes),
Some(s) => {
let is_expected_signer = s
.recover_address_from_prehash(prehash)
.ok()
.is_some_and(|r| r == expected_signer);
if is_expected_signer {
Self::Eoa(s)
} else {
Self::EIP1271(bytes)
}
}
};
Ok(signature)
}
}