Skip to content

Commit

Permalink
Implement FromStr for the unchecked types
Browse files Browse the repository at this point in the history
  • Loading branch information
larry0x committed Feb 12, 2022
1 parent 354e83e commit 4189af3
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 5 deletions.
60 changes: 58 additions & 2 deletions src/asset.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;

use cosmwasm_std::{
to_binary, Addr, Api, BankMsg, Binary, Coin, CosmosMsg, StdError, StdResult, Uint128, WasmMsg,
Expand All @@ -9,7 +10,7 @@ use cw20::Cw20ExecuteMsg;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::asset_info::{AssetInfo, AssetInfoBase};
use super::asset_info::{AssetInfo, AssetInfoBase, AssetInfoUnchecked};

/// Represents a fungible asset with a known amount
///
Expand Down Expand Up @@ -85,6 +86,28 @@ pub type AssetUnchecked = AssetBase<String>;
// Represents an **asset** instance containing only verified data; to be saved in contract storage
pub type Asset = AssetBase<Addr>;

impl FromStr for AssetUnchecked {
type Err = StdError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let words: Vec<&str> = s.split(":").collect();
if words.len() != 3 {
return Err(StdError::generic_err(
format!("invalid asset format `{}`; must be in format `native:{{denom}}:{{amount}}` or `cw20:{{contract_addr}}:{{amount}}`", s)
));
}

let info = AssetInfoUnchecked::from_str(&format!("{}:{}", words[0], words[1]))?;
let amount = Uint128::from_str(words[2]).map_err(
|_| StdError::generic_err(
format!("invalid asset amount `{}`; must be a 128-bit unsigned integer", words[2])
)
)?;

Ok(AssetUnchecked { info, amount })
}
}

impl From<Asset> for AssetUnchecked {
fn from(asset: Asset) -> Self {
AssetUnchecked {
Expand Down Expand Up @@ -441,7 +464,40 @@ mod tests {
}

#[test]
fn displaying() {
fn from_string() {
let s = "native:uusd:12345:67890";
assert_eq!(
AssetUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset format `native:uusd:12345:67890`; must be in format `native:{denom}:{amount}` or `cw20:{contract_addr}:{amount}`")),
);

let s = "cw721:galactic_punk:1";
assert_eq!(
AssetUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset type `cw721`; must be `native` or `cw20`")),
);

let s = "native:uusd:ngmi";
assert_eq!(
AssetUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset amount `ngmi`; must be a 128-bit unsigned integer")),
);

let s = "native:uusd:12345";
assert_eq!(
AssetUnchecked::from_str(s).unwrap(),
AssetUnchecked::native("uusd", 12345u128),
);

let s = "cw20:mock_token:12345";
assert_eq!(
AssetUnchecked::from_str(s).unwrap(),
AssetUnchecked::cw20("mock_token", 12345u128),
);
}

#[test]
fn to_string() {
let asset = Asset::native("uusd", 69420u128);
assert_eq!(asset.to_string(), String::from("native:uusd:69420"));

Expand Down
51 changes: 50 additions & 1 deletion src/asset_info.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt;
use std::str::FromStr;

use cosmwasm_std::{
to_binary, Addr, Api, BalanceResponse, BankQuery, QuerierWrapper, QueryRequest, StdError,
Expand Down Expand Up @@ -56,6 +57,27 @@ pub type AssetInfoUnchecked = AssetInfoBase<String>;
/// Represents an **asset info** instance containing only verified data; to be saved in contract storage
pub type AssetInfo = AssetInfoBase<Addr>;

impl FromStr for AssetInfoUnchecked {
type Err = StdError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let words: Vec<&str> = s.split(":").collect();
if words.len() != 2 {
return Err(StdError::generic_err(
format!("invalid asset info format `{}`; must be in format `native:{{denom}}` or `cw20:{{contract_addr}}`", s)
));
}

match words[0] {
"native" => Ok(AssetInfoUnchecked::Native(String::from(words[1]))),
"cw20" => Ok(AssetInfoUnchecked::Cw20(String::from(words[1]))),
ty => Err(StdError::generic_err(
format!("invalid asset type `{}`; must be `native` or `cw20`", ty)
))
}
}
}

impl From<AssetInfo> for AssetInfoUnchecked {
fn from(asset_info: AssetInfo) -> Self {
match &asset_info {
Expand Down Expand Up @@ -260,7 +282,34 @@ mod test {
}

#[test]
fn displaying() {
fn from_string() {
let s = "native:uusd:12345";
assert_eq!(
AssetInfoUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset info format `native:uusd:12345`; must be in format `native:{denom}` or `cw20:{contract_addr}`")),
);

let s = "cw721:galactic_punk";
assert_eq!(
AssetInfoUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset type `cw721`; must be `native` or `cw20`")),
);

let s = "native:uusd";
assert_eq!(
AssetInfoUnchecked::from_str(s).unwrap(),
AssetInfoUnchecked::native("uusd"),
);

let s = "cw20:mock_token";
assert_eq!(
AssetInfoUnchecked::from_str(s).unwrap(),
AssetInfoUnchecked::cw20("mock_token"),
);
}

#[test]
fn to_string() {
let info = AssetInfo::native("uusd");
assert_eq!(info.to_string(), String::from("native:uusd"));

Expand Down
51 changes: 49 additions & 2 deletions src/asset_list.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fmt;
use std::str::FromStr;
#[cfg(feature = "legacy")]
use std::convert::TryInto;

Expand All @@ -7,7 +8,7 @@ use cosmwasm_std::{Addr, Api, Coin, CosmosMsg, StdError, StdResult};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use super::asset::{Asset, AssetBase};
use super::asset::{Asset, AssetBase, AssetUnchecked};
use super::asset_info::AssetInfo;

/// Represents a list of fungible tokens, each with a known amount
Expand All @@ -26,6 +27,25 @@ pub type AssetListUnchecked = AssetListBase<String>;
/// Represents an **asset list** instance containing only verified data; to be used in contract storage
pub type AssetList = AssetListBase<Addr>;

impl FromStr for AssetListUnchecked {
type Err = StdError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() == 0 {
return Ok(Self(vec![]));
}

Ok(Self(
s
.split(",")
.collect::<Vec<&str>>()
.iter()
.map(|s| AssetUnchecked::from_str(s))
.collect::<Result<Vec<AssetUnchecked>, Self::Err>>()?
))
}
}

impl From<AssetList> for AssetListUnchecked {
fn from(list: AssetList) -> Self {
Self(list.to_vec().iter().cloned().map(|asset| asset.into()).collect())
Expand Down Expand Up @@ -462,7 +482,34 @@ mod tests {
use cw20::Cw20ExecuteMsg;

#[test]
fn displaying() {
fn from_string() {
let s = "";
assert_eq!(AssetListUnchecked::from_str(s).unwrap(), AssetListBase::<String>(vec![]));

let s = "native:uusd:69420,cw20:mock_token";
assert_eq!(
AssetListUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset format `cw20:mock_token`; must be in format `native:{denom}:{amount}` or `cw20:{contract_addr}:{amount}`")),
);

let s = "native:uusd:69420,cw721:galactic_punk:1";
assert_eq!(
AssetListUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset type `cw721`; must be `native` or `cw20`")),
);

let s = "native:uusd:69420,cw20:mock_token:ngmi";
assert_eq!(
AssetListUnchecked::from_str(s),
Err(StdError::generic_err("invalid asset amount `ngmi`; must be a 128-bit unsigned integer")),
);

let s = "native:uusd:69420,cw20:mock_token:88888";
assert_eq!(AssetListUnchecked::from_str(s).unwrap(), AssetListUnchecked::from(mock_list()));
}

#[test]
fn to_string() {
let list = mock_list();
assert_eq!(list.to_string(), String::from("native:uusd:69420,cw20:mock_token:88888"));
}
Expand Down

0 comments on commit 4189af3

Please sign in to comment.