use crate::{error::AbstractError, AbstractResult};
use cosmwasm_std::{Addr, Api, CosmosMsg, Decimal, Uint128};
use cw_asset::Asset;
#[cosmwasm_schema::cw_serde]
pub struct UsageFee {
fee: Fee,
recipient: Addr,
}
impl UsageFee {
pub fn new(
api: &dyn Api,
share: Decimal,
recipient: impl Into<String>,
) -> AbstractResult<Self> {
let recipient = api.addr_validate(&recipient.into())?;
let fee = Fee::new(share)?;
Ok(UsageFee { fee, recipient })
}
pub fn set_share(&mut self, share: Decimal) -> AbstractResult<()> {
self.fee = Fee::new(share)?;
Ok(())
}
pub fn share(&self) -> Decimal {
self.fee.share()
}
pub fn compute(&self, amount: Uint128) -> Uint128 {
amount * self.share()
}
pub fn recipient(&self) -> Addr {
self.recipient.clone()
}
pub fn set_recipient(
&mut self,
api: &dyn Api,
recipient: impl Into<String>,
) -> AbstractResult<()> {
self.recipient = api.addr_validate(&recipient.into())?;
Ok(())
}
}
#[cosmwasm_schema::cw_serde]
pub struct Fee {
share: Decimal,
}
impl Fee {
pub fn new(share: Decimal) -> AbstractResult<Self> {
if share >= Decimal::percent(100) {
return Err(AbstractError::Fee(
"fee share must be lesser than 100%".to_string(),
));
}
Ok(Fee { share })
}
pub fn compute(&self, amount: Uint128) -> Uint128 {
amount * self.share
}
pub fn msg(&self, asset: Asset, recipient: Addr) -> AbstractResult<CosmosMsg> {
asset.transfer_msg(recipient).map_err(Into::into)
}
pub fn share(&self) -> Decimal {
self.share
}
}
#[cfg(test)]
mod tests {
use super::*;
mod fee {
use super::*;
#[test]
fn test_fee_manual_construction() {
let fee = Fee {
share: Decimal::percent(20u64),
};
let deposit = Uint128::from(1000000u64);
let deposit_fee = fee.compute(deposit);
assert_eq!(deposit_fee, Uint128::from(200000u64));
}
#[test]
fn test_fee_new() {
let fee = Fee::new(Decimal::percent(20u64)).unwrap();
let deposit = Uint128::from(1000000u64);
let deposit_fee = fee.compute(deposit);
assert_eq!(deposit_fee, Uint128::from(200000u64));
}
#[test]
fn test_fee_new_gte_100() {
let fee = Fee::new(Decimal::percent(100u64));
assert!(fee.is_err());
let fee = Fee::new(Decimal::percent(101u64));
assert!(fee.is_err());
}
#[test]
fn test_fee_share() {
let expected_percent = 20u64;
let fee = Fee::new(Decimal::percent(expected_percent)).unwrap();
assert_eq!(fee.share(), Decimal::percent(expected_percent));
}
#[test]
fn test_fee_msg() {
let fee = Fee::new(Decimal::percent(20u64)).unwrap();
let asset = Asset::native("uusd", Uint128::from(1000000u64));
let recipient = Addr::unchecked("recipient");
let msg = fee.msg(asset.clone(), recipient.clone()).unwrap();
assert_eq!(msg, asset.transfer_msg(recipient).unwrap(),);
}
}
mod transfer_fee {
use cosmwasm_std::testing::MockApi;
use super::*;
#[test]
fn test_transfer_fee_new() {
let api = MockApi::default();
let fee = UsageFee::new(&api, Decimal::percent(20u64), "recipient").unwrap();
let deposit = Uint128::from(1000000u64);
let deposit_fee = fee.compute(deposit);
assert_eq!(deposit_fee, Uint128::from(200000u64));
}
#[test]
fn test_transfer_fee_share() {
let api = MockApi::default();
let expected_percent = 20u64;
let fee = UsageFee::new(&api, Decimal::percent(expected_percent), "recipient").unwrap();
assert_eq!(fee.share(), Decimal::percent(expected_percent));
}
#[test]
fn test_transfer_fee_msg() {
let api = MockApi::default();
let fee = UsageFee::new(&api, Decimal::percent(20u64), "recipient").unwrap();
let asset = Asset::native("uusd", Uint128::from(1000000u64));
let recipient = Addr::unchecked("recipient");
let msg = fee.fee.msg(asset.clone(), recipient.clone()).unwrap();
assert_eq!(msg, asset.transfer_msg(recipient).unwrap(),);
}
#[test]
fn test_transfer_fee_new_gte_100() {
let api = MockApi::default();
let fee = UsageFee::new(&api, Decimal::percent(100u64), "recipient");
assert!(fee.is_err());
let fee = UsageFee::new(&api, Decimal::percent(101u64), "recipient");
assert!(fee.is_err());
}
#[test]
fn test_transfer_fee_set_recipient() {
let api = MockApi::default();
let mut fee = UsageFee::new(&api, Decimal::percent(20u64), "recipient").unwrap();
let new_recipient = "new_recipient";
fee.set_recipient(&api, new_recipient).unwrap();
assert_eq!(fee.recipient(), Addr::unchecked(new_recipient));
}
#[test]
fn test_transfer_fee_set_share() {
let api = MockApi::default();
let mut fee = UsageFee::new(&api, Decimal::percent(20u64), "recipient").unwrap();
let new_share = Decimal::percent(10u64);
fee.set_share(new_share).unwrap();
assert_eq!(fee.share(), new_share);
}
}
}