1use alloy::primitives::{Address, B256, TxHash, U256};
6use alloy::providers::Provider;
7use alloy::sol_types::SolEvent;
8
9use crate::config::{self, assert_transfer_contract_configured};
10use crate::error::SdkError;
11use crate::events::TransferWithCommitmentSent;
12use crate::utils::assert_transfer_contract_deployed;
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct VerifyArgs {
17 pub from: Address,
19 pub token: Address,
21 pub to: Address,
23 pub value: U256,
25 pub commitment: B256,
27}
28
29pub async fn get_transfer_with_commitment_sent_event_logs<P: Provider>(
49 public_provider: &P,
50 hash: TxHash,
51) -> Result<Vec<TransferWithCommitmentSent>, SdkError> {
52 assert_transfer_contract_configured()?;
53 assert_transfer_contract_deployed(public_provider).await?;
54 let receipt = public_provider
55 .get_transaction_receipt(hash)
56 .await?
57 .ok_or(SdkError::ReceiptNotFound)?;
58 let mut out = Vec::new();
59 for log in receipt.logs() {
60 if log.address() != config::transfer_with_commitment_address() {
61 continue;
62 }
63 if let Ok(d) = TransferWithCommitmentSent::decode_log(&log.inner) {
64 out.push(d.data);
65 }
66 }
67 Ok(out)
68}
69
70pub async fn verify<P: Provider>(
90 public_provider: &P,
91 hash: TxHash,
92 args: &VerifyArgs,
93) -> Result<(), SdkError> {
94 assert_transfer_contract_configured()?;
95 assert_transfer_contract_deployed(public_provider).await?;
96 let receipt = public_provider
97 .get_transaction_receipt(hash)
98 .await?
99 .ok_or(SdkError::ReceiptNotFound)?;
100 for log in receipt.logs() {
101 if log.address() != config::transfer_with_commitment_address() {
102 continue;
103 }
104 let Ok(wrapped) = TransferWithCommitmentSent::decode_log(&log.inner) else {
105 continue;
106 };
107 let ev = wrapped.data;
108 if ev.from == args.from
109 && ev.token == args.token
110 && ev.to == args.to
111 && ev.value == args.value
112 && ev.commitment == args.commitment
113 {
114 return Ok(());
115 }
116 }
117 Err(SdkError::EventNotFound)
118}
119
120#[cfg(all(test, not(feature = "test-config")))]
121mod tests {
122 use alloy::primitives::{address, B256, TxHash, U256};
123 use alloy::providers::ProviderBuilder;
124 use serde_json::json;
125 use wiremock::{matchers::method, Mock, MockServer, ResponseTemplate};
126
127 use super::{get_transfer_with_commitment_sent_event_logs, verify, VerifyArgs};
128
129 async fn mock_rpc_chain_and_code(chain_id_hex: &str, code: &str) -> MockServer {
130 let server = MockServer::start().await;
131 let cid = chain_id_hex.to_string();
132 let code = code.to_string();
133 Mock::given(method("POST"))
134 .respond_with(move |req: &wiremock::Request| {
135 let body: serde_json::Value =
136 serde_json::from_slice(&req.body).unwrap_or(json!({}));
137 let id = body.get("id").cloned().unwrap_or(json!(1));
138 let m = body.get("method").and_then(|x| x.as_str()).unwrap_or("");
139 let result = match m {
140 "eth_chainId" => json!(cid.clone()),
141 "eth_getCode" => json!(code.clone()),
142 _ => json!(null),
143 };
144 ResponseTemplate::new(200).set_body_json(json!({
145 "jsonrpc": "2.0",
146 "id": id,
147 "result": result
148 }))
149 })
150 .mount(&server)
151 .await;
152 server
153 }
154
155 fn sample_verify_args() -> VerifyArgs {
156 let a = address!("0x1111111111111111111111111111111111111111");
157 VerifyArgs {
158 from: a,
159 token: a,
160 to: a,
161 value: U256::from(1u64),
162 commitment: B256::repeat_byte(0xaa),
163 }
164 }
165
166 #[tokio::test]
167 async fn get_logs_fails_when_twc_not_deployed() {
168 let server = mock_rpc_chain_and_code("0x1", "0x").await;
169 let provider = ProviderBuilder::new().connect_http(server.uri().parse().unwrap());
170 let hash = TxHash::repeat_byte(0xab);
171 let r = get_transfer_with_commitment_sent_event_logs(&provider, hash).await;
172 assert!(matches!(
173 r,
174 Err(crate::SdkError::ContractNotDeployed { .. })
175 ));
176 }
177
178 #[tokio::test]
179 async fn verify_fails_when_twc_not_deployed() {
180 let server = mock_rpc_chain_and_code("0x1", "0x").await;
181 let provider = ProviderBuilder::new().connect_http(server.uri().parse().unwrap());
182 let hash = TxHash::repeat_byte(0xab);
183 let args = sample_verify_args();
184 let r = verify(&provider, hash, &args).await;
185 assert!(matches!(
186 r,
187 Err(crate::SdkError::ContractNotDeployed { .. })
188 ));
189 }
190}