Skip to main content

circles_rpc/methods/
invitation.rs

1use crate::client::RpcClient;
2use crate::error::Result;
3use circles_types::{
4    Address, AllInvitationsResponse, Balance, InvitationOriginResponse, InvitationsFromResponse,
5};
6use futures::pin_mut;
7use futures::stream::{self, StreamExt};
8
9/// Methods for invitation discovery and balance lookups.
10///
11/// `get_invitations` fetches inviters then batches `circles_getInvitationBalance`
12/// with bounded concurrency to avoid hammering the RPC.
13#[derive(Clone, Debug)]
14pub struct InvitationMethods {
15    client: RpcClient,
16}
17
18/// Invitation + balance row returned by `get_invitations`.
19#[derive(Debug, serde::Deserialize)]
20pub struct InvitationRow {
21    /// The address that sent the invitation.
22    pub inviter: Address,
23    /// The invitee address queried.
24    pub invitee: Address,
25    /// Balance available for the invitee from this inviter.
26    pub invitation_balance: Balance,
27}
28
29impl InvitationMethods {
30    pub fn new(client: RpcClient) -> Self {
31        Self { client }
32    }
33
34    /// `circles_getInvitationOrigin` — reconstruct how an avatar joined Circles.
35    pub async fn get_invitation_origin(
36        &self,
37        address: Address,
38    ) -> Result<Option<InvitationOriginResponse>> {
39        self.client
40            .call("circles_getInvitationOrigin", (address,))
41            .await
42    }
43
44    /// TS parity helper: return only the direct inviter address when present.
45    pub async fn get_invited_by(&self, address: Address) -> Result<Option<Address>> {
46        Ok(self
47            .get_invitation_origin(address)
48            .await?
49            .and_then(|origin| origin.inviter))
50    }
51
52    /// `circles_getAllInvitations` — return trust, escrow, and at-scale invitations.
53    pub async fn get_all_invitations(
54        &self,
55        address: Address,
56        minimum_balance: Option<String>,
57    ) -> Result<AllInvitationsResponse> {
58        match minimum_balance {
59            Some(minimum_balance) => {
60                self.client
61                    .call("circles_getAllInvitations", (address, minimum_balance))
62                    .await
63            }
64            None => {
65                self.client
66                    .call("circles_getAllInvitations", (address,))
67                    .await
68            }
69        }
70    }
71
72    /// circles_getInvitations — batches balance lookups concurrently per invitee.
73    pub async fn get_invitations(&self, invitee: Address) -> Result<Vec<InvitationRow>> {
74        // Fetch inviters list first.
75        let inviters: Vec<Address> = self
76            .client
77            .call("circles_getInvitations", (invitee,))
78            .await?;
79
80        // Concurrently fetch balances with bounded concurrency to avoid hammering the RPC.
81        const MAX_CONCURRENT: usize = 10;
82        let client = self.client.clone();
83        let invitee_addr = invitee;
84        let stream = stream::iter(inviters.into_iter().map(move |inviter| {
85            let client = client.clone();
86            async move {
87                let bal: Balance = client
88                    .call("circles_getInvitationBalance", (inviter, invitee_addr))
89                    .await?;
90                Ok::<_, crate::error::CirclesRpcError>(InvitationRow {
91                    inviter,
92                    invitee: invitee_addr,
93                    invitation_balance: bal,
94                })
95            }
96        }))
97        .buffer_unordered(MAX_CONCURRENT);
98
99        let mut rows = Vec::new();
100        pin_mut!(stream);
101        while let Some(res) = stream.next().await {
102            rows.push(res?);
103        }
104        Ok(rows)
105    }
106
107    /// `circles_getInvitationsFrom` — accepted or pending invitees for an inviter.
108    pub async fn get_invitations_from(
109        &self,
110        address: Address,
111        accepted: bool,
112    ) -> Result<InvitationsFromResponse> {
113        self.client
114            .call("circles_getInvitationsFrom", (address, accepted))
115            .await
116    }
117}