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}