diff --git a/src/asset.rs b/src/asset.rs index 0823db7..d3e1705 100644 --- a/src/asset.rs +++ b/src/asset.rs @@ -1,9 +1,7 @@ use std::{convert::TryFrom, fmt, str::FromStr}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ - to_binary, Addr, Api, BankMsg, Binary, Coin, CosmosMsg, StdError, StdResult, Uint128, WasmMsg, -}; +use cosmwasm_std::{to_binary, Addr, Api, BankMsg, Binary, Coin, CosmosMsg, Uint128, WasmMsg}; use cw20::Cw20ExecuteMsg; use cw_address_like::AddressLike; @@ -175,7 +173,11 @@ impl AssetUnchecked { /// } /// } /// ``` - pub fn check(&self, api: &dyn Api, optional_whitelist: Option<&[&str]>) -> Result { + pub fn check( + &self, + api: &dyn Api, + optional_whitelist: Option<&[&str]>, + ) -> Result { Ok(Asset { info: self.info.check(api, optional_whitelist)?, amount: self.amount, @@ -205,23 +207,24 @@ impl From<&Coin> for Asset { } impl TryFrom for Coin { - type Error = StdError; + type Error = AssetError; + fn try_from(asset: Asset) -> Result { match &asset.info { AssetInfo::Native(denom) => Ok(Coin { denom: denom.clone(), amount: asset.amount, }), - AssetInfo::Cw20(_) => Err(StdError::generic_err(format!( - "cannot cast asset {} into cosmwasm_std::Coin", - asset - ))), + AssetInfo::Cw20(_) => Err(AssetError::CannotCastToStdCoin { + asset: asset.to_string(), + }), } } } impl TryFrom<&Asset> for Coin { - type Error = StdError; + type Error = AssetError; + fn try_from(asset: &Asset) -> Result { Coin::try_from(asset.clone()) } @@ -258,20 +261,22 @@ impl Asset { /// MockCommand {}, /// } /// - /// use cosmwasm_std::{to_binary, Addr, Response, StdResult}; - /// use cw_asset::Asset; + /// use cosmwasm_std::{to_binary, Addr, Response}; + /// use cw_asset::{Asset, AssetError}; /// /// fn send_asset( /// asset: &Asset, /// contract_addr: &Addr, /// msg: &MockReceiveMsg, - /// ) -> StdResult { + /// ) -> Result { /// let msg = asset.send_msg(contract_addr, to_binary(msg)?)?; /// - /// Ok(Response::new().add_message(msg).add_attribute("asset_sent", asset.to_string())) + /// Ok(Response::new() + /// .add_message(msg) + /// .add_attribute("asset_sent", asset.to_string())) /// } /// ``` - pub fn send_msg>(&self, to: A, msg: Binary) -> StdResult { + pub fn send_msg>(&self, to: A, msg: Binary) -> Result { match &self.info { AssetInfo::Cw20(contract_addr) => Ok(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: contract_addr.into(), @@ -282,9 +287,9 @@ impl Asset { })?, funds: vec![], })), - AssetInfo::Native(_) => { - Err(StdError::generic_err("native coins do not have `send` method")) - }, + AssetInfo::Native(_) => Err(AssetError::UnavailableMethodForNative { + method: "send".into(), + }), } } @@ -292,16 +297,18 @@ impl Asset { /// specified account. /// /// ```rust - /// use cosmwasm_std::{Addr, Response, StdResult}; - /// use cw_asset::Asset; + /// use cosmwasm_std::{Addr, Response}; + /// use cw_asset::{Asset, AssetError}; /// - /// fn transfer_asset(asset: &Asset, recipient_addr: &Addr) -> StdResult { + /// fn transfer_asset(asset: &Asset, recipient_addr: &Addr) -> Result { /// let msg = asset.transfer_msg(recipient_addr)?; /// - /// Ok(Response::new().add_message(msg).add_attribute("asset_sent", asset.to_string())) + /// Ok(Response::new() + /// .add_message(msg) + /// .add_attribute("asset_sent", asset.to_string())) /// } /// ``` - pub fn transfer_msg>(&self, to: A) -> StdResult { + pub fn transfer_msg>(&self, to: A) -> Result { match &self.info { AssetInfo::Native(denom) => Ok(CosmosMsg::Bank(BankMsg::Send { to_address: to.into(), @@ -329,20 +336,26 @@ impl Asset { /// equivalent method implemented. /// /// ```rust - /// use cosmwasm_std::{Addr, Response, StdResult}; - /// use cw_asset::Asset; + /// use cosmwasm_std::{Addr, Response}; + /// use cw_asset::{Asset, AssetError}; /// - /// fn draw_asset(asset: &Asset, user_addr: &Addr, contract_addr: &Addr) -> StdResult { + /// fn draw_asset( + /// asset: &Asset, + /// user_addr: &Addr, + /// contract_addr: &Addr, + /// ) -> Result { /// let msg = asset.transfer_from_msg(user_addr, contract_addr)?; /// - /// Ok(Response::new().add_message(msg).add_attribute("asset_drawn", asset.to_string())) + /// Ok(Response::new() + /// .add_message(msg) + /// .add_attribute("asset_drawn", asset.to_string())) /// } /// ``` pub fn transfer_from_msg, B: Into>( &self, from: A, to: B, - ) -> StdResult { + ) -> Result { match &self.info { AssetInfo::Cw20(contract_addr) => Ok(CosmosMsg::Wasm(WasmMsg::Execute { contract_addr: contract_addr.into(), @@ -353,9 +366,9 @@ impl Asset { })?, funds: vec![], })), - AssetInfo::Native(_) => { - Err(StdError::generic_err("native coins do not have `transfer_from` method")) - }, + AssetInfo::Native(_) => Err(AssetError::UnavailableMethodForNative { + method: "transfer_from".into(), + }), } } } @@ -366,7 +379,7 @@ impl Asset { #[cfg(test)] mod tests { - use cosmwasm_std::testing::MockApi; + use cosmwasm_std::{testing::MockApi, StdError}; use serde::Serialize; use super::*; @@ -421,15 +434,15 @@ mod tests { let astro = Asset::cw20(Addr::unchecked("astro_token"), 69u128); assert_eq!( Coin::try_from(&astro), - Err(StdError::generic_err( - "cannot cast asset cw20:astro_token:69 into cosmwasm_std::Coin" - )), + Err(AssetError::CannotCastToStdCoin { + asset: "cw20:astro_token:69".into(), + }), ); assert_eq!( Coin::try_from(astro), - Err(StdError::generic_err( - "cannot cast asset cw20:astro_token:69 into cosmwasm_std::Coin" - )), + Err(AssetError::CannotCastToStdCoin { + asset: "cw20:astro_token:69".into(), + }), ); } @@ -607,7 +620,12 @@ mod tests { ); let err = coin.send_msg("mock_contract", bin_msg); - assert_eq!(err, Err(StdError::generic_err("native coins do not have `send` method"))); + assert_eq!( + err, + Err(AssetError::UnavailableMethodForNative { + method: "send".into(), + }), + ); let msg = token.transfer_msg("alice").unwrap(); assert_eq!( @@ -649,7 +667,9 @@ mod tests { let err = coin.transfer_from_msg("bob", "charlie"); assert_eq!( err, - Err(StdError::generic_err("native coins do not have `transfer_from` method")) + Err(AssetError::UnavailableMethodForNative { + method: "transfer_from".into(), + }), ); } } diff --git a/src/asset_list.rs b/src/asset_list.rs index e0bd153..cf5b773 100644 --- a/src/asset_list.rs +++ b/src/asset_list.rs @@ -1,7 +1,7 @@ use std::{fmt, str::FromStr}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Api, Coin, CosmosMsg, StdError, StdResult}; +use cosmwasm_std::{Addr, Api, Coin, CosmosMsg, StdResult}; use cw_address_like::AddressLike; use crate::{Asset, AssetBase, AssetError, AssetInfo, AssetUnchecked}; @@ -377,16 +377,15 @@ impl AssetList { /// /// let len = list.len(); // should be zero, as uluna is purged from the list /// ``` - pub fn deduct(&mut self, asset_to_deduct: &Asset) -> StdResult<&mut Self> { + pub fn deduct(&mut self, asset_to_deduct: &Asset) -> Result<&mut Self, AssetError> { match self.0.iter_mut().find(|asset| asset.info == asset_to_deduct.info) { Some(asset) => { asset.amount = asset.amount.checked_sub(asset_to_deduct.amount)?; }, None => { - return Err(StdError::generic_err(format!( - "not found in asset list: {}", - asset_to_deduct.info - ))); + return Err(AssetError::NotFoundInList { + info: asset_to_deduct.info.to_string(), + }); }, } Ok(self.purge()) @@ -414,7 +413,7 @@ impl AssetList { /// /// let len = list.len(); // should be zero, as uusd is purged from the list /// ``` - pub fn deduct_many(&mut self, assets_to_deduct: &AssetList) -> StdResult<&mut Self> { + pub fn deduct_many(&mut self, assets_to_deduct: &AssetList) -> Result<&mut Self, AssetError> { for asset in &assets_to_deduct.0 { self.deduct(asset)?; } @@ -424,20 +423,28 @@ impl AssetList { /// Generate a transfer messages for every asset in the list /// /// ```rust - /// use cosmwasm_std::{Addr, Response, StdResult}; - /// use cw_asset::AssetList; + /// use cosmwasm_std::{Addr, Response}; + /// use cw_asset::{AssetError, AssetList}; /// - /// fn transfer_assets(list: &AssetList, recipient_addr: &Addr) -> StdResult { + /// fn transfer_assets( + /// list: &AssetList, + /// recipient_addr: &Addr, + /// ) -> Result { /// let msgs = list.transfer_msgs(recipient_addr)?; /// - /// Ok(Response::new().add_messages(msgs).add_attribute("assets_sent", list.to_string())) + /// Ok(Response::new() + /// .add_messages(msgs) + /// .add_attribute("assets_sent", list.to_string())) /// } /// ``` - pub fn transfer_msgs + Clone>(&self, to: A) -> StdResult> { + pub fn transfer_msgs + Clone>( + &self, + to: A, + ) -> Result, AssetError> { self.0 .iter() .map(|asset| asset.transfer_msg(to.clone())) - .collect::>>() + .collect() } } @@ -447,7 +454,8 @@ impl AssetList { #[cfg(test)] mod test_helpers { - use super::{super::asset::Asset, *}; + use super::*; + use crate::Asset; pub fn uluna() -> AssetInfo { AssetInfo::native("uluna") @@ -473,7 +481,7 @@ mod test_helpers { mod tests { use cosmwasm_std::{ testing::MockApi, to_binary, BankMsg, Coin, CosmosMsg, Decimal, OverflowError, - OverflowOperation, Uint128, WasmMsg, + OverflowOperation, StdError, Uint128, WasmMsg, }; use cw20::Cw20ExecuteMsg; @@ -637,7 +645,12 @@ mod tests { assert_eq!(asset_option, None); let err = list.deduct(&Asset::new(uusd(), 57075u128)); - assert_eq!(err, Err(StdError::generic_err("not found in asset list: native:uusd"))); + assert_eq!( + err, + Err(AssetError::NotFoundInList { + info: "native:uusd".into(), + }), + ); list.deduct(&Asset::new(mock_token(), 12345u128)).unwrap(); let asset = list.find(&mock_token()).unwrap(); @@ -646,11 +659,12 @@ mod tests { let err = list.deduct(&Asset::new(mock_token(), 99999u128)); assert_eq!( err, - Err(StdError::overflow(OverflowError::new( + Err(OverflowError::new( OverflowOperation::Sub, Uint128::new(76543), Uint128::new(99999) - ))) + ) + .into()), ); } diff --git a/src/error.rs b/src/error.rs index e68827e..800331a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,11 +1,14 @@ -use cosmwasm_std::StdError; +use cosmwasm_std::{StdError, OverflowError}; use thiserror::Error; #[derive(Error, Debug, PartialEq)] pub enum AssetError { - #[error("{0}")] + #[error("std error encountered while handling assets: {0}")] Std(#[from] StdError), + #[error("overflow error encountered while handling assets: {0}")] + Overflow(#[from] OverflowError), + #[error("invalid asset type `{ty}`; must be either `native` or `cw20`")] InvalidAssetType { ty: String, @@ -40,4 +43,19 @@ pub enum AssetError { denom: String, whitelist: String, }, + + #[error("asset `{info}` is not found in asset list")] + NotFoundInList { + info: String, + }, + + #[error("native coins do not have the `{method}` method")] + UnavailableMethodForNative { + method: String, + }, + + #[error("cannot cast asset {asset} to cosmwasm_std::Coin")] + CannotCastToStdCoin { + asset: String, + }, } diff --git a/src/lib.rs b/src/lib.rs index da192c4..34ac57d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,10 +7,10 @@ //! The following code generates messages the sends some SDK coins and CW20 tokens to a recipient: //! //! ```rust -//! use cosmwasm_std::{Api, Response, StdResult}; -//! use cw_asset::Asset; +//! use cosmwasm_std::{Api, Response}; +//! use cw_asset::{Asset, AssetError}; //! -//! fn transfer_two_assets(api: &dyn Api) -> StdResult { +//! fn transfer_two_assets(api: &dyn Api) -> Result { //! let asset1 = Asset::native("uusd", 12345u128); //! let msg1 = asset1.transfer_msg("recipient_addr")?; //! @@ -30,10 +30,10 @@ //! An [`AssetList`] struct is also provided for dealing with multiple assets at the same time: //! //! ```rust -//! use cosmwasm_std::{Api, Response, StdResult}; -//! use cw_asset::{Asset, AssetList}; +//! use cosmwasm_std::{Api, Response}; +//! use cw_asset::{Asset, AssetError, AssetList}; //! -//! fn transfer_multiple_assets(api: &dyn Api) -> StdResult { +//! fn transfer_multiple_assets(api: &dyn Api) -> Result { //! let assets = AssetList::from(vec![ //! Asset::native("uusd", 12345u128), //! Asset::cw20(api.addr_validate("token_addr")?, 67890u128),