Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
* [FIX][rust] `Client::prove_transaction_with` now checks that the `TransactionProver` returned a proof of the transaction it was asked to prove, rejecting a mismatch with the new `ClientError::MismatchedProvenTransaction` ([#2391](https://github.com/0xMiden/rust-sdk/pull/2391)).
* [FIX][cli] `miden-client notes --show` now prints the note sender in the `Sender` row; it was printing the note tag there ([#2412](https://github.com/0xMiden/rust-sdk/pull/2412)).
* [FIX][rust] `VerifyingRpcClient::get_account` now validates that the returned `AccountProof` belongs to the requested account ID, rejecting a mismatch with `RpcError::InvalidResponse` ([#2419](https://github.com/0xMiden/rust-sdk/pull/2419)).
* [FIX][rust] `Client::fetch_remote_token_metadata` now rejects a faucet whose token config reports more decimals than `FungibleFaucet::MAX_DECIMALS`, instead of caching the out-of-range value and rendering every balance for that faucet with it ([#2423](https://github.com/0xMiden/rust-sdk/pull/2423)).

## 0.16.0-alpha.1 (2026-07-17)

Expand Down
81 changes: 73 additions & 8 deletions crates/rust-client/src/account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,24 @@ impl Deserializable for FaucetMetadata {
}
}

/// Decodes a fungible faucet token config slot value into display metadata.
///
/// Returns `None` when the value does not describe a fungible faucet config the protocol would
/// accept: the symbol must decode as a [`TokenSymbol`], and the decimals must be within
/// [`FungibleFaucet::MAX_DECIMALS`], which is what [`FungibleFaucet`] enforces when the component
/// is built.
fn faucet_metadata_from_token_config(token_config: [Felt; 4]) -> Option<FaucetMetadata> {
let [_token_supply, _max_supply, decimals, symbol] = token_config;

let symbol = TokenSymbol::try_from(symbol).ok()?;
let decimals = u8::try_from(decimals.as_canonical_u64()).ok()?;
if decimals > FungibleFaucet::MAX_DECIMALS {
return None;
}

Some(FaucetMetadata { symbol: symbol.to_string(), decimals })
}

mod account_reader;
pub use account_reader::AccountReader;
/// Raw access to `miden-standards` account modules for items not curated by `miden-client`.
Expand Down Expand Up @@ -448,14 +466,7 @@ impl<AUTH> Client<AUTH> {
return Ok(None);
};

let [_token_supply, _max_supply, decimals, symbol] = *slot_header.value();
let Ok(symbol) = TokenSymbol::try_from(symbol) else {
return Ok(None);
};
let Ok(decimals) = u8::try_from(decimals.as_canonical_u64()) else {
return Ok(None);
};
Ok(Some(FaucetMetadata { symbol: symbol.to_string(), decimals }))
Ok(faucet_metadata_from_token_config(*slot_header.value()))
}

/// Adds an [`Address`] to the associated [`AccountId`], alongside its derived [`NoteTag`]. If
Expand Down Expand Up @@ -694,3 +705,57 @@ mod schema_commitment_tests {
assert_ne!(commitment, EMPTY_WORD);
}
}

#[cfg(test)]
mod faucet_metadata_tests {
use miden_protocol::Felt;

use super::{FungibleFaucet, TokenSymbol, faucet_metadata_from_token_config};

/// Builds a token config slot value carrying the given decimals and the symbol "TST".
fn token_config(decimals: u32) -> [Felt; 4] {
[
Felt::from(0u32),
Felt::from(0u32),
Felt::from(decimals),
TokenSymbol::new("TST").unwrap().as_element(),
]
}

#[test]
fn decodes_a_config_within_the_protocol_bounds() {
let metadata = faucet_metadata_from_token_config(token_config(8)).unwrap();

assert_eq!(metadata.symbol, "TST");
assert_eq!(metadata.decimals, 8);
}

#[test]
fn accepts_the_maximum_supported_decimals() {
let max = u32::from(FungibleFaucet::MAX_DECIMALS);
let metadata = faucet_metadata_from_token_config(token_config(max)).unwrap();

assert_eq!(metadata.decimals, FungibleFaucet::MAX_DECIMALS);
}

#[test]
fn rejects_decimals_above_the_maximum() {
let above_max = u32::from(FungibleFaucet::MAX_DECIMALS) + 1;

assert!(faucet_metadata_from_token_config(token_config(above_max)).is_none());
assert!(faucet_metadata_from_token_config(token_config(200)).is_none());
}

#[test]
fn rejects_decimals_that_do_not_fit_a_u8() {
assert!(faucet_metadata_from_token_config(token_config(300)).is_none());
}

#[test]
fn rejects_a_symbol_that_is_not_a_token_symbol() {
let mut config = token_config(8);
config[3] = Felt::from(0u32);

assert!(faucet_metadata_from_token_config(config).is_none());
}
}