Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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());
}
}
Loading