cw3/
deposit.rs

1use cosmwasm_schema::cw_serde;
2use cw_utils::{must_pay, PaymentError};
3use thiserror::Error;
4
5use cosmwasm_std::{
6    to_json_binary, Addr, BankMsg, Coin, CosmosMsg, Deps, MessageInfo, StdResult, Uint128, WasmMsg,
7};
8use cw20::{Denom, UncheckedDenom};
9
10/// Information about the deposit required to create a proposal.
11#[cw_serde]
12pub struct DepositInfo {
13    /// The number tokens required for payment.
14    pub amount: Uint128,
15    /// The denom of the deposit payment.
16    pub denom: Denom,
17    /// Should failed proposals have their deposits refunded?
18    pub refund_failed_proposals: bool,
19}
20
21/// Information about the deposit required to create a proposal. For
22/// use in messages. To validate, transform into `DepositInfo` via
23/// `into_checked()`.
24#[cw_serde]
25pub struct UncheckedDepositInfo {
26    /// The number tokens required for payment.
27    pub amount: Uint128,
28    /// The denom of the deposit payment.
29    pub denom: UncheckedDenom,
30    /// Should failed proposals have their deposits refunded?
31    pub refund_failed_proposals: bool,
32}
33
34#[derive(Error, Debug, PartialEq, Eq)]
35pub enum DepositError {
36    #[error("Invalid zero deposit. Set the deposit to None to have no deposit.")]
37    ZeroDeposit {},
38
39    #[error("Invalid cw20")]
40    InvalidCw20 {},
41
42    #[error("{0}")]
43    Payment(#[from] PaymentError),
44
45    #[error("Invalid native deposit amount")]
46    InvalidDeposit {},
47}
48
49impl UncheckedDepositInfo {
50    /// Checks deposit info.
51    pub fn into_checked(self, deps: Deps) -> Result<DepositInfo, DepositError> {
52        if self.amount.is_zero() {
53            Err(DepositError::ZeroDeposit {})
54        } else {
55            Ok(DepositInfo {
56                amount: self.amount,
57                denom: self
58                    .denom
59                    .into_checked(deps)
60                    .map_err(|_| DepositError::InvalidCw20 {})?,
61                refund_failed_proposals: self.refund_failed_proposals,
62            })
63        }
64    }
65}
66
67impl DepositInfo {
68    pub fn check_native_deposit_paid(&self, info: &MessageInfo) -> Result<(), DepositError> {
69        if let Self {
70            amount,
71            denom: Denom::Native(denom),
72            ..
73        } = self
74        {
75            let paid = must_pay(info, denom)?;
76            if paid != *amount {
77                Err(DepositError::InvalidDeposit {})
78            } else {
79                Ok(())
80            }
81        } else {
82            Ok(())
83        }
84    }
85
86    pub fn get_take_deposit_messages(
87        &self,
88        depositor: &Addr,
89        contract: &Addr,
90    ) -> StdResult<Vec<CosmosMsg>> {
91        let take_deposit_msg: Vec<CosmosMsg> = if let DepositInfo {
92            amount,
93            denom: Denom::Cw20(address),
94            ..
95        } = self
96        {
97            // into_checked() makes sure this isn't the case, but just for
98            // posterity.
99            if amount.is_zero() {
100                vec![]
101            } else {
102                vec![WasmMsg::Execute {
103                    contract_addr: address.to_string(),
104                    funds: vec![],
105                    msg: to_json_binary(&cw20::Cw20ExecuteMsg::TransferFrom {
106                        owner: depositor.to_string(),
107                        recipient: contract.to_string(),
108                        amount: *amount,
109                    })?,
110                }
111                .into()]
112            }
113        } else {
114            vec![]
115        };
116        Ok(take_deposit_msg)
117    }
118
119    pub fn get_return_deposit_message(&self, depositor: &Addr) -> StdResult<CosmosMsg> {
120        let message = match &self.denom {
121            Denom::Native(denom) => BankMsg::Send {
122                to_address: depositor.to_string(),
123                amount: vec![Coin {
124                    amount: self.amount,
125                    denom: denom.to_string(),
126                }],
127            }
128            .into(),
129            Denom::Cw20(address) => WasmMsg::Execute {
130                contract_addr: address.to_string(),
131                msg: to_json_binary(&cw20::Cw20ExecuteMsg::Transfer {
132                    recipient: depositor.to_string(),
133                    amount: self.amount,
134                })?,
135                funds: vec![],
136            }
137            .into(),
138        };
139        Ok(message)
140    }
141}