cap_sdk_core/
index.rs

1//! Contains logic for interacting with cap's index canister.
2//!
3//! For more information on the purpose of a index canister, see the documentation on
4//!['Index`].
5
6use crate::root::RootBucket;
7use crate::router::Router;
8use cap_common::{
9    GetIndexCanistersResponse, GetTokenContractRootBucketArg, GetTokenContractRootBucketResponse,
10    GetUserRootBucketsArg, GetUserRootBucketsResponse, WithWitnessArg,
11};
12use ic_kit::ic::call;
13use ic_kit::{Principal, RejectionCode};
14use thiserror::Error;
15
16/// A Cap index canister.
17#[derive(Clone)]
18pub struct Index(Principal);
19
20impl Index {
21    /// Creates a new index from the given [`Principal`]
22    pub fn new(principal: Principal) -> Self {
23        Self(principal)
24    }
25
26    /// Returns the root bucket canister associated with the given token contract.
27    pub async fn get_token_contract_root_bucket(
28        &self,
29        contract: Principal,
30    ) -> Result<RootBucket, GetContractRootError> {
31        let result: (GetTokenContractRootBucketResponse,) = call(
32            self.0,
33            "get_token_contract_root_bucket",
34            (GetTokenContractRootBucketArg {
35                canister: contract,
36                witness: false,
37            },),
38        )
39        .await
40        .map_err(|err| GetContractRootError::Rejected(err.0, err.1))?;
41
42        if let Some(canister) = result.0.canister {
43            Ok(RootBucket(canister))
44        } else {
45            Err(GetContractRootError::InvalidContract)
46        }
47    }
48
49    /// Returns all roots for contracts a user has transactions on.
50    pub async fn get_user_root_buckets(
51        &self,
52        user: Principal,
53    ) -> Result<Vec<RootBucket>, (RejectionCode, String)> {
54        let result: (GetUserRootBucketsResponse,) = call(
55            self.0,
56            "get_user_root_buckets",
57            (GetUserRootBucketsArg {
58                user,
59                witness: false,
60            },),
61        )
62        .await?;
63
64        Ok(result
65            .0
66            .contracts
67            .iter()
68            .map(|canister| RootBucket(*canister))
69            .collect())
70    }
71
72    /// Returns the list of router canisters that can be used for querying the indexes.
73    pub async fn get_router_canisters(&self) -> Result<Vec<Router>, (RejectionCode, String)> {
74        let result: (GetIndexCanistersResponse,) = call(
75            self.0,
76            "get_router_canisters",
77            (WithWitnessArg { witness: false },),
78        )
79        .await?;
80
81        Ok(result
82            .0
83            .canisters
84            .iter()
85            .map(|canister| Router(*canister))
86            .collect())
87    }
88}
89
90impl From<Router> for Index {
91    fn from(router: Router) -> Self {
92        Index(router.0)
93    }
94}
95
96/// An error thrown when querying for a contract's root bucket.
97#[derive(Error, Debug)]
98pub enum GetContractRootError {
99    /// The bucket rejected the call.
100    #[error("the query was rejected (TODO: Display here)")]
101    Rejected(RejectionCode, String),
102    /// There is no root bucket for the given contract.
103    #[error("no root found for the given contract")]
104    InvalidContract,
105}