1use alloy::primitives::Address;
4use alloy::providers::Provider;
5use alloy::sol_types::Eip712Domain;
6
7use crate::config::{
8 self, assert_transfer_contract_configured, EIP712_DOMAIN_NAME, EIP712_DOMAIN_VERSION,
9};
10use crate::error::SdkError;
11
12#[inline]
14pub fn chain_id_matches(domain_chain_id: alloy::primitives::U256, client_chain_id: u64) -> bool {
15 domain_chain_id == alloy::primitives::U256::from(client_chain_id)
16}
17
18pub async fn assert_transfer_contract_deployed<P: Provider>(
20 provider: &P,
21) -> Result<(), SdkError> {
22 assert_transfer_contract_configured()?;
23 let addr = config::transfer_with_commitment_address();
24 let chain_id = provider.get_chain_id().await?;
25 let code = provider.get_code_at(addr).await?;
26 if code.is_empty() {
27 return Err(SdkError::ContractNotDeployed {
28 address: addr,
29 chain_id,
30 });
31 }
32 Ok(())
33}
34
35#[inline]
37pub async fn assert_provider_supported_chain<P: Provider>(
38 provider: &P,
39) -> Result<(), SdkError> {
40 assert_transfer_contract_deployed(provider).await
41}
42
43pub fn assert_onchain_eip712_domain_matches_config(
45 domain: &Eip712Domain,
46 provider_chain_id: u64,
47) -> Result<(), SdkError> {
48 let twc = config::transfer_with_commitment_address();
49 let vc = domain.verifying_contract.ok_or_else(|| {
50 SdkError::Alloy("eip712Domain: verifyingContract missing".into())
51 })?;
52 if vc != twc {
53 return Err(SdkError::DomainVerifyingContractMismatch {
54 domain: vc,
55 configured: twc,
56 });
57 }
58 let domain_chain = domain
59 .chain_id
60 .ok_or_else(|| SdkError::Alloy("eip712Domain: chainId missing".into()))?;
61 if !chain_id_matches(domain_chain, provider_chain_id) {
62 return Err(SdkError::DomainChainIdMismatch {
63 domain: domain_chain.to_string(),
64 client: provider_chain_id,
65 });
66 }
67 let name_ok = domain
68 .name
69 .as_ref()
70 .map(|n| n.as_ref() == EIP712_DOMAIN_NAME)
71 .unwrap_or(false);
72 if !name_ok {
73 return Err(SdkError::Alloy(format!(
74 "eip712Domain: unexpected name (expected {EIP712_DOMAIN_NAME})"
75 )));
76 }
77 let ver_ok = domain
78 .version
79 .as_ref()
80 .map(|v| v.as_ref() == EIP712_DOMAIN_VERSION)
81 .unwrap_or(false);
82 if !ver_ok {
83 return Err(SdkError::Alloy(format!(
84 "eip712Domain: unexpected version (expected {EIP712_DOMAIN_VERSION})"
85 )));
86 }
87 Ok(())
88}
89
90pub fn assert_signed_domain_matches_client_and_config(
92 client_chain_id: u64,
93 domain_chain_id: alloy::primitives::U256,
94 domain_verifying_contract: Address,
95 configured_contract: Address,
96) -> Result<(), SdkError> {
97 if !chain_id_matches(domain_chain_id, client_chain_id) {
98 return Err(SdkError::DomainChainIdMismatch {
99 domain: domain_chain_id.to_string(),
100 client: client_chain_id,
101 });
102 }
103 if domain_verifying_contract != configured_contract {
104 return Err(SdkError::DomainVerifyingContractMismatch {
105 domain: domain_verifying_contract,
106 configured: configured_contract,
107 });
108 }
109 Ok(())
110}
111
112#[inline]
114pub fn assert_signed_domain_matches_provider_and_config(
115 provider_chain_id: u64,
116 domain_chain_id: alloy::primitives::U256,
117 domain_verifying_contract: Address,
118) -> Result<(), SdkError> {
119 assert_signed_domain_matches_client_and_config(
120 provider_chain_id,
121 domain_chain_id,
122 domain_verifying_contract,
123 config::transfer_with_commitment_address(),
124 )
125}
126
127#[cfg(test)]
128mod tests {
129 use alloy::primitives::{address, U256};
130
131 use super::*;
132
133 #[test]
134 fn chain_id_matches_u256_and_u64() {
135 assert!(chain_id_matches(U256::from(1u64), 1));
136 assert!(chain_id_matches(U256::from(137u64), 137));
137 assert!(!chain_id_matches(U256::from(137u64), 1));
138 }
139
140 #[test]
141 fn assert_signed_domain_matches_ok_when_ids_and_contract_match() {
142 let vc = address!("0x2222222222222222222222222222222222222222");
143 assert!(assert_signed_domain_matches_client_and_config(
144 1,
145 U256::from(1u64),
146 vc,
147 vc,
148 )
149 .is_ok());
150 }
151
152 #[test]
153 fn assert_signed_domain_matches_fails_domain_chain_id_mismatch() {
154 let vc = address!("0x2222222222222222222222222222222222222222");
155 assert!(matches!(
156 assert_signed_domain_matches_client_and_config(1, U256::from(137u64), vc, vc),
157 Err(SdkError::DomainChainIdMismatch { .. })
158 ));
159 }
160
161 #[test]
162 fn assert_signed_domain_matches_fails_verifying_contract_mismatch() {
163 let a = address!("0x2222222222222222222222222222222222222222");
164 let b = address!("0x4444444444444444444444444444444444444444");
165 assert!(matches!(
166 assert_signed_domain_matches_client_and_config(1, U256::from(1u64), a, b),
167 Err(SdkError::DomainVerifyingContractMismatch { .. })
168 ));
169 }
170}