#37624 [SC-Critical] lock issue bricks the redeem functionality
Previous#37607 [SC-Low] bricking redeem functionNext#37650 [SC-Low] redeem functionality partially failing
Last updated
Was this helpful?
Was this helpful?
#[storage(read, write)]
fn internal_redeem_collateral_from_trove(
borrower: Identity,
max_usdf_amount: u64,
price: u64,
partial_redemption_hint: u64,
upper_partial_hint: Identity,
lower_partial_hint: Identity,
) -> SingleRedemptionValues {
// Prevent reentrancy
require(
storage
.lock_internal_redeem_collateral_from_trove
.read() == false,
"TroveManager: Internal redeem collateral from trove is locked",
);
storage
.lock_internal_redeem_collateral_from_trove
.write(true);
// ... not relevant parts
if (new_debt < MIN_NET_DEBT) {
// Issue: Lock is not released on cancellation
single_redemption_values.cancelled_partial = true;
return single_redemption_values;
} #[storage(read)]
fn get_lock_internal_redeem_collateral_from_trove() -> bool;diff --git a/contracts/trove-manager-contract/src/main.sw b/contracts/trove-manager-contract/src/main.sw
index 7e02245..cc408b0 100644
--- a/contracts/trove-manager-contract/src/main.sw
+++ b/contracts/trove-manager-contract/src/main.sw
@@ -140,6 +140,10 @@ impl TroveManager for Contract {
fn get_trove_rewards_snapshot(id: Identity) -> RewardSnapshot {
return storage.reward_snapshots.get(id).read();
}
+ #[storage(read)]
+ fn get_lock_internal_redeem_collateral_from_trove() -> bool {
+ return storage.lock_internal_redeem_collateral_from_trove.read();
+ }
#[storage(read, write)]
fn redeem_collateral_from_trove(
borrower: Identity,
@@ -149,7 +153,7 @@ impl TroveManager for Contract {
upper_partial_hint: Identity,
lower_partial_hint: Identity,
) -> SingleRedemptionValues {
- require_caller_is_protocol_manager_contract();
+ // require_caller_is_protocol_manager_contract();
internal_redeem_collateral_from_trove(
borrower,
max_usdf_amount,
@@ -183,7 +187,7 @@ impl TroveManager for Contract {
}
#[storage(read, write)]
fn set_trove_status(id: Identity, status: Status) {
- require_caller_is_borrow_operations_contract();
+ // require_caller_is_borrow_operations_contract();
match storage.troves.get(id).try_read() {
Some(trove) => {
let mut new_trove = trove;
@@ -199,17 +203,17 @@ impl TroveManager for Contract {
}
#[storage(read, write)]
fn increase_trove_coll(id: Identity, coll: u64) -> u64 {
- require_caller_is_borrow_operations_contract();
+ // require_caller_is_borrow_operations_contract();
internal_increase_trove_coll(id, coll)
}
#[storage(read, write)]
fn update_stake_and_total_stakes(id: Identity) -> u64 {
- require_caller_is_borrow_operations_contract();
+ // require_caller_is_borrow_operations_contract();
internal_update_stake_and_total_stakes(id)
}
#[storage(read, write)]
fn increase_trove_debt(id: Identity, debt: u64) -> u64 {
- require_caller_is_borrow_operations_contract();
+ // require_caller_is_borrow_operations_contract();
internal_increase_trove_debt(id, debt)
}
#[storage(read, write)]
@@ -224,7 +228,7 @@ impl TroveManager for Contract {
}
#[storage(read, write)]
fn add_trove_owner_to_array(id: Identity) -> u64 {
- require_caller_is_borrow_operations_contract();
+ // require_caller_is_borrow_operations_contract();
storage.trove_owners.push(id);
let indx = storage.trove_owners.len() - 1;
let mut trove = storage.troves.get(id).read();
@@ -280,7 +284,7 @@ impl TroveManager for Contract {
}
#[storage(read, write)]
fn update_trove_reward_snapshots(id: Identity) {
- require_caller_is_borrow_operations_contract();
+ // require_caller_is_borrow_operations_contract();
internal_update_trove_reward_snapshots(id);
}
#[storage(read)]
@@ -979,3 +983,25 @@ fn internal_update_system_snapshots_exclude_coll_remainder(coll_remainder: u64)
.total_collateral_snapshot
.write(active_pool_coll - coll_remainder + liquidated_coll);
}
+
+#[test]
+fn test_bricking_redeem_function() {
+ let price = 3_000_000_000_000;
+ let debt = 500_000_000_000;
+ let coll = fm_multiply_ratio(debt * 2, DECIMAL_PRECISION, price); // not really relevant
+ let sender = null_identity_address();
+
+ let caller = abi(TroveManager, CONTRACT_ID);
+ caller.set_trove_status(sender, Status::Active);
+ let _ = caller.increase_trove_coll(sender, coll);
+ let _ = caller.increase_trove_debt(sender, 500_000_000_000); // 500 USDF
+ caller.update_trove_reward_snapshots(sender);
+ let _ = caller.update_stake_and_total_stakes(sender);
+ let _ = caller.add_trove_owner_to_array(sender);
+
+ assert(caller.get_lock_internal_redeem_collateral_from_trove() == false); // lock is free before redemption cancellation
+
+ let _ = caller.redeem_collateral_from_trove(sender, 10, price, 0, sender, sender); // this will cause a redeeption cancellation
+
+ assert(caller.get_lock_internal_redeem_collateral_from_trove()); // lock is locked after redemption cancellation
+}
\ No newline at end of file