1use cosmwasm_std::{coin, coins, Addr, BankMsg, Coin, Decimal, Event, MessageInfo, Uint128};
2use cw_utils::{may_pay, PaymentError};
3use terp_sdk::{create_fund_community_pool_msg, Response, SubMsg, NATIVE_FEE_DENOM};
4use thiserror::Error;
5
6const FEE_BURN_PERCENT: u64 = 50;
8const FOUNDATION: &str = "terp1gnfqzr25fgds7zn7m6njd795aat5yaf6vk7hd9";
9
10pub fn checked_fair_burn(
12 info: &MessageInfo,
13 fee: u128,
14 developer: Option<Addr>,
15 res: &mut Response,
16) -> Result<(), FeeError> {
17 let payment = may_pay(info, NATIVE_FEE_DENOM)?;
19 if payment.u128() < fee {
20 return Err(FeeError::InsufficientFee(fee, payment.u128()));
21 };
22
23 if payment.u128() != 0u128 {
24 fair_burn(fee, developer, res);
25 }
26
27 Ok(())
28}
29
30pub fn ibc_denom_fair_burn(
33 fee: Coin,
34 developer: Option<Addr>,
35 res: &mut Response,
36) -> Result<(), FeeError> {
37 let mut event = Event::new("ibc-fair-burn");
38
39 match &developer {
40 Some(developer) => {
41 let dev_fee = (fee.amount.mul_ceil(Decimal::percent(FEE_BURN_PERCENT))).u128();
43 let dev_coin = coin(dev_fee, fee.denom.to_string());
44 let foundation_coin = coin(fee.amount.u128() - dev_fee, fee.denom);
45
46 event = event.add_attribute("dev_addr", developer.to_string());
47 event = event.add_attribute("dev_coin", dev_coin.to_string());
48 event = event.add_attribute("foundation_coin", foundation_coin.to_string());
49
50 res.messages.push(SubMsg::new(BankMsg::Send {
51 to_address: developer.to_string(),
52 amount: vec![dev_coin],
53 }));
54 res.messages.push(SubMsg::new(BankMsg::Send {
55 to_address: FOUNDATION.to_string(),
56 amount: vec![foundation_coin],
57 }));
58 }
59 None => {
60 event = event.add_attribute("foundation_coin", fee.to_string());
62 res.messages.push(SubMsg::new(BankMsg::Send {
63 to_address: FOUNDATION.to_string(),
64 amount: vec![fee],
65 }));
66 }
67 }
68
69 res.events.push(event);
70 Ok(())
71}
72
73pub fn fair_burn(fee: u128, developer: Option<Addr>, res: &mut Response) {
75 let mut event = Event::new("fair-burn");
76
77 let burn_fee = (Uint128::from(fee) * Decimal::percent(FEE_BURN_PERCENT)).u128();
79 let burn_coin = coins(burn_fee, NATIVE_FEE_DENOM);
80 res.messages
81 .push(SubMsg::new(BankMsg::Burn { amount: burn_coin }));
82 event = event.add_attribute("burn_amount", Uint128::from(burn_fee).to_string());
83
84 let remainder = fee - burn_fee;
86
87 if let Some(dev) = developer {
88 res.messages.push(SubMsg::new(BankMsg::Send {
89 to_address: dev.to_string(),
90 amount: coins(remainder, NATIVE_FEE_DENOM),
91 }));
92 event = event.add_attribute("dev", dev.to_string());
93 event = event.add_attribute("dev_amount", Uint128::from(remainder).to_string());
94 } else {
95 res.messages
96 .push(SubMsg::new(create_fund_community_pool_msg(coins(
97 remainder,
98 NATIVE_FEE_DENOM,
99 ))));
100 event = event.add_attribute("dist_amount", Uint128::from(remainder).to_string());
101 }
102
103 res.events.push(event);
104}
105
106#[derive(Error, Debug, PartialEq, Eq)]
107pub enum FeeError {
108 #[error("Insufficient fee: expected {0}, got {1}")]
109 InsufficientFee(u128, u128),
110
111 #[error("{0}")]
112 Payment(#[from] PaymentError),
113}
114
115#[cfg(test)]
116mod tests {
117 use cosmwasm_std::{coins, Addr, BankMsg};
118 use terp_sdk::{create_fund_community_pool_msg, Response, NATIVE_FEE_DENOM};
119
120 use crate::{fair_burn, SubMsg};
121
122 #[test]
123 fn check_fair_burn_no_dev_rewards() {
124 let mut res = Response::new();
125
126 fair_burn(9u128, None, &mut res);
127 let burn_msg = SubMsg::new(BankMsg::Burn {
128 amount: coins(4, "uthiol".to_string()),
129 });
130 let dist_msg = SubMsg::new(create_fund_community_pool_msg(coins(5, NATIVE_FEE_DENOM)));
131 assert_eq!(res.messages.len(), 2);
132 assert_eq!(res.messages[0], burn_msg);
133 assert_eq!(res.messages[1], dist_msg);
134 }
135
136 #[test]
137 fn check_fair_burn_with_dev_rewards() {
138 let mut res = Response::new();
139
140 fair_burn(9u128, Some(Addr::unchecked("jeret")), &mut res);
141 let bank_msg = SubMsg::new(BankMsg::Send {
142 to_address: "jeret".to_string(),
143 amount: coins(5, NATIVE_FEE_DENOM),
144 });
145 let burn_msg = SubMsg::new(BankMsg::Burn {
146 amount: coins(4, NATIVE_FEE_DENOM),
147 });
148 assert_eq!(res.messages.len(), 2);
149 assert_eq!(res.messages[0], burn_msg);
150 assert_eq!(res.messages[1], bank_msg);
151 }
152
153 #[test]
154 fn check_fair_burn_with_dev_rewards_different_amount() {
155 let mut res = Response::new();
156
157 fair_burn(1420u128, Some(Addr::unchecked("eret")), &mut res);
158 let bank_msg = SubMsg::new(BankMsg::Send {
159 to_address: "eret".to_string(),
160 amount: coins(710, NATIVE_FEE_DENOM),
161 });
162 let burn_msg = SubMsg::new(BankMsg::Burn {
163 amount: coins(710, NATIVE_FEE_DENOM),
164 });
165 assert_eq!(res.messages.len(), 2);
166 assert_eq!(res.messages[0], burn_msg);
167 assert_eq!(res.messages[1], bank_msg);
168 }
169}