use alloy_primitives::keccak256;
use alloy_signer::Signer;
use alloy_sol_types::SolStruct;
use crate::evm::EvmChainError;
use crate::evm::abi::SpendIntent;
use crate::proto::v1;
pub const TRIDENT_DOMAIN_SEPARATOR: alloy_primitives::B256 =
alloy_primitives::b256!("c3e730f724f9f24a93c08888f26f828931ae0eeeb4f19c5469c4e56049b5b567");
const EIP712_PREFIX: [u8; 2] = [0x19, 0x01];
pub async fn sign_spend_intent(
signer: &(impl Signer + Sync + ?Sized),
intent: v1::SpendIntent,
) -> Result<Vec<u8>, EvmChainError> {
let abi_intent: SpendIntent = intent.try_into()?;
let struct_hash = abi_intent.eip712_hash_struct();
let digest = keccak256(
[
EIP712_PREFIX.as_slice(),
TRIDENT_DOMAIN_SEPARATOR.as_slice(),
struct_hash.as_slice(),
]
.concat(),
);
let signature = signer
.sign_hash(&digest)
.await
.map_err(EvmChainError::Signer)?;
Ok(signature.as_bytes().to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::{Address, U256};
use alloy_signer_local::PrivateKeySigner;
fn sample_proto_intent(src_spender: Address) -> v1::SpendIntent {
v1::SpendIntent {
spec: Some(v1::SpendSpec {
src_spender: Some(src_spender.into()),
src_domain: 1,
src_token: Some(Address::from([0x02; 20]).into()),
src_deposit_contract: Some(Address::from([0x03; 20]).into()),
dst_domain: 2,
dst_token: Some(Address::from([0x04; 20]).into()),
dst_delivery_contract: Some(Address::from([0x05; 20]).into()),
dst_recipient: Some(Address::from([0x06; 20]).into()),
dst_caller: Some(Address::from([0x07; 20]).into()),
amount: Some(U256::from(1000u64).into()),
salt: Some([0xAA; 32].into()),
}),
max_fee: Some(U256::from(10u64).into()),
expiry_height: 1000,
}
}
#[tokio::test]
async fn sign_spend_intent_success() {
let signer: PrivateKeySigner =
"0x0123456789012345678901234567890123456789012345678901234567890123"
.parse()
.unwrap();
let intent = sample_proto_intent(signer.address());
let signature = sign_spend_intent(&signer, intent).await.unwrap();
assert_eq!(signature.len(), 65);
}
}