use alloy_primitives::{Address, B256, Signature, U256};
use alloy_sol_types::{Eip712Domain, SolStruct};
use super::Eip3009Payment;
use crate::exact::TransferWithAuthorization;
#[derive(Debug, Clone)]
pub(crate) struct SignedMessage {
pub address: Address,
}
impl SignedMessage {
pub(super) fn extract(
payment: &Eip3009Payment,
domain: &Eip712Domain,
) -> Result<Self, TronSignatureError> {
let transfer_with_authorization = TransferWithAuthorization {
from: payment.from,
to: payment.to,
value: payment.value,
validAfter: U256::from(payment.valid_after.as_secs()),
validBefore: U256::from(payment.valid_before.as_secs()),
nonce: payment.nonce,
};
let eip712_hash = transfer_with_authorization.eip712_signing_hash(domain);
let _ = parse_eoa_signature(&payment.signature, &eip712_hash, payment.from)?;
Ok(Self {
address: payment.from,
})
}
}
#[derive(Debug, thiserror::Error)]
pub enum TronSignatureError {
#[error("invalid signature length: {0} bytes (expected 65)")]
InvalidLength(usize),
#[error("invalid signature encoding: {0}")]
InvalidEncoding(String),
#[error("signature does not recover to the declared sender")]
SignerMismatch,
}
fn parse_eoa_signature(
bytes: &[u8],
prehash: &B256,
expected_signer: Address,
) -> Result<Signature, TronSignatureError> {
let signature = if bytes.len() == 65 {
Signature::from_raw(bytes)
.map_err(|e| TronSignatureError::InvalidEncoding(e.to_string()))?
.normalized_s()
} else if bytes.len() == 64 {
Signature::from_erc2098(bytes).normalized_s()
} else {
return Err(TronSignatureError::InvalidLength(bytes.len()));
};
let recovered = signature
.recover_address_from_prehash(prehash)
.map_err(|e| TronSignatureError::InvalidEncoding(e.to_string()))?;
if recovered != expected_signer {
return Err(TronSignatureError::SignerMismatch);
}
Ok(signature)
}