Skip to main content

miden_client/account/
account_reader.rs

1//! Provides lazy access to account data.
2
3use alloc::sync::Arc;
4use alloc::vec::Vec;
5
6use miden_protocol::account::{
7    AccountHeader,
8    AccountId,
9    StorageMapKey,
10    StorageMapWitness,
11    StorageSlotName,
12};
13use miden_protocol::address::Address;
14use miden_protocol::asset::{Asset, AssetVaultKey};
15use miden_protocol::{Felt, Word};
16
17use crate::errors::ClientError;
18use crate::store::{AccountStatus, Store};
19
20/// Provides lazy access to account data.
21///
22/// `AccountReader` executes queries lazily - each method call fetches fresh data
23/// from storage, ensuring you always see the current state.
24///
25/// # Example
26/// ```ignore
27/// let reader = client.account_reader(account_id);
28///
29/// // Each call fetches fresh data
30/// let nonce = reader.nonce().await?;
31/// let status = reader.status().await?;
32/// let commitment = reader.commitment().await?;
33///
34/// // Vault access
35/// let balance = reader.get_balance(faucet_id).await?;
36///
37/// // Storage access
38/// let value = reader.get_storage_item("my_slot").await?;
39/// ```
40pub struct AccountReader {
41    store: Arc<dyn Store>,
42    account_id: AccountId,
43}
44
45impl AccountReader {
46    /// Creates a new `AccountReader` for the given account.
47    pub fn new(store: Arc<dyn Store>, account_id: AccountId) -> Self {
48        Self { store, account_id }
49    }
50
51    /// Returns the account ID (fixed at construction).
52    pub fn account_id(&self) -> AccountId {
53        self.account_id
54    }
55
56    // HEADER ACCESS
57    // --------------------------------------------------------------------------------------------
58
59    /// Retrieves the current account nonce.
60    pub async fn nonce(&self) -> Result<Felt, ClientError> {
61        let (header, _) = self.header().await?;
62        Ok(header.nonce())
63    }
64
65    /// Retrieves the account commitment (hash of the full state).
66    pub async fn commitment(&self) -> Result<Word, ClientError> {
67        let (header, _) = self.header().await?;
68        Ok(header.to_commitment())
69    }
70
71    /// Retrieves the storage commitment (root of the storage tree).
72    pub async fn storage_commitment(&self) -> Result<Word, ClientError> {
73        let (header, _) = self.header().await?;
74        Ok(header.storage_commitment())
75    }
76
77    /// Retrieves the vault root (root of the asset vault tree).
78    pub async fn vault_root(&self) -> Result<Word, ClientError> {
79        let (header, _) = self.header().await?;
80        Ok(header.vault_root())
81    }
82
83    /// Retrieves the code commitment (hash of the account code).
84    pub async fn code_commitment(&self) -> Result<Word, ClientError> {
85        let (header, _) = self.header().await?;
86        Ok(header.code_commitment())
87    }
88
89    /// Retrieves the current account status (New, Tracked, or Locked).
90    pub async fn status(&self) -> Result<AccountStatus, ClientError> {
91        let (_, status) = self.header().await?;
92        Ok(status)
93    }
94
95    /// Retrieves the account header and status.
96    pub async fn header(&self) -> Result<(AccountHeader, AccountStatus), ClientError> {
97        self.store
98            .get_account_header(self.account_id)
99            .await?
100            .ok_or(ClientError::AccountDataNotFound(self.account_id))
101    }
102
103    /// Retrieves the addresses associated with this account.
104    pub async fn addresses(&self) -> Result<Vec<Address>, ClientError> {
105        self.store
106            .get_addresses_by_account_id(self.account_id)
107            .await
108            .map_err(ClientError::StoreError)
109    }
110
111    // VAULT ACCESS
112    // --------------------------------------------------------------------------------------------
113
114    /// Retrieves the balance of a fungible asset in the account's vault.
115    ///
116    /// Returns `0` if the asset is not present in the vault or if the asset is not a fungible
117    /// asset.
118    ///
119    /// To load the entire vault, use
120    /// [`Client::get_account_vault`](crate::Client::get_account_vault).
121    pub async fn get_balance(&self, faucet_id: AccountId) -> Result<u64, ClientError> {
122        if let Some(vault_key) = AssetVaultKey::from_account_id(faucet_id)
123            && let Some((Asset::Fungible(fungible_asset), _)) =
124                self.store.get_account_asset(self.account_id, vault_key).await?
125        {
126            return Ok(fungible_asset.amount());
127        }
128        Ok(0)
129    }
130
131    // STORAGE ACCESS
132    // --------------------------------------------------------------------------------------------
133
134    /// Retrieves a storage slot value by name.
135    ///
136    /// This method fetches the requested slot from storage.
137    ///
138    /// For `Value` slots, returns the stored word.
139    /// For `Map` slots, returns the map root.
140    pub async fn get_storage_item(
141        &self,
142        slot_name: impl Into<StorageSlotName>,
143    ) -> Result<Word, ClientError> {
144        self.store
145            .get_account_storage_item(self.account_id, slot_name.into())
146            .await
147            .map_err(ClientError::StoreError)
148    }
149
150    /// Retrieves a value from a storage map slot by name and key.
151    ///
152    /// This method fetches only the requested slot from storage.
153    ///
154    /// # Errors
155    /// Returns an error if the slot is not found or is not a map.
156    pub async fn get_storage_map_item(
157        &self,
158        slot_name: impl Into<StorageSlotName>,
159        key: StorageMapKey,
160    ) -> Result<Word, ClientError> {
161        let (value, _witness) =
162            self.store.get_account_map_item(self.account_id, slot_name.into(), key).await?;
163        Ok(value)
164    }
165
166    /// Retrieves a value and its Merkle witness from a storage map slot.
167    ///
168    /// This method fetches the requested slot from storage and it's inclusion proof.
169    ///
170    /// # Errors
171    /// Returns an error if the slot is not found or is not a map.
172    pub async fn get_storage_map_witness(
173        &self,
174        slot_name: impl Into<StorageSlotName>,
175        key: StorageMapKey,
176    ) -> Result<(Word, StorageMapWitness), ClientError> {
177        self.store
178            .get_account_map_item(self.account_id, slot_name.into(), key)
179            .await
180            .map_err(ClientError::StoreError)
181    }
182}