Skip to content
This repository was archived by the owner on Jul 27, 2026. It is now read-only.

Commit 0f67fd9

Browse files
committed
refactor: change is_below_premium_threshold function and change tests
1 parent bb9960e commit 0f67fd9

5 files changed

Lines changed: 83 additions & 75 deletions

File tree

crates/redeem/src/ext.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,6 @@ pub(crate) mod vault_registry {
143143
<vault_registry::Pallet<T>>::ensure_not_banned(vault_id)
144144
}
145145

146-
pub fn is_vault_below_premium_threshold<T: crate::Config>(
147-
vault_id: &DefaultVaultId<T>,
148-
) -> Result<bool, DispatchError> {
149-
<vault_registry::Pallet<T>>::is_vault_below_premium_threshold(vault_id)
150-
}
151-
152146
pub fn is_vault_below_secure_threshold<T: crate::Config>(
153147
vault_id: &DefaultVaultId<T>,
154148
) -> Result<bool, DispatchError> {

crates/redeem/src/lib.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -499,22 +499,19 @@ impl<T: Config> Pallet<T> {
499499
Error::<T>::AmountBelowDustAmount
500500
);
501501

502-
let below_premium_redeem = ext::vault_registry::is_vault_below_premium_threshold::<T>(&vault_id)?;
503502
let currency_id = vault_id.collateral_currency();
504503

505504
// Calculate the premium collateral amount based on whether the redemption is below the premium redeem
506505
// threshold. This should come before increasing the `to_be_redeemed` tokens and locking the amount to
507506
// ensure accurate premium redeem calculations.
508-
let premium_collateral = if below_premium_redeem {
507+
let premium_collateral = {
509508
let redeem_amount_wrapped_in_collateral = user_to_be_received_btc.convert_to(currency_id)?;
510509
let premium_redeem_rate = ext::fee::premium_redeem_reward_rate::<T>();
511510
let premium_for_redeem_amount =
512511
redeem_amount_wrapped_in_collateral.checked_rounded_mul(&premium_redeem_rate, Rounding::Down)?;
513512

514513
let max_premium = ext::vault_registry::get_vault_max_premium_redeem(&vault_id)?;
515514
max_premium.min(&premium_for_redeem_amount)?
516-
} else {
517-
Amount::zero(currency_id)
518515
};
519516

520517
// vault will get rid of the btc + btc_inclusion_fee

crates/redeem/src/tests.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,6 @@ fn test_request_redeem_succeeds_with_normal_redeem() {
182182
});
183183

184184
ext::security::get_secure_id::<Test>.mock_safe(move |_| MockResult::Return(H256([0; 32])));
185-
ext::vault_registry::is_vault_below_premium_threshold::<Test>.mock_safe(move |_| MockResult::Return(Ok(false)));
186185
ext::fee::get_redeem_fee::<Test>.mock_safe(move |_| MockResult::Return(Ok(wrapped(redeem_fee))));
187186
let btc_fee = Redeem::get_current_inclusion_fee(DEFAULT_WRAPPED_CURRENCY).unwrap();
188187

@@ -286,7 +285,6 @@ fn test_request_redeem_succeeds_with_self_redeem() {
286285
});
287286

288287
ext::security::get_secure_id::<Test>.mock_safe(move |_| MockResult::Return(H256::zero()));
289-
ext::vault_registry::is_vault_below_premium_threshold::<Test>.mock_safe(move |_| MockResult::Return(Ok(false)));
290288
let btc_fee = Redeem::get_current_inclusion_fee(DEFAULT_WRAPPED_CURRENCY).unwrap();
291289

292290
assert_ok!(Redeem::request_redeem(
@@ -760,8 +758,6 @@ mod spec_based_tests {
760758
ext::vault_registry::ensure_not_banned::<Test>.mock_safe(move |_vault_id| MockResult::Return(Ok(())));
761759
ext::vault_registry::try_increase_to_be_redeemed_tokens::<Test>
762760
.mock_safe(move |_vault_id, _amount| MockResult::Return(Ok(())));
763-
ext::vault_registry::is_vault_below_premium_threshold::<Test>
764-
.mock_safe(move |_vault_id| MockResult::Return(Ok(false)));
765761
let redeem_fee = Fee::get_redeem_fee(&wrapped(amount_to_redeem)).unwrap();
766762
let burned_tokens = wrapped(amount_to_redeem) - redeem_fee;
767763

@@ -919,7 +915,6 @@ mod spec_based_tests {
919915
inject_redeem_request(H256([0u8; 32]), redeem_request.clone());
920916

921917
ext::btc_relay::has_request_expired::<Test>.mock_safe(|_, _, _| MockResult::Return(Ok(true)));
922-
ext::vault_registry::is_vault_below_secure_threshold::<Test>.mock_safe(|_| MockResult::Return(Ok(false)));
923918
ext::vault_registry::ban_vault::<Test>.mock_safe(move |vault| {
924919
assert_eq!(vault, &VAULT);
925920
MockResult::Return(Ok(()))

crates/vault-registry/src/lib.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -804,10 +804,10 @@ impl<T: Config> Pallet<T> {
804804

805805
let required_collateral =
806806
Self::get_required_collateral_for_wrapped(&to_be_backed_tokens, vault_id.collateral_currency())?;
807-
808807
let current_collateral = Self::get_backing_collateral(&vault_id)?;
809808
let missing_collateral = required_collateral.saturating_sub(&current_collateral)?;
810809

810+
// factor = fee / (secure - fee)
811811
let factor = premium_redeem_rate
812812
.checked_div(
813813
&global_secure_threshold
@@ -1563,7 +1563,10 @@ impl<T: Config> Pallet<T> {
15631563
Ok(Self::get_vault_from_id(&vault_id)?.is_liquidated())
15641564
}
15651565

1566-
pub fn is_vault_below_premium_threshold(vault_id: &DefaultVaultId<T>) -> Result<bool, DispatchError> {
1566+
#[cfg(feature = "integration-tests")]
1567+
// note: unlike `is_vault_below_secure_threshold` and `is_vault_below_liquidation_threshold`,
1568+
// this function uses to_be_backed tokens
1569+
pub fn will_be_below_premium_threshold(vault_id: &DefaultVaultId<T>) -> Result<bool, DispatchError> {
15671570
let vault = Self::get_rich_vault_from_id(&vault_id)?;
15681571
let threshold = Self::premium_redeem_threshold(&vault_id.currencies).ok_or(Error::<T>::ThresholdNotSet)?;
15691572
let collateral = Self::get_backing_collateral(vault_id)?;
@@ -1704,10 +1707,7 @@ impl<T: Config> Pallet<T> {
17041707

17051708
let request_redeem_tokens_for_max_premium = vault_to_burn_tokens.checked_div(&amount_wrapped).ok()?;
17061709

1707-
if Self::ensure_not_banned(&vault_id).is_ok()
1708-
&& !request_redeem_tokens_for_max_premium.is_zero()
1709-
&& Self::is_vault_below_premium_threshold(&vault_id).unwrap_or(false)
1710-
{
1710+
if Self::ensure_not_banned(&vault_id).is_ok() && !request_redeem_tokens_for_max_premium.is_zero() {
17111711
Some((vault_id, request_redeem_tokens_for_max_premium))
17121712
} else {
17131713
None

parachain/runtime/runtime-tests/src/parachain/redeem.rs

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,21 @@ mod premium_redeem_tests {
127127
CoreVaultData::force_to(
128128
&vault_id,
129129
CoreVaultData {
130-
issued: vault_id.wrapped(450_000),
131-
to_be_issued: vault_id.wrapped(250_000),
132-
to_be_redeemed: vault_id.wrapped(50_000),
133-
backing_collateral: vault_id.collateral(2_000_000),
130+
issued: vault_id.wrapped(450_000_000),
131+
to_be_issued: vault_id.wrapped(250_000_000),
132+
to_be_redeemed: vault_id.wrapped(50_000_000),
133+
backing_collateral: vault_id.collateral(2_000_000_000),
134134
to_be_replaced: vault_id.wrapped(0),
135135
replace_collateral: griefing(0),
136136
..default_vault_state(&vault_id)
137137
},
138138
);
139+
140+
// make sure user has enough tokens to redeem
141+
let mut user_state = UserData::get(USER);
142+
(*user_state.balances.get_mut(&vault_id.wrapped_currency()).unwrap()).free =
143+
(*user_state.balances.get_mut(&vault_id.wrapped_currency()).unwrap()).free * 1000;
144+
UserData::force_to(USER, user_state);
139145
}
140146

141147
#[test]
@@ -144,46 +150,53 @@ mod premium_redeem_tests {
144150
setup_vault_below_premium_threshold(vault_id.clone());
145151

146152
assert!(!VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
147-
assert!(VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());
153+
assert!(VaultRegistryPallet::will_be_below_premium_threshold(&vault_id).unwrap());
148154

149-
let redeem_id = setup_redeem(vault_id.wrapped(400_000), USER, &vault_id);
155+
let compute_collateral = VaultRegistryPallet::compute_collateral(&vault_id).unwrap().amount();
156+
assert_eq!(compute_collateral, 2_000_000_000);
150157

151-
assert!(!VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
152-
assert!(!VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());
158+
let initial_state = ParachainState::get(&vault_id);
153159

160+
let redeem_id = setup_redeem(vault_id.wrapped(400_000_000), USER, &vault_id);
154161
let redeem = RedeemPallet::get_open_redeem_request_from_id(&redeem_id).unwrap();
155-
// we should get rewarded only for 150_000 + 3840 tokens (that's when we reach nearer to secure threshold)
156-
let expected_premium = FeePallet::get_premium_redeem_fee(
157-
&vault_id
158-
.wrapped(150_000 + 3840) // need to add 0.384 = 153.84
159-
.convert_to(vault_id.collateral_currency())
160-
.unwrap(),
161-
)
162-
.unwrap();
163-
assert_eq!(vault_id.collateral(redeem.premium), expected_premium);
164162

165-
// Execute redeem
166-
execute_redeem(redeem_id);
163+
assert!(!VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
164+
assert!(!VaultRegistryPallet::will_be_below_premium_threshold(&vault_id).unwrap());
167165

168-
let compute_collateral = VaultRegistryPallet::compute_collateral(&vault_id).unwrap().amount();
169-
assert_eq!(compute_collateral, 2000000 - 15384); //15.384 COL tokens lost as premium fees
166+
dry_run(|| {
167+
// further redeems will have no rewards, even though the premium redeem
168+
// has not executed yet
169+
let redeem_id = setup_redeem(vault_id.wrapped(2_000_000), USER, &vault_id);
170+
let redeem = RedeemPallet::get_open_redeem_request_from_id(&redeem_id).unwrap();
171+
assert_eq!(redeem.premium, 0);
172+
});
170173

171-
// Setup another redeem request
172-
let redeem_id = setup_redeem(vault_id.wrapped(2_000), USER, &vault_id);
174+
execute_redeem(redeem_id);
173175

174-
let redeem = RedeemPallet::get_open_redeem_request_from_id(&redeem_id).unwrap();
176+
assert_eq!(
177+
ParachainState::get(&vault_id),
178+
initial_state.with_changes(|user, vault, _, fee_pool| {
179+
// premium transferred to user
180+
// we should get rewarded only for 15.3846153846 *10^6 tokens (that's when we reach nearer to secure
181+
// threshold)
182+
let expected_premium = vault_id.collateral(15_384_615);
183+
vault.backing_collateral -= expected_premium;
184+
(*user.balances.get_mut(&vault_id.collateral_currency()).unwrap()).free += expected_premium;
185+
186+
// bitcoin balance update as usual
187+
(*user.balances.get_mut(&vault_id.wrapped_currency()).unwrap()).free -=
188+
redeem.amount_btc() + redeem.fee() + redeem.transfer_fee_btc();
189+
vault.issued -= redeem.amount_btc() + redeem.transfer_fee_btc();
190+
*fee_pool.rewards_for(&vault_id) += redeem.fee();
191+
})
192+
);
175193

176-
// No premium should be given for this request
194+
// We already checked that redeems have no more rewards after requesting the
195+
// premium redeem. Here we do a sanity check that it's still the case after
196+
// execution
197+
let redeem_id = setup_redeem(vault_id.wrapped(2_000_000), USER, &vault_id);
198+
let redeem = RedeemPallet::get_open_redeem_request_from_id(&redeem_id).unwrap();
177199
assert_eq!(redeem.premium, 0);
178-
179-
// Execute redeem
180-
execute_redeem(redeem_id);
181-
182-
// initially 400 tokens, 1st redeem consumed 398 tokens , 2nd redeem consumed 1.99 tokens, remaining 0.01
183-
let get_free_redeemable_tokens = VaultRegistryPallet::get_free_redeemable_tokens(&vault_id)
184-
.unwrap()
185-
.amount();
186-
assert_eq!(get_free_redeemable_tokens, 10);
187200
});
188201
}
189202

@@ -195,13 +208,14 @@ mod premium_redeem_tests {
195208
let global_secure = VaultRegistryPallet::get_global_secure_threshold(&vault_id.currencies).unwrap(); // 200%
196209

197210
// secure > premium > liquidation threshold
198-
// at start vault should be below premium threshold, while above global secure & secure threshold
211+
// at start the vault is above the custom&global secure threshold, but due to the to_be_issued
212+
// tokens it is already eligible for premium redeem
199213
assert!(!VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
200-
assert!(VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());
201214
assert!(!VaultRegistryPallet::is_vault_below_certain_threshold(&vault_id, global_secure).unwrap());
215+
assert!(VaultRegistryPallet::will_be_below_premium_threshold(&vault_id).unwrap());
202216

203217
// Change vault secure threshold,
204-
// now secure > global secure > premium > liquidation threshold
218+
// now custom secure > global secure > premium > liquidation threshold
205219
let vault_custom_secure_threshold = UnsignedFixedPoint::checked_from_rational(300, 100);
206220
assert_ok!(
207221
RuntimeCall::VaultRegistry(VaultRegistryCall::set_custom_secure_threshold {
@@ -213,23 +227,24 @@ mod premium_redeem_tests {
213227

214228
// vault should be below premium & secure threshold, while above global secure threshold
215229
assert!(VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
216-
assert!(VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());
217230
assert!(!VaultRegistryPallet::is_vault_below_certain_threshold(&vault_id, global_secure).unwrap());
231+
assert!(VaultRegistryPallet::will_be_below_premium_threshold(&vault_id).unwrap());
218232

219233
let max_premium_for_vault = VaultRegistryPallet::get_vault_max_premium_redeem(&vault_id).unwrap();
220234
// get premium redeem vaults
221-
let premium_redeem_vaults = RedeemPallet::get_premium_redeem_vaults()
222-
.unwrap()
223-
.get(0)
224-
.unwrap()
225-
.clone();
235+
let premium_redeem_vaults = RedeemPallet::get_premium_redeem_vaults().unwrap()[0].clone();
236+
// non-zero amount of tokens that are elible for premium redeem
237+
assert!(!premium_redeem_vaults.1.is_zero());
226238

227239
// request redeem tokens given by RPC
228240
let redeem_id_1 = setup_redeem(premium_redeem_vaults.1, USER, &vault_id);
229241

230242
let redeem_1 = RedeemPallet::get_open_redeem_request_from_id(&redeem_id_1).unwrap();
231-
// recv premium should be equal to max premium
232-
assert_eq!(redeem_1.premium, max_premium_for_vault.amount());
243+
// premium should be equal to max premium, but allow rounding error in this check.
244+
assert!(
245+
redeem_1.premium >= max_premium_for_vault.amount() - 1
246+
&& redeem_1.premium <= max_premium_for_vault.amount() + 1
247+
);
233248
assert!(!redeem_1.premium.is_zero());
234249

235250
// max premium for vault should be zero
@@ -239,9 +254,21 @@ mod premium_redeem_tests {
239254
// redeeming the max premium amount put backs vault above premium threshold
240255
// vault should be below secure threshold, while above global secure & premium threshold
241256
assert!(VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
242-
assert!(!VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());
257+
assert!(!VaultRegistryPallet::will_be_below_premium_threshold(&vault_id).unwrap());
243258
assert!(!VaultRegistryPallet::is_vault_below_certain_threshold(&vault_id, global_secure).unwrap());
244259

260+
execute_redeem(redeem_id_1);
261+
// We should be almost exactly at the secure threshold (there should only be minor
262+
// rounding errors)
263+
let vault = CoreVaultData::vault(vault_id.clone());
264+
let future_tokens = vault.to_be_issued + vault.issued - vault.to_be_redeemed;
265+
let collateral = vault.backing_collateral;
266+
let future_ratio = collateral
267+
.ratio(&future_tokens.convert_to(vault_id.collateral_currency()).unwrap())
268+
.unwrap();
269+
// actual collateralization rate: 2.000004822104648639. Allow small rounding changes
270+
assert!(future_ratio - global_secure < FixedU128::from_float(0.00001));
271+
245272
let redeem_id_2 = setup_redeem(vault_id.wrapped(800_00), USER, &vault_id);
246273
let redeem_2 = RedeemPallet::get_open_redeem_request_from_id(&redeem_id_2).unwrap();
247274
// no premium is given out for new redeems
@@ -254,12 +281,12 @@ mod premium_redeem_tests {
254281
setup_vault_below_premium_threshold(vault_id.clone());
255282

256283
assert!(!VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
257-
assert!(VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());
284+
assert!(VaultRegistryPallet::will_be_below_premium_threshold(&vault_id).unwrap());
258285

259-
let redeem_id = setup_redeem(vault_id.wrapped(100_000), USER, &vault_id);
286+
let redeem_id = setup_redeem(vault_id.wrapped(100_000_000), USER, &vault_id);
260287

261288
assert!(!VaultRegistryPallet::is_vault_below_secure_threshold(&vault_id).unwrap());
262-
assert!(!VaultRegistryPallet::is_vault_below_premium_threshold(&vault_id).unwrap());
289+
assert!(!VaultRegistryPallet::will_be_below_premium_threshold(&vault_id).unwrap());
263290

264291
let redeem = RedeemPallet::get_open_redeem_request_from_id(&redeem_id).unwrap();
265292

@@ -272,11 +299,6 @@ mod premium_redeem_tests {
272299
)
273300
.unwrap();
274301
assert_eq!(vault_id.collateral(redeem.premium), expected_premium);
275-
276-
let get_free_redeemable_tokens = VaultRegistryPallet::get_free_redeemable_tokens(&vault_id)
277-
.unwrap()
278-
.amount();
279-
assert_eq!(get_free_redeemable_tokens, 300500);
280302
});
281303
}
282304
}

0 commit comments

Comments
 (0)