1use cosmwasm_std::{MessageInfo, Uint128};
2
3use crate::{AmountU128, CheckedCoin, Denom, MonetaryError};
4
5pub fn must_pay<T>(info: &MessageInfo, denom: &Denom<T>) -> Result<AmountU128<T>, MonetaryError> {
8 if info.funds.len() != 1 {
9 return Err(MonetaryError::DenomNotFound(denom.to_string()));
10 }
11
12 let coin = &info.funds[0];
13 if coin.amount.is_zero() || coin.denom != denom.repr() {
14 return Err(MonetaryError::DenomNotFound(denom.to_string()));
15 }
16
17 Ok(AmountU128::new(coin.amount))
18}
19
20pub fn may_pay<T>(info: &MessageInfo, denom: &Denom<T>) -> Result<AmountU128<T>, MonetaryError> {
23 if info.funds.is_empty() {
24 Ok(AmountU128::new(Uint128::zero()))
25 } else if info.funds.len() == 1 {
26 if info.funds[0].denom == denom.repr() {
27 Ok(AmountU128::new(info.funds[0].amount))
28 } else {
29 Err(MonetaryError::DenomNotFound(denom.to_string()))
30 }
31 } else {
32 Err(MonetaryError::TooManyDenoms {})
33 }
34}
35
36pub fn amount<T>(info: &MessageInfo, denom: &Denom<T>) -> AmountU128<T> {
37 info.funds
38 .iter()
39 .find(|coin| coin.denom == denom.repr())
40 .map(|coin| AmountU128::new(coin.amount))
41 .unwrap_or_else(|| AmountU128::zero())
42}
43
44pub fn coin<T>(amount: impl Into<Uint128>, denom: &Denom<T>) -> CheckedCoin<T> {
45 CheckedCoin::new(denom.clone(), AmountU128::new(amount.into()))
46}
47
48pub fn coins<T>(amount: impl Into<Uint128>, denom: &Denom<T>) -> Vec<CheckedCoin<T>> {
49 vec![coin(amount, denom)]
50}
51
52#[cfg(test)]
53mod test {
54 use crate::denom;
55
56 use super::*;
57 use cosmwasm_std::testing::mock_info;
58 use cosmwasm_std::{coin, coins};
59
60 const SENDER: &str = "sender";
61
62 #[denom]
63 pub struct Atom;
64
65 #[test]
66 fn may_pay_works() {
67 let atom: Denom<Atom> = Denom::new("uatom");
68 let no_payment = mock_info(SENDER, &[]);
69 let atom_payment = mock_info(SENDER, &coins(100, &atom));
70 let eth_payment = mock_info(SENDER, &coins(100, "wei"));
71 let mixed_payment = mock_info(SENDER, &[coin(50, &atom), coin(120, "wei")]);
72
73 let res = may_pay(&no_payment, &atom).unwrap();
74 assert_eq!(res, AmountU128::zero());
75
76 let res = may_pay(&atom_payment, &atom).unwrap();
77 assert_eq!(res, AmountU128::new(100u128.into()));
78
79 let err = may_pay(ð_payment, &atom).unwrap_err();
80 assert_eq!(err, MonetaryError::DenomNotFound("uatom".to_string()));
81
82 let err = may_pay(&mixed_payment, &atom).unwrap_err();
83 assert_eq!(err, MonetaryError::TooManyDenoms {});
84 }
85
86 #[test]
87 fn must_pay_works() {
88 let atom: Denom<Atom> = Denom::new("uatom");
89 let no_payment = mock_info(SENDER, &[]);
90 let atom_payment = mock_info(SENDER, &coins(100, &atom));
91 let zero_payment = mock_info(SENDER, &coins(0, &atom));
92 let eth_payment = mock_info(SENDER, &coins(100, "wei"));
93 let mixed_payment = mock_info(SENDER, &[coin(50, &atom), coin(120, "wei")]);
94
95 let res = must_pay(&atom_payment, &atom).unwrap();
96 assert_eq!(res, AmountU128::new(100u128.into()));
97
98 let err = must_pay(&no_payment, &atom).unwrap_err();
99 assert_eq!(err, MonetaryError::DenomNotFound("uatom".to_string()));
100
101 let err = must_pay(&zero_payment, &atom).unwrap_err();
102 assert_eq!(err, MonetaryError::DenomNotFound("uatom".to_string()));
103
104 let err = must_pay(ð_payment, &atom).unwrap_err();
105 assert_eq!(err, MonetaryError::DenomNotFound("uatom".to_string()));
106
107 let err = must_pay(&mixed_payment, &atom).unwrap_err();
108 assert_eq!(err, MonetaryError::DenomNotFound("uatom".to_string()));
109 }
110}