Skip to content

Commit

Permalink
Merge pull request #1684 from CosmWasm/chipshort/issue1575
Browse files Browse the repository at this point in the history
Construct Coin from String
  • Loading branch information
webmaster128 authored May 22, 2023
2 parents 14864cc + 66d026b commit 45c6d98
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ and this project adheres to
- cosmwasm-vm: Add `Cache::save_wasm_unchecked` to save Wasm blobs that have
been checked before. This is useful for state-sync where we know the Wasm code
was checked when it was first uploaded. ([#1635])
- cosmwasm-std: Add `FromStr` impl for `Coin`. ([#1684])

[#1635]: https://github.com/CosmWasm/cosmwasm/pull/1635
[#1684]: https://github.com/CosmWasm/cosmwasm/pull/1684

### Changed

Expand Down
73 changes: 71 additions & 2 deletions packages/std/src/coin.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::{fmt, str::FromStr};

use crate::math::Uint128;
use crate::{errors::CoinFromStrError, math::Uint128};

#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
pub struct Coin {
Expand All @@ -19,6 +19,26 @@ impl Coin {
}
}

impl FromStr for Coin {
type Err = CoinFromStrError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let pos = s
.find(|c: char| !c.is_ascii_digit())
.ok_or(CoinFromStrError::MissingDenom)?;
let (amount, denom) = s.split_at(pos);

if amount.is_empty() {
return Err(CoinFromStrError::MissingAmount);
}

Ok(Coin {
amount: amount.parse::<u128>()?.into(),
denom: denom.to_string(),
})
}
}

impl fmt::Display for Coin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// We use the formatting without a space between amount and denom,
Expand Down Expand Up @@ -166,4 +186,53 @@ mod tests {
// less than same type
assert!(has_coins(&wallet, &coin(777, "ETH")));
}

#[test]
fn parse_coin() {
let expected = Coin::new(123, "ucosm");
assert_eq!("123ucosm".parse::<Coin>().unwrap(), expected);
// leading zeroes should be ignored
assert_eq!("00123ucosm".parse::<Coin>().unwrap(), expected);
// 0 amount parses correctly
assert_eq!("0ucosm".parse::<Coin>().unwrap(), Coin::new(0, "ucosm"));
// ibc denom should work
let ibc_str = "11111ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2";
let ibc_coin = Coin::new(
11111,
"ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
);
assert_eq!(ibc_str.parse::<Coin>().unwrap(), ibc_coin);

// error cases
assert_eq!(
Coin::from_str("123").unwrap_err(),
CoinFromStrError::MissingDenom
);
assert_eq!(
Coin::from_str("ucosm").unwrap_err(), // no amount
CoinFromStrError::MissingAmount
);
assert_eq!(
Coin::from_str("-123ucosm").unwrap_err(), // negative amount
CoinFromStrError::MissingAmount
);
assert_eq!(
Coin::from_str("").unwrap_err(), // empty input
CoinFromStrError::MissingDenom
);
assert_eq!(
Coin::from_str(" 1ucosm").unwrap_err(), // unsupported whitespace
CoinFromStrError::MissingAmount
);
assert_eq!(
Coin::from_str("�1ucosm").unwrap_err(), // other broken data
CoinFromStrError::MissingAmount
);
assert_eq!(
Coin::from_str("340282366920938463463374607431768211456ucosm")
.unwrap_err()
.to_string(),
"Invalid amount: number too large to fit in target type"
);
}
}
2 changes: 1 addition & 1 deletion packages/std/src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod verification_error;
pub use recover_pubkey_error::RecoverPubkeyError;
pub use std_error::{
CheckedFromRatioError, CheckedMultiplyFractionError, CheckedMultiplyRatioError,
ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation,
CoinFromStrError, ConversionOverflowError, DivideByZeroError, OverflowError, OverflowOperation,
RoundUpOverflowError, StdError, StdResult,
};
pub use system_error::SystemError;
Expand Down
22 changes: 22 additions & 0 deletions packages/std/src/errors/std_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,28 @@ pub enum CheckedFromRatioError {
#[error("Round up operation failed because of overflow")]
pub struct RoundUpOverflowError;

#[derive(Error, Debug, PartialEq, Eq)]
pub enum CoinFromStrError {
#[error("Missing denominator")]
MissingDenom,
#[error("Missing amount or non-digit characters in amount")]
MissingAmount,
#[error("Invalid amount: {0}")]
InvalidAmount(std::num::ParseIntError),
}

impl From<std::num::ParseIntError> for CoinFromStrError {
fn from(value: std::num::ParseIntError) -> Self {
Self::InvalidAmount(value)
}
}

impl From<CoinFromStrError> for StdError {
fn from(value: CoinFromStrError) -> Self {
Self::generic_err(format!("Parsing Coin: {}", value))
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down

0 comments on commit 45c6d98

Please sign in to comment.