use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
use crate::querier::{query_balance, query_native_decimals, query_token_balance, query_token_info};
use cosmwasm_std::{
to_binary, Addr, Api, BankMsg, CanonicalAddr, Coin, CosmosMsg, MessageInfo, QuerierWrapper,
StdError, StdResult, SubMsg, Uint128, WasmMsg,
};
use cw20::Cw20ExecuteMsg;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct Asset {
pub info: AssetInfo,
pub amount: Uint128,
}
impl fmt::Display for Asset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.amount, self.info)
}
}
impl Asset {
pub fn is_native_token(&self) -> bool {
self.info.is_native_token()
}
pub fn into_msg(self, recipient: Addr) -> StdResult<CosmosMsg> {
let amount = self.amount;
match &self.info {
AssetInfo::Token { contract_addr } => Ok(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: contract_addr.to_string(),
msg: to_binary(&Cw20ExecuteMsg::Transfer {
recipient: recipient.to_string(),
amount,
})?,
funds: vec![],
})),
AssetInfo::NativeToken { denom } => Ok(CosmosMsg::Bank(BankMsg::Send {
to_address: recipient.to_string(),
amount: vec![Coin {
amount: self.amount,
denom: denom.to_string(),
}],
})),
}
}
pub fn into_submsg(self, recipient: Addr) -> StdResult<SubMsg> {
Ok(SubMsg::new(self.into_msg(recipient)?))
}
pub fn assert_sent_native_token_balance(&self, message_info: &MessageInfo) -> StdResult<()> {
if let AssetInfo::NativeToken { denom } = &self.info {
match message_info.funds.iter().find(|x| x.denom == *denom) {
Some(coin) => {
if self.amount == coin.amount {
Ok(())
} else {
Err(StdError::generic_err("Native token balance mismatch between the argument and the transferred"))
}
}
None => {
if self.amount.is_zero() {
Ok(())
} else {
Err(StdError::generic_err("Native token balance mismatch between the argument and the transferred"))
}
}
}
} else {
Ok(())
}
}
pub fn to_raw(&self, api: &dyn Api) -> StdResult<AssetRaw> {
Ok(AssetRaw {
info: match &self.info {
AssetInfo::NativeToken { denom } => AssetInfoRaw::NativeToken {
denom: denom.to_string(),
},
AssetInfo::Token { contract_addr } => AssetInfoRaw::Token {
contract_addr: api.addr_canonicalize(contract_addr.as_str())?,
},
},
amount: self.amount,
})
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AssetInfo {
Token { contract_addr: String },
NativeToken { denom: String },
}
impl fmt::Display for AssetInfo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AssetInfo::NativeToken { denom } => write!(f, "{denom}"),
AssetInfo::Token { contract_addr } => write!(f, "{contract_addr}"),
}
}
}
impl AssetInfo {
pub fn to_raw(&self, api: &dyn Api) -> StdResult<AssetInfoRaw> {
match self {
AssetInfo::NativeToken { denom } => Ok(AssetInfoRaw::NativeToken {
denom: denom.to_string(),
}),
AssetInfo::Token { contract_addr } => Ok(AssetInfoRaw::Token {
contract_addr: api.addr_canonicalize(contract_addr.as_str())?,
}),
}
}
pub fn is_native_token(&self) -> bool {
match self {
AssetInfo::NativeToken { .. } => true,
AssetInfo::Token { .. } => false,
}
}
pub fn query_pool(
&self,
querier: &QuerierWrapper,
api: &dyn Api,
pool_addr: Addr,
) -> StdResult<Uint128> {
match self {
AssetInfo::Token { contract_addr, .. } => query_token_balance(
querier,
api.addr_validate(contract_addr.as_str())?,
pool_addr,
),
AssetInfo::NativeToken { denom, .. } => {
query_balance(querier, pool_addr, denom.to_string())
}
}
}
pub fn equal(&self, asset: &AssetInfo) -> bool {
match self {
AssetInfo::Token { contract_addr, .. } => {
let self_contract_addr = contract_addr;
match asset {
AssetInfo::Token { contract_addr, .. } => self_contract_addr == contract_addr,
AssetInfo::NativeToken { .. } => false,
}
}
AssetInfo::NativeToken { denom, .. } => {
let self_denom = denom;
match asset {
AssetInfo::Token { .. } => false,
AssetInfo::NativeToken { denom, .. } => self_denom == denom,
}
}
}
}
pub fn query_decimals(&self, account_addr: Addr, querier: &QuerierWrapper) -> StdResult<u8> {
match self {
AssetInfo::NativeToken { denom } => {
query_native_decimals(querier, account_addr, denom.to_string())
}
AssetInfo::Token { contract_addr } => {
let token_info = query_token_info(querier, Addr::unchecked(contract_addr))?;
Ok(token_info.decimals)
}
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct AssetRaw {
pub info: AssetInfoRaw,
pub amount: Uint128,
}
impl AssetRaw {
pub fn to_normal(&self, api: &dyn Api) -> StdResult<Asset> {
Ok(Asset {
info: match &self.info {
AssetInfoRaw::NativeToken { denom } => AssetInfo::NativeToken {
denom: denom.to_string(),
},
AssetInfoRaw::Token { contract_addr } => AssetInfo::Token {
contract_addr: api.addr_humanize(contract_addr)?.to_string(),
},
},
amount: self.amount,
})
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub enum AssetInfoRaw {
Token { contract_addr: CanonicalAddr },
NativeToken { denom: String },
}
impl AssetInfoRaw {
pub fn to_normal(&self, api: &dyn Api) -> StdResult<AssetInfo> {
match self {
AssetInfoRaw::NativeToken { denom } => Ok(AssetInfo::NativeToken {
denom: denom.to_string(),
}),
AssetInfoRaw::Token { contract_addr } => Ok(AssetInfo::Token {
contract_addr: api.addr_humanize(contract_addr)?.to_string(),
}),
}
}
pub fn as_bytes(&self) -> &[u8] {
match self {
AssetInfoRaw::NativeToken { denom } => denom.as_bytes(),
AssetInfoRaw::Token { contract_addr } => contract_addr.as_slice(),
}
}
pub fn equal(&self, asset: &AssetInfoRaw) -> bool {
match self {
AssetInfoRaw::Token { contract_addr, .. } => {
let self_contract_addr = contract_addr;
match asset {
AssetInfoRaw::Token { contract_addr, .. } => {
self_contract_addr == contract_addr
}
AssetInfoRaw::NativeToken { .. } => false,
}
}
AssetInfoRaw::NativeToken { denom, .. } => {
let self_denom = denom;
match asset {
AssetInfoRaw::Token { .. } => false,
AssetInfoRaw::NativeToken { denom, .. } => self_denom == denom,
}
}
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct PairInfo {
pub asset_infos: [AssetInfo; 2],
pub contract_addr: String,
pub liquidity_token: String,
pub asset_decimals: [u8; 2],
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
pub struct PairInfoRaw {
pub asset_infos: [AssetInfoRaw; 2],
pub contract_addr: CanonicalAddr,
pub liquidity_token: CanonicalAddr,
pub asset_decimals: [u8; 2],
}
impl PairInfoRaw {
pub fn to_normal(&self, api: &dyn Api) -> StdResult<PairInfo> {
Ok(PairInfo {
liquidity_token: api.addr_humanize(&self.liquidity_token)?.to_string(),
contract_addr: api.addr_humanize(&self.contract_addr)?.to_string(),
asset_infos: [
self.asset_infos[0].to_normal(api)?,
self.asset_infos[1].to_normal(api)?,
],
asset_decimals: self.asset_decimals,
})
}
pub fn query_pools(
&self,
querier: &QuerierWrapper,
api: &dyn Api,
contract_addr: Addr,
) -> StdResult<[Asset; 2]> {
let info_0: AssetInfo = self.asset_infos[0].to_normal(api)?;
let info_1: AssetInfo = self.asset_infos[1].to_normal(api)?;
Ok([
Asset {
amount: info_0.query_pool(querier, api, contract_addr.clone())?,
info: info_0,
},
Asset {
amount: info_1.query_pool(querier, api, contract_addr)?,
info: info_1,
},
])
}
}