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