dexter_multi_staking/execute/
unbond.rs1use crate::{
2 contract::{update_staking_rewards, ContractResult, CONTRACT_NAME},
3 state::USER_LP_TOKEN_LOCKS,
4};
5use const_format::concatcp;
6use cosmwasm_std::{Addr, DepsMut, Env, Event, MessageInfo, Response, Uint128};
7
8use dexter::{
9 asset::AssetInfo,
10 helper::build_transfer_token_to_user_msg,
11 multi_staking::{Config, TokenLock, MAX_USER_LP_TOKEN_LOCKS},
12};
13
14use dexter::helper::EventExt;
15
16use crate::{
17 error::ContractError,
18 state::{CONFIG, LP_GLOBAL_STATE, USER_BONDED_LP_TOKENS},
19};
20
21pub fn instant_unbond(
26 mut deps: DepsMut,
27 env: Env,
28 info: MessageInfo,
29 lp_token: Addr,
30 amount: Uint128,
31) -> ContractResult<Response> {
32 let mut response = Response::new();
33
34 if amount.is_zero() {
35 return Err(ContractError::ZeroAmount);
36 }
37
38 let current_bond_amount = USER_BONDED_LP_TOKENS
39 .may_load(deps.storage, (&lp_token, &info.sender))?
40 .unwrap_or_default();
41
42 let config: Config = CONFIG.load(deps.storage)?;
43 let mut lp_global_state = LP_GLOBAL_STATE.load(deps.storage, &lp_token)?;
44
45 let user_updated_bond_amount = current_bond_amount.checked_sub(amount).map_err(|_| {
46 ContractError::CantUnbondMoreThanBonded {
47 amount_to_unbond: amount,
48 current_bond_amount,
49 }
50 })?;
51
52 for asset in &lp_global_state.active_reward_assets {
53 update_staking_rewards(
54 asset,
55 &lp_token,
56 &info.sender,
57 lp_global_state.total_bond_amount,
58 current_bond_amount,
59 env.block.time.seconds(),
60 &mut deps,
61 &mut response,
62 None,
63 )?;
64 }
65
66 lp_global_state.total_bond_amount = lp_global_state.total_bond_amount.checked_sub(amount)?;
68 LP_GLOBAL_STATE.save(deps.storage, &lp_token, &lp_global_state)?;
69
70 USER_BONDED_LP_TOKENS.save(
71 deps.storage,
72 (&lp_token, &info.sender),
73 &user_updated_bond_amount,
74 )?;
75
76 let instant_unbond_fee = amount.multiply_ratio(config.instant_unbond_fee_bp, Uint128::from(10000u128));
78
79 let fee_receiver = config.keeper;
81
82 let fee_msg = build_transfer_token_to_user_msg(
84 AssetInfo::token(lp_token.clone()),
85 fee_receiver,
86 instant_unbond_fee,
87 )?;
88 response = response.add_message(fee_msg);
89
90 let unbond_msg = build_transfer_token_to_user_msg(
92 AssetInfo::token(lp_token.clone()),
93 info.sender.clone(),
94 amount.checked_sub(instant_unbond_fee)?,
95 )?;
96 response = response.add_message(unbond_msg);
97
98 let event = Event::from_info(concatcp!(CONTRACT_NAME, "::instant_unbond"), &info)
99 .add_attribute("lp_token", lp_token)
100 .add_attribute("amount", amount)
101 .add_attribute("total_bond_amount", lp_global_state.total_bond_amount)
102 .add_attribute("user_updated_bond_amount", user_updated_bond_amount)
103 .add_attribute("instant_unbond_fee", instant_unbond_fee)
104 .add_attribute("user_withdrawn_amount", amount.checked_sub(instant_unbond_fee)?);
105
106 response = response.add_event(event);
107 Ok(response)
108}
109
110pub fn unbond(
112 mut deps: DepsMut,
113 env: Env,
114 info: MessageInfo,
115 lp_token: Addr,
116 amount: Option<Uint128>,
117) -> ContractResult<Response> {
118 let mut response = Response::new();
122
123 let current_bond_amount = USER_BONDED_LP_TOKENS
124 .may_load(deps.storage, (&lp_token, &info.sender))?
125 .unwrap_or_default();
126
127 let amount = amount.unwrap_or(current_bond_amount);
129 if amount.is_zero() {
130 return Err(ContractError::ZeroAmount);
131 }
132
133 let mut lp_global_state = LP_GLOBAL_STATE.load(deps.storage, &lp_token)?;
134 for asset in &lp_global_state.active_reward_assets {
135 update_staking_rewards(
136 asset,
137 &lp_token,
138 &info.sender,
139 lp_global_state.total_bond_amount,
140 current_bond_amount,
141 env.block.time.seconds(),
142 &mut deps,
143 &mut response,
144 None,
145 )?;
146 }
147
148 lp_global_state.total_bond_amount = lp_global_state.total_bond_amount.checked_sub(amount)?;
150 LP_GLOBAL_STATE.save(deps.storage, &lp_token, &lp_global_state)?;
151
152 let user_updated_bond_amount = current_bond_amount.checked_sub(amount).map_err(|_| {
153 ContractError::CantUnbondMoreThanBonded {
154 amount_to_unbond: amount,
155 current_bond_amount,
156 }
157 })?;
158
159 USER_BONDED_LP_TOKENS.save(
160 deps.storage,
161 (&lp_token, &info.sender),
162 &user_updated_bond_amount,
163 )?;
164
165 let mut unlocks = USER_LP_TOKEN_LOCKS
167 .may_load(deps.storage, (&lp_token, &info.sender))?
168 .unwrap_or_default();
169
170 if unlocks.len() == MAX_USER_LP_TOKEN_LOCKS {
171 return Err(ContractError::CantAllowAnyMoreLpTokenUnbonds);
172 }
173
174 let config = CONFIG.load(deps.storage)?;
175
176 let unlock_time = env.block.time.seconds() + config.unlock_period;
177 unlocks.push(TokenLock {
178 unlock_time,
179 amount,
180 });
181
182 USER_LP_TOKEN_LOCKS.save(deps.storage, (&lp_token, &info.sender), &unlocks)?;
183
184 let event = Event::from_info(concatcp!(CONTRACT_NAME, "::unbond"), &info)
185 .add_attribute("lp_token", lp_token)
186 .add_attribute("amount", amount)
187 .add_attribute("total_bond_amount", lp_global_state.total_bond_amount)
188 .add_attribute("user_updated_bond_amount", user_updated_bond_amount)
189 .add_attribute("unlock_time", unlock_time.to_string());
190
191 response = response.add_event(event);
192 Ok(response)
193}