Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Bug Fixes

* [fix][rust] `tokens_to_base_units` now returns `TokenParseError::AmountTooLarge` instead of `TokenParseError::ParseU64` when the combined integer+fractional string overflows `u64::MAX` ([#2506](https://github.com/0xMiden/rust-sdk/pull/2506)).

### Breaking Changes

* [BREAKING][removal][rust] Removed `Client::try_get_account`. Use `Client::get_account` and handle the `None` case, or `Client::account_reader` for existence checks and single-field reads that don't need the full materialized account ([#2362](https://github.com/0xMiden/rust-sdk/pull/2362)).
Expand Down
19 changes: 16 additions & 3 deletions crates/rust-client/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ pub enum TokenParseError {
ParseU64(#[source] ParseIntError),
#[error("Amount has more than {0} decimal places")]
TooManyDecimals(u8),
#[error("Amount is too large to fit in a u64")]
AmountTooLarge,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid mentioning u64 here and instead we should just say the amount is too large. InvalidAmount already covers the asset amount not being representable

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't fully get why we would need to add this error type, considering ParseU64(#[source] ParseIntError) already has in its inner error the specifics of the failure (overflow in this case). If we are to add it because we don't want the user to see u64 then we should take the extra effort and remove ParseU64 altogether.

#[error("Amount is not a valid asset amount")]
InvalidAmount(#[source] AssetError),
}
Expand All @@ -75,7 +77,9 @@ pub fn tokens_to_base_units(
return Err(TokenParseError::MultipleDecimalPoints);
}

// Validate that the parts are valid numbers
// Validate that the parts contain only valid digits (catches non-numeric chars).
// Note: each part is validated individually, so a large combined value is NOT
// caught here — that case is handled by the AmountTooLarge error below.
for part in &parts {
part.parse::<u64>().map_err(TokenParseError::ParseU64)?;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still reports overflow as ParseU64 when the integer part itself is too large. For example, a very large number fails here and never reaches the AmountTooLarge mapping below. I think we should check the error kind here too: map IntErrorKind::PosOverflow to AmountTooLarge, and keep ParseU64(err) for everything else.

Expand Down Expand Up @@ -103,8 +107,10 @@ pub fn tokens_to_base_units(
// Combine the integer and padded fractional part
let combined = format!("{}{}", integer_part, &fractional_part[0..n_decimals.into()]);

// Convert the combined string to an integer
let units = combined.parse::<u64>().map_err(TokenParseError::ParseU64)?;
// Convert the combined string to an integer; a parse failure here means the
// combined value overflows u64, not that it contains invalid digits (those
// are already caught by the split/trim logic above).
let units = combined.parse::<u64>().map_err(|_| TokenParseError::AmountTooLarge)?;

AssetAmount::new(units).map_err(TokenParseError::InvalidAmount)
}
Expand Down Expand Up @@ -147,6 +153,13 @@ mod tests {
tokens_to_base_units("18446744.073709551615", 12),
Err(TokenParseError::InvalidAmount(_))
),);
// Combined string overflows u64 entirely — must return AmountTooLarge,
// not a confusing ParseU64 error.
// "184467440737095516.16" with 2 decimals → "18446744073709551616" = u64::MAX + 1
assert!(matches!(
tokens_to_base_units("184467440737095516.16", 2),
Err(TokenParseError::AmountTooLarge)
));
}

#[test]
Expand Down
Loading