Skip to content
Merged
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 @@ -135,6 +135,7 @@
* [FIX][cli] `miden-client account --default none` now reports whether a default account was actually removed instead of always printing that it was. Removing an absent setting also no longer panics in debug builds ([#2439](https://github.com/0xMiden/rust-sdk/pull/2439)).
* [FIX][cli] `miden-client address remove` now reports whether the address was actually removed instead of always printing that it was being removed.
* [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)).
* [FIX][rust] On wasm32 the node RPC and note transport gRPC clients now apply the configured request timeout instead of silently ignoring it, so a request whose response never arrives fails with `deadline_exceeded` rather than hanging forever. Long-lived note streams are exempt, as the fetch-level timeout would abort a stream that is still delivering updates ([#2452](https://github.com/0xMiden/rust-sdk/pull/2452)).
* [FIX][rust] `TransactionRequestBuilder::build_mint_fungible_asset` now rejects a zero-amount asset with `TransactionRequestError::P2IDNoteWithoutAsset`, matching `build_pay_to_id`; both emit a P2ID note, and minting nothing produced one the target could draw nothing from ([#2457](https://github.com/0xMiden/rust-sdk/pull/2457)).
* [FIX][rust] `InputNoteReader::next` now fails with `ClientError::MissingNoteConsumptionPosition` when the store yields a note that carries no consumption position. The walk cannot advance past such a note, and it previously restarted from the first note on every subsequent call ([#2364](https://github.com/0xMiden/rust-sdk/pull/2364)).
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 @@ -695,3 +706,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