mars_core/
asset.rs

1use cosmwasm_std::{to_binary, Addr, BankMsg, Coin, CosmosMsg, Deps, StdResult, Uint128, WasmMsg};
2use cw20::Cw20ExecuteMsg;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::helpers::cw20_get_balance;
7use crate::tax::deduct_tax;
8use astroport::asset::AssetInfo as AstroportAssetInfo;
9
10/// Represents either a native asset or a cw20. Meant to be used as part of a msg
11/// in a contract call and not to be used internally
12#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
13#[serde(rename_all = "snake_case")]
14pub enum Asset {
15    Cw20 { contract_addr: String },
16    Native { denom: String },
17}
18
19impl Asset {
20    /// Get label (denom/address as string),
21    /// reference (denom/address as bytes, used as key for storage)
22    /// and asset type
23    pub fn get_attributes(&self) -> (String, Vec<u8>, AssetType) {
24        match &self {
25            Asset::Native { denom } => {
26                let asset_reference = denom.as_bytes().to_vec();
27                (denom.clone(), asset_reference, AssetType::Native)
28            }
29            Asset::Cw20 { contract_addr } => {
30                let lower_case_contract_addr = contract_addr.to_lowercase();
31                let asset_reference = lower_case_contract_addr.as_bytes().to_vec();
32                (lower_case_contract_addr, asset_reference, AssetType::Cw20)
33            }
34        }
35    }
36
37    /// Return bytes used as key for storage
38    pub fn get_reference(&self) -> Vec<u8> {
39        match &self {
40            Asset::Native { denom } => denom.as_bytes().to_vec(),
41            Asset::Cw20 { contract_addr } => contract_addr.to_lowercase().as_bytes().to_vec(),
42        }
43    }
44}
45
46// Cast astroport::asset::AssetInfo into mars_core::asset::Asset so that they can be compared
47impl From<&AstroportAssetInfo> for Asset {
48    fn from(info: &AstroportAssetInfo) -> Self {
49        match info {
50            AstroportAssetInfo::Token { contract_addr } => Asset::Cw20 {
51                contract_addr: contract_addr.to_string(),
52            },
53            AstroportAssetInfo::NativeToken { denom } => Asset::Native {
54                denom: denom.clone(),
55            },
56        }
57    }
58}
59
60#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, JsonSchema)]
61#[serde(rename_all = "snake_case")]
62pub enum AssetType {
63    Cw20,
64    Native,
65}
66
67/// Prepares a message to send the asset from the contract executing the messages to the recipient.
68/// If the `AssetType` is `Native`, a "tax" is charged (see [`build_send_native_asset_with_tax_deduction_msg`] for details). Also sender will always be the contract calling this method
69/// as it's the only Bank Transfer
70/// No tax is charged on `Cw20` asset transfers.
71pub fn build_send_asset_with_tax_deduction_msg(
72    deps: Deps,
73    recipient_address: Addr,
74    asset_label: String,
75    asset_type: AssetType,
76    amount: Uint128,
77) -> StdResult<CosmosMsg> {
78    match asset_type {
79        AssetType::Native => Ok(build_send_native_asset_with_tax_deduction_msg(
80            deps,
81            recipient_address,
82            asset_label,
83            amount,
84        )?),
85        AssetType::Cw20 => build_send_cw20_token_msg(recipient_address, asset_label, amount),
86    }
87}
88
89/// Prepare BankMsg::Send message.
90/// When doing native transfers a "tax" is charged.
91/// The actual amount taken from the contract is: amount + tax.
92/// Instead of sending amount, send: amount - compute_tax(amount).
93pub fn build_send_native_asset_with_tax_deduction_msg(
94    deps: Deps,
95    recipient_address: Addr,
96    denom: String,
97    amount: Uint128,
98) -> StdResult<CosmosMsg> {
99    Ok(CosmosMsg::Bank(BankMsg::Send {
100        to_address: recipient_address.into(),
101        amount: vec![deduct_tax(deps, Coin { denom, amount })?],
102    }))
103}
104
105pub fn build_send_cw20_token_msg(
106    recipient_address: Addr,
107    token_contract_address_unchecked: String,
108    amount: Uint128,
109) -> StdResult<CosmosMsg> {
110    Ok(CosmosMsg::Wasm(WasmMsg::Execute {
111        contract_addr: token_contract_address_unchecked,
112        msg: to_binary(&Cw20ExecuteMsg::Transfer {
113            recipient: recipient_address.into(),
114            amount,
115        })?,
116        funds: vec![],
117    }))
118}
119
120/// Gets asset balance for the given address
121pub fn get_asset_balance(
122    deps: Deps,
123    address: Addr,
124    asset_label: String,
125    asset_type: AssetType,
126) -> StdResult<Uint128> {
127    match asset_type {
128        AssetType::Native => {
129            let balance_query = deps.querier.query_balance(address, asset_label.as_str())?;
130            Ok(balance_query.amount)
131        }
132        AssetType::Cw20 => {
133            let token_addr = deps.api.addr_validate(&asset_label)?;
134            cw20_get_balance(&deps.querier, token_addr, address)
135        }
136    }
137}