-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #219 from CosmWasm/payment-helpers
Payment helpers
- Loading branch information
Showing
4 changed files
with
146 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
mod balance; | ||
mod expiration; | ||
mod pagination; | ||
mod payment; | ||
|
||
pub use crate::balance::NativeBalance; | ||
pub use crate::expiration::{Duration, Expiration, DAY, HOUR, WEEK}; | ||
pub use pagination::{ | ||
calc_range_end_human, calc_range_start_human, calc_range_start_string, maybe_canonical, | ||
}; | ||
pub use payment::{may_pay, must_pay, nonpayable, PaymentError}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
use cosmwasm_std::{MessageInfo, Uint128}; | ||
use thiserror::Error; | ||
|
||
/// returns an error if any coins were sent | ||
pub fn nonpayable(info: &MessageInfo) -> Result<(), PaymentError> { | ||
if info.sent_funds.is_empty() { | ||
Ok(()) | ||
} else { | ||
Err(PaymentError::NonPayable {}) | ||
} | ||
} | ||
|
||
/// Requires exactly one denom sent, which matches the requested denom. | ||
/// Returns the amount if only one denom and non-zero amount. Errors otherwise. | ||
pub fn must_pay(info: &MessageInfo, denom: &str) -> Result<Uint128, PaymentError> { | ||
match info.sent_funds.len() { | ||
0 => Err(PaymentError::NoFunds {}), | ||
1 => { | ||
if info.sent_funds[0].denom == denom { | ||
let payment = info.sent_funds[0].amount; | ||
if payment.is_zero() { | ||
Err(PaymentError::NoFunds {}) | ||
} else { | ||
Ok(payment) | ||
} | ||
} else { | ||
Err(PaymentError::MissingDenom(denom.to_string())) | ||
} | ||
} | ||
_ => { | ||
// find first mis-match | ||
let wrong = info.sent_funds.iter().find(|c| c.denom != denom).unwrap(); | ||
Err(PaymentError::ExtraDenom(wrong.denom.to_string())) | ||
} | ||
} | ||
} | ||
|
||
/// Similar to must_pay, but it any payment is optional. Returns an error if a different | ||
/// denom was sent. Otherwise, returns the amount of `denom` sent, or 0 if nothing sent. | ||
pub fn may_pay(info: &MessageInfo, denom: &str) -> Result<Uint128, PaymentError> { | ||
if info.sent_funds.is_empty() { | ||
Ok(Uint128(0)) | ||
} else if info.sent_funds.len() == 1 && info.sent_funds[0].denom == denom { | ||
Ok(info.sent_funds[0].amount) | ||
} else { | ||
// find first mis-match | ||
let wrong = info.sent_funds.iter().find(|c| c.denom != denom).unwrap(); | ||
Err(PaymentError::ExtraDenom(wrong.denom.to_string())) | ||
} | ||
} | ||
|
||
#[derive(Error, Debug, PartialEq)] | ||
pub enum PaymentError { | ||
#[error("Must send reserve token '{0}'")] | ||
MissingDenom(String), | ||
|
||
#[error("Received unsupported denom '{0}'")] | ||
ExtraDenom(String), | ||
|
||
#[error("No funds sent")] | ||
NoFunds {}, | ||
|
||
#[error("This message does no accept funds")] | ||
NonPayable {}, | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
use cosmwasm_std::testing::mock_info; | ||
use cosmwasm_std::{coin, coins}; | ||
|
||
const SENDER: &str = "sender"; | ||
|
||
#[test] | ||
fn nonpayable_works() { | ||
let no_payment = mock_info(SENDER, &[]); | ||
nonpayable(&no_payment).unwrap(); | ||
|
||
let payment = mock_info(SENDER, &coins(100, "uatom")); | ||
let res = nonpayable(&payment); | ||
assert_eq!(res.unwrap_err(), PaymentError::NonPayable {}); | ||
} | ||
|
||
#[test] | ||
fn may_pay_works() { | ||
let atom: &str = "uatom"; | ||
let no_payment = mock_info(SENDER, &[]); | ||
let atom_payment = mock_info(SENDER, &coins(100, atom)); | ||
let eth_payment = mock_info(SENDER, &coins(100, "wei")); | ||
let mixed_payment = mock_info(SENDER, &[coin(50, atom), coin(120, "wei")]); | ||
|
||
let res = may_pay(&no_payment, atom).unwrap(); | ||
assert_eq!(res, Uint128(0)); | ||
|
||
let res = may_pay(&atom_payment, atom).unwrap(); | ||
assert_eq!(res, Uint128(100)); | ||
|
||
let err = may_pay(ð_payment, atom).unwrap_err(); | ||
assert_eq!(err, PaymentError::ExtraDenom("wei".to_string())); | ||
|
||
let err = may_pay(&mixed_payment, atom).unwrap_err(); | ||
assert_eq!(err, PaymentError::ExtraDenom("wei".to_string())); | ||
} | ||
|
||
#[test] | ||
fn must_pay_works() { | ||
let atom: &str = "uatom"; | ||
let no_payment = mock_info(SENDER, &[]); | ||
let atom_payment = mock_info(SENDER, &coins(100, atom)); | ||
let zero_payment = mock_info(SENDER, &coins(0, atom)); | ||
let eth_payment = mock_info(SENDER, &coins(100, "wei")); | ||
let mixed_payment = mock_info(SENDER, &[coin(50, atom), coin(120, "wei")]); | ||
|
||
let res = must_pay(&atom_payment, atom).unwrap(); | ||
assert_eq!(res, Uint128(100)); | ||
|
||
let err = must_pay(&no_payment, atom).unwrap_err(); | ||
assert_eq!(err, PaymentError::NoFunds {}); | ||
|
||
let err = must_pay(&zero_payment, atom).unwrap_err(); | ||
assert_eq!(err, PaymentError::NoFunds {}); | ||
|
||
let err = must_pay(ð_payment, atom).unwrap_err(); | ||
assert_eq!(err, PaymentError::MissingDenom(atom.to_string())); | ||
|
||
let err = must_pay(&mixed_payment, atom).unwrap_err(); | ||
assert_eq!(err, PaymentError::ExtraDenom("wei".to_string())); | ||
} | ||
} |