Skip to main content

xmtp_proto/
api_client.rs

1pub use super::xmtp::message_api::v1::{
2    BatchQueryRequest, BatchQueryResponse, Envelope, PublishRequest, PublishResponse, QueryRequest,
3    QueryResponse, SubscribeRequest,
4};
5use crate::api::IsConnectedCheck;
6use crate::mls_v1::{
7    BatchPublishCommitLogRequest, BatchQueryCommitLogRequest, BatchQueryCommitLogResponse,
8    GetNewestGroupMessageRequest, PagingInfo,
9};
10use crate::types::{
11    Cursor, GroupId, GroupMessage, GroupMessageMetadata, InstallationId, TopicCursor,
12    WelcomeMessage,
13};
14use crate::xmtp::identity::api::v1::{
15    GetIdentityUpdatesRequest as GetIdentityUpdatesV2Request,
16    GetIdentityUpdatesResponse as GetIdentityUpdatesV2Response, GetInboxIdsRequest,
17    GetInboxIdsResponse, PublishIdentityUpdateRequest, VerifySmartContractWalletSignaturesRequest,
18    VerifySmartContractWalletSignaturesResponse,
19};
20use crate::xmtp::mls::api::v1::{
21    FetchKeyPackagesRequest, FetchKeyPackagesResponse, GroupMessage as ProtoGroupMessage,
22    QueryWelcomeMessagesResponse, SendGroupMessagesRequest, SendWelcomeMessagesRequest,
23    UploadKeyPackageRequest, WelcomeMessage as ProtoWelcomeMessage,
24};
25use futures::Stream;
26use std::pin::Pin;
27use std::sync::Arc;
28use xmtp_common::{MaybeSend, MaybeSync};
29use xmtp_common::{Retry, RetryableError};
30
31mod impls;
32mod stats;
33pub use stats::*;
34
35xmtp_common::if_test! {
36    mod tests;
37    pub use tests::*;
38}
39
40/// A type-erased version of the Xmtp Api in a [`Box`]
41pub type BoxedXmtpApi<Error> = Box<dyn BoxableXmtpApi<Error>>;
42/// A type-erased version of the Xntp Api in a [`Arc`]
43pub type ArcedXmtpApi<Error> = Arc<dyn BoxableXmtpApi<Error>>;
44
45xmtp_common::if_native! {
46    pub type BoxedGroupS<Err> = Pin<Box<dyn Stream<Item = Result<GroupMessage, Err>> + Send>>;
47    pub type BoxedWelcomeS<Err> = Pin<Box<dyn Stream<Item = Result<WelcomeMessage, Err>> + Send>>;
48}
49
50xmtp_common::if_wasm! {
51    pub type BoxedGroupS<Err> = Pin<Box<dyn Stream<Item = Result<GroupMessage, Err>>>>;
52    pub type BoxedWelcomeS<Err> = Pin<Box<dyn Stream<Item = Result<WelcomeMessage, Err>>>>;
53}
54
55pub trait BoxableXmtpApi<Err>
56where
57    Self: XmtpMlsClient<Error = Err>
58        + XmtpIdentityClient<Error = Err>
59        + XmtpMlsStreams<
60            Error = Err,
61            WelcomeMessageStream = BoxedWelcomeS<Err>,
62            GroupMessageStream = BoxedGroupS<Err>,
63        > + IsConnectedCheck
64        + MaybeSend
65        + MaybeSync,
66{
67}
68
69impl<T, Err> BoxableXmtpApi<Err> for T where
70    T: XmtpMlsClient<Error = Err>
71        + XmtpIdentityClient<Error = Err>
72        + XmtpMlsStreams<
73            Error = Err,
74            WelcomeMessageStream = BoxedWelcomeS<Err>,
75            GroupMessageStream = BoxedGroupS<Err>,
76        > + IsConnectedCheck
77        + MaybeSend
78        + MaybeSync
79        + ?Sized
80{
81}
82
83pub trait XmtpApi
84where
85    Self: XmtpMlsClient + XmtpIdentityClient,
86{
87}
88
89impl<T> XmtpApi for T where T: XmtpMlsClient + XmtpIdentityClient + ?Sized {}
90
91/// Trait which for protobuf-generated type
92/// which can be paged.
93/// Paged implementation indicates a response
94/// that returns a collection of envelopes
95pub trait Paged: MaybeSend + MaybeSync {
96    type Message: MaybeSend + MaybeSync;
97    fn info(&self) -> &Option<PagingInfo>;
98    fn messages(self) -> Vec<Self::Message>;
99}
100
101/// Represents the backend API required for an MLS Delivery Service
102/// to be compatible with XMTP
103#[xmtp_common::async_trait]
104pub trait XmtpMlsClient: MaybeSend + MaybeSync {
105    type Error: RetryableError + MaybeSend + MaybeSync + 'static;
106    async fn upload_key_package(&self, request: UploadKeyPackageRequest)
107    -> Result<(), Self::Error>;
108    async fn fetch_key_packages(
109        &self,
110        request: FetchKeyPackagesRequest,
111    ) -> Result<FetchKeyPackagesResponse, Self::Error>;
112    async fn send_group_messages(
113        &self,
114        request: SendGroupMessagesRequest,
115    ) -> Result<(), Self::Error>;
116    async fn send_welcome_messages(
117        &self,
118        request: SendWelcomeMessagesRequest,
119    ) -> Result<(), Self::Error>;
120    async fn query_group_messages(
121        &self,
122        group_id: crate::types::GroupId,
123    ) -> Result<Vec<GroupMessage>, Self::Error>;
124    async fn query_latest_group_message(
125        &self,
126        group_id: crate::types::GroupId,
127    ) -> Result<Option<GroupMessage>, Self::Error>;
128    async fn query_welcome_messages(
129        &self,
130        installation_key: InstallationId,
131    ) -> Result<Vec<WelcomeMessage>, Self::Error>;
132    async fn publish_commit_log(
133        &self,
134        request: BatchPublishCommitLogRequest,
135    ) -> Result<(), Self::Error>;
136    async fn query_commit_log(
137        &self,
138        request: BatchQueryCommitLogRequest,
139    ) -> Result<BatchQueryCommitLogResponse, Self::Error>;
140    async fn get_newest_group_message(
141        &self,
142        request: GetNewestGroupMessageRequest,
143    ) -> Result<Vec<Option<GroupMessageMetadata>>, Self::Error>;
144}
145
146/// Represents the backend API required for an MLS Delivery Service
147/// to be compatible with XMTP streaming
148#[xmtp_common::async_trait]
149pub trait XmtpMlsStreams: MaybeSend + MaybeSync {
150    type GroupMessageStream: Stream<Item = Result<GroupMessage, Self::Error>> + MaybeSend;
151
152    type WelcomeMessageStream: Stream<Item = Result<WelcomeMessage, Self::Error>> + MaybeSend;
153
154    type Error: RetryableError + 'static;
155
156    async fn subscribe_group_messages(
157        &self,
158        group_ids: &[&GroupId],
159    ) -> Result<Self::GroupMessageStream, Self::Error>;
160    async fn subscribe_group_messages_with_cursors(
161        &self,
162        groups_with_cursors: &TopicCursor,
163    ) -> Result<Self::GroupMessageStream, Self::Error>;
164    async fn subscribe_welcome_messages(
165        &self,
166        installations: &[&InstallationId],
167    ) -> Result<Self::WelcomeMessageStream, Self::Error>;
168}
169
170xmtp_common::if_native! {
171    /// The XIP-83 bidirectional subscription: one long-lived stream carrying
172    /// group and welcome messages, mutated in place (no reconnect on membership
173    /// change) and kept alive with WebSocket-style ping/pong. Native-only —
174    /// gRPC-Web transports cannot speak full-duplex, so browsers stay on
175    /// [`XmtpMlsStreams`] with a client-side watchdog.
176    #[xmtp_common::async_trait]
177    pub trait XmtpMlsBidiStreams: MaybeSend + MaybeSync {
178        type SubscribeStream: Stream<Item = Result<crate::mls_v1::SubscribeResponse, Self::Error>>
179            + MaybeSend;
180
181        type Error: RetryableError + 'static;
182
183        /// Open the bidirectional stream. `requests` is the outbound
184        /// client→server frame stream (typically fed by a channel; the first
185        /// frame is usually a `Mutate` naming the initial topic set); the
186        /// returned stream yields the server→client frames.
187        async fn subscribe_bidi(
188            &self,
189            requests: futures::stream::BoxStream<'static, crate::mls_v1::SubscribeRequest>,
190        ) -> Result<Self::SubscribeStream, Self::Error>;
191    }
192}
193
194/// Represents the backend API required for the XMTP
195/// Identity Service described by [XIP-46 Multi-Wallet Identity](https://github.com/xmtp/XIPs/blob/main/XIPs/xip-46-multi-wallet-identity.md)
196#[xmtp_common::async_trait]
197pub trait XmtpIdentityClient: MaybeSend + MaybeSync {
198    type Error: RetryableError + MaybeSend + MaybeSync + 'static;
199    async fn publish_identity_update(
200        &self,
201        request: PublishIdentityUpdateRequest,
202    ) -> Result<Option<Cursor>, Self::Error>;
203
204    async fn get_identity_updates_v2(
205        &self,
206        request: GetIdentityUpdatesV2Request,
207    ) -> Result<GetIdentityUpdatesV2Response, Self::Error>;
208
209    async fn get_inbox_ids(
210        &self,
211        request: GetInboxIdsRequest,
212    ) -> Result<GetInboxIdsResponse, Self::Error>;
213
214    async fn verify_smart_contract_wallet_signatures(
215        &self,
216        request: VerifySmartContractWalletSignaturesRequest,
217    ) -> Result<VerifySmartContractWalletSignaturesResponse, Self::Error>;
218}
219
220/// describe how to create a single network
221/// connection.
222/// Implement this trait if your type connects to a single
223/// network channel/connection (like gRPc or HTTP)
224pub trait NetConnectConfig: ApiBuilder + MaybeSend + MaybeSync {
225    /// set the libxmtp version (required)
226    fn set_libxmtp_version(&mut self, version: String) -> Result<(), Self::Error>;
227
228    /// set the sdk app version (required)
229    fn set_app_version(&mut self, version: crate::types::AppVersion) -> Result<(), Self::Error>;
230
231    /// Set the libxmtp host (required)
232    fn set_host(&mut self, host: url::Url);
233
234    /// Set the retry strategy for this client
235    fn set_retry(&mut self, retry: Retry);
236
237    /// Set the rate limit per minute for this client
238    fn rate_per_minute(&mut self, limit: u32);
239
240    /// The port this api builder is using
241    fn port(&self) -> Result<Option<String>, Self::Error>;
242
243    /// Host of the builder
244    fn host(&self) -> Option<&str>;
245}
246
247/// Build an API from its parts for the XMTP Backend
248pub trait ApiBuilder: MaybeSend + MaybeSync {
249    type Output: MaybeSend + MaybeSync;
250    type Error: MaybeSend + MaybeSync + std::fmt::Debug;
251    /// Build the api client
252    fn build(self) -> Result<Self::Output, Self::Error>;
253}