secret_cosmwasm_std/
coin.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5use crate::math::Uint128;
6
7#[derive(Serialize, Deserialize, Clone, Default, Debug, PartialEq, Eq, JsonSchema)]
8pub struct Coin {
9    pub denom: String,
10    pub amount: Uint128,
11}
12
13impl Coin {
14    pub fn new(amount: u128, denom: impl Into<String>) -> Self {
15        Coin {
16            amount: Uint128::new(amount),
17            denom: denom.into(),
18        }
19    }
20}
21
22impl fmt::Display for Coin {
23    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24        // We use the formatting without a space between amount and denom,
25        // which is common in the Cosmos SDK ecosystem:
26        // https://github.com/cosmos/cosmos-sdk/blob/v0.42.4/types/coin.go#L643-L645
27        // For communication to end users, Coin needs to transformed anways (e.g. convert integer uatom to decimal ATOM).
28        write!(f, "{}{}", self.amount, self.denom)
29    }
30}
31
32/// A shortcut constructor for a set of one denomination of coins
33///
34/// # Examples
35///
36/// ```
37/// # use secret_cosmwasm_std::{coins, BankMsg, CosmosMsg, Response, SubMsg};
38/// # use secret_cosmwasm_std::testing::{mock_env, mock_info};
39/// # let env = mock_env();
40/// # let info = mock_info("sender", &[]);
41/// let tip = coins(123, "ucosm");
42///
43/// let mut response: Response = Default::default();
44/// response.messages = vec![SubMsg::new(BankMsg::Send {
45///   to_address: info.sender.into(),
46///   amount: tip,
47/// })];
48/// ```
49pub fn coins(amount: u128, denom: impl Into<String>) -> Vec<Coin> {
50    vec![coin(amount, denom)]
51}
52
53/// A shorthand constructor for Coin
54///
55/// # Examples
56///
57/// ```
58/// # use secret_cosmwasm_std::{coin, BankMsg, CosmosMsg, Response, SubMsg};
59/// # use secret_cosmwasm_std::testing::{mock_env, mock_info};
60/// # let env = mock_env();
61/// # let info = mock_info("sender", &[]);
62/// let tip = vec![
63///     coin(123, "ucosm"),
64///     coin(24, "ustake"),
65/// ];
66///
67/// let mut response: Response = Default::default();
68/// response.messages = vec![SubMsg::new(BankMsg::Send {
69///     to_address: info.sender.into(),
70///     amount: tip,
71/// })];
72/// ```
73pub fn coin(amount: u128, denom: impl Into<String>) -> Coin {
74    Coin::new(amount, denom)
75}
76
77/// has_coins returns true if the list of coins has at least the required amount
78pub fn has_coins(coins: &[Coin], required: &Coin) -> bool {
79    coins
80        .iter()
81        .find(|c| c.denom == required.denom)
82        .map(|m| m.amount >= required.amount)
83        .unwrap_or(false)
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn coin_implements_display() {
92        let a = Coin {
93            amount: Uint128::new(123),
94            denom: "ucosm".to_string(),
95        };
96
97        let embedded = format!("Amount: {}", a);
98        assert_eq!(embedded, "Amount: 123ucosm");
99        assert_eq!(a.to_string(), "123ucosm");
100    }
101
102    #[test]
103    fn coin_works() {
104        let a = coin(123, "ucosm");
105        assert_eq!(
106            a,
107            Coin {
108                amount: Uint128::new(123),
109                denom: "ucosm".to_string()
110            }
111        );
112
113        let zero = coin(0, "ucosm");
114        assert_eq!(
115            zero,
116            Coin {
117                amount: Uint128::new(0),
118                denom: "ucosm".to_string()
119            }
120        );
121
122        let string_denom = coin(42, String::from("ucosm"));
123        assert_eq!(
124            string_denom,
125            Coin {
126                amount: Uint128::new(42),
127                denom: "ucosm".to_string()
128            }
129        );
130    }
131
132    #[test]
133    fn coins_works() {
134        let a = coins(123, "ucosm");
135        assert_eq!(
136            a,
137            vec![Coin {
138                amount: Uint128::new(123),
139                denom: "ucosm".to_string()
140            }]
141        );
142
143        let zero = coins(0, "ucosm");
144        assert_eq!(
145            zero,
146            vec![Coin {
147                amount: Uint128::new(0),
148                denom: "ucosm".to_string()
149            }]
150        );
151
152        let string_denom = coins(42, String::from("ucosm"));
153        assert_eq!(
154            string_denom,
155            vec![Coin {
156                amount: Uint128::new(42),
157                denom: "ucosm".to_string()
158            }]
159        );
160    }
161
162    #[test]
163    fn has_coins_matches() {
164        let wallet = vec![coin(12345, "ETH"), coin(555, "BTC")];
165
166        // less than same type
167        assert!(has_coins(&wallet, &coin(777, "ETH")));
168    }
169}