Skip to main content

xmtp_api/
scw_verifier.rs

1use crate::ApiClientWrapper;
2use xmtp_id::scw_verifier::SmartContractSignatureVerifier;
3use xmtp_id::scw_verifier::VerifierError;
4use xmtp_id::scw_verifier::{ScwInput, ValidationResponse};
5use xmtp_id::{BlockNumber, Bytes, associations::AccountId};
6use xmtp_proto::backend_v1::VerifySmartContractWalletSignaturesRequest;
7use xmtp_proto::backend_v1::VerifySmartContractWalletSignaturesResponse;
8use xmtp_proto::backend_v1::verify_smart_contract_wallet_signatures_request::Signature as VerifySmartContractWalletSignatureRequestSignature;
9use xmtp_proto::backend_v1::verify_smart_contract_wallet_signatures_response::Response as VerifySmartContractWalletSignatureResponse;
10use xmtp_proto::prelude::XmtpBackendClient;
11
12fn verdict(response: VerifySmartContractWalletSignatureResponse) -> ValidationResponse {
13    ValidationResponse {
14        is_valid: response.is_valid,
15        block_number: response.block_number,
16        error: response.error,
17    }
18}
19
20#[xmtp_common::async_trait]
21impl<C> SmartContractSignatureVerifier for ApiClientWrapper<C>
22where
23    C: XmtpBackendClient,
24{
25    /// Verifies an ERC-6492<https://eips.ethereum.org/EIPS/eip-6492> signature.
26    ///
27    /// # Arguments
28    ///
29    /// * `signer` - can be the smart wallet address or EOA address.
30    /// * `hash` - Message digest for the signature.
31    /// * `signature` - Could be encoded smart wallet signature or raw ECDSA signature.
32    async fn is_valid_signature(
33        &self,
34        account_id: AccountId,
35        hash: [u8; 32],
36        signature: Bytes,
37        block_number: Option<BlockNumber>,
38    ) -> Result<ValidationResponse, VerifierError> {
39        let result = self
40            .verify_smart_contract_wallet_signatures(VerifySmartContractWalletSignaturesRequest {
41                signatures: vec![VerifySmartContractWalletSignatureRequestSignature {
42                    account_id: account_id.into(),
43                    block_number,
44                    signature: signature.to_vec(),
45                    hash: hash.to_vec(),
46                }],
47            })
48            .await
49            .map_err(|e| VerifierError::Other(Box::new(e) as Box<_>))?;
50
51        let VerifySmartContractWalletSignaturesResponse { responses } = result;
52
53        let response =
54            responses
55                .into_iter()
56                .next()
57                .ok_or(VerifierError::Io(std::io::Error::new(
58                    std::io::ErrorKind::InvalidData,
59                    "API returned empty response for signature verification request",
60                )))?;
61        Ok(verdict(response))
62    }
63
64    /// Verify the inputs in as few backend requests as the published limits
65    /// allow, and return the verdicts in input order. The split and the
66    /// verdict-count check are those of
67    /// [`ApiClientWrapper::verify_smart_contract_wallet_signatures`]; its
68    /// error keeps its retryability.
69    async fn verify_many(
70        &self,
71        inputs: &[ScwInput],
72    ) -> Result<Vec<ValidationResponse>, VerifierError> {
73        let signatures = inputs
74            .iter()
75            .map(|input| VerifySmartContractWalletSignatureRequestSignature {
76                account_id: input.account_id().clone().into(),
77                block_number: input.block_number(),
78                signature: input.signature().to_vec(),
79                hash: input.hash().to_vec(),
80            })
81            .collect();
82        let VerifySmartContractWalletSignaturesResponse { responses } = self
83            .verify_smart_contract_wallet_signatures(VerifySmartContractWalletSignaturesRequest {
84                signatures,
85            })
86            .await
87            .map_err(|e| VerifierError::Other(Box::new(e) as Box<_>))?;
88        Ok(responses.into_iter().map(verdict).collect())
89    }
90}