Skip to main content

iota_sdk_graphql_client/api/
balance.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2026 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! Balance API implementation.
6
7use cynic::QueryBuilder;
8use iota_types::{Address, StructTag};
9
10use crate::{
11    GraphQLClient,
12    api::define_query,
13    error::GraphQLResult,
14    query_types::{BalanceArgs, BalanceQueryFragment},
15};
16
17define_query! {
18    /// Query for [`GraphQLClient::balance`]. Await it to send the request.
19    pub struct GetBalanceQuery {
20        client: GraphQLClient,
21        address: Address,
22        coin_type: Option<StructTag>,
23    }
24    output: GraphQLResult<Option<u64>>;
25}
26
27impl GetBalanceQuery {
28    /// Set the coin type. Defaults to `0x2::iota::IOTA`.
29    pub fn coin_type(mut self, coin_type: StructTag) -> Self {
30        self.coin_type = Some(coin_type);
31        self
32    }
33
34    async fn send(self) -> GraphQLResult<Option<u64>> {
35        let operation = BalanceQueryFragment::build(BalanceArgs {
36            address: self.address,
37            coin_type: self.coin_type.map(|t| t.to_string()),
38        });
39        let response = self.client.run_query(&operation).await?;
40
41        let total_balance = response
42            .owner
43            .and_then(|o| o.balance.and_then(|b| b.total_balance))
44            .map(|x| x.0.parse::<u64>())
45            .transpose()?;
46        Ok(total_balance)
47    }
48}
49
50impl GraphQLClient {
51    /// Get the balance of all the coins owned by address.
52    pub fn balance(&self, address: Address) -> GetBalanceQuery {
53        GetBalanceQuery {
54            client: self.clone(),
55            address,
56            coin_type: None,
57        }
58    }
59}
60
61#[cfg(all(test, not(target_arch = "wasm32")))]
62mod tests {
63    use iota_types::{Address, StructTag};
64
65    use crate::test_utils::{sent_variables, test_client};
66
67    #[tokio::test]
68    async fn balance_sends_the_address_and_coin_type() {
69        let vars = sent_variables("BalanceQueryFragment", |client| async move {
70            let _ = client
71                .balance(Address::STD)
72                .coin_type(StructTag::new_gas())
73                .await;
74        })
75        .await;
76        assert_eq!(vars["address"], Address::STD.to_string());
77        assert_eq!(vars["coinType"], "0x2::iota::IOTA");
78
79        let vars = sent_variables("BalanceQueryFragment", |client| async move {
80            let _ = client.balance(Address::STD).await;
81        })
82        .await;
83        assert!(vars["coinType"].is_null());
84    }
85
86    #[tokio::test]
87    async fn test_balance_query() {
88        let client = test_client();
89        client
90            .balance(Address::STD)
91            .await
92            .map_err(|e| {
93                format!(
94                    "Balance query failed for {} network: Error: {e}",
95                    client.rpc_server()
96                )
97            })
98            .unwrap();
99    }
100}