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}