#37671 [SC-Critical] CRITICAL-02 / The contract could be permanently locked due to not reseting the
Previous#37668 [SC-Low] Incorrect Scale Factor value leads to early scale changeNextFolks: Liquid Staking
Last updated
Was this helpful?
Last updated
Was this helpful?
Was this helpful?
// ... calculations and checks above ...
if (new_debt < MIN_NET_DEBT) {
single_redemption_values.cancelled_partial = true;
return single_redemption_values;
}
// ... rest of the function that eventually resets the lock ...#[tokio::test]
async fn fails_with_stuck_redemption_lock() {
let (contracts, _admin, mut wallets) = setup_protocol(5, false, false).await;
oracle_abi::set_debug_timestamp(&contracts.asset_contracts[0].oracle, PYTH_TIMESTAMP).await;
pyth_oracle_abi::update_price_feeds(
&contracts.asset_contracts[0].mock_pyth_oracle,
pyth_price_feed(10),
)
.await;
let wallet1 = wallets.pop().unwrap();
let wallet2 = wallets.pop().unwrap();
// Setup initial balances
let balance = 25_000 * PRECISION;
token_abi::mint_to_id(
&contracts.asset_contracts[0].asset,
balance,
Identity::Address(wallet1.address().into()),
)
.await;
token_abi::mint_to_id(
&contracts.asset_contracts[0].asset,
balance,
Identity::Address(wallet2.address().into()),
)
.await;
// Create troves
let borrow_operations_wallet1 = ContractInstance::new(
BorrowOperations::new(
contracts.borrow_operations.contract.contract_id().clone(),
wallet1.clone(),
),
contracts.borrow_operations.implementation_id.clone(),
);
let borrow_operations_wallet2 = ContractInstance::new(
BorrowOperations::new(
contracts.borrow_operations.contract.contract_id().clone(),
wallet2.clone(),
),
contracts.borrow_operations.implementation_id.clone(),
);
// first trove with debt that will be below MIN_NET_DEBT after partial redemption
borrow_operations_abi::open_trove(
&borrow_operations_wallet1,
&contracts.asset_contracts[0].oracle,
&contracts.asset_contracts[0].mock_pyth_oracle,
&contracts.asset_contracts[0].mock_redstone_oracle,
&contracts.asset_contracts[0].asset,
&contracts.usdf,
&contracts.fpt_staking,
&contracts.sorted_troves,
&contracts.asset_contracts[0].trove_manager,
&contracts.active_pool,
1_100 * PRECISION,
1_000 * PRECISION, // Initial debt
Identity::Address(Address::zeroed()),
Identity::Address(Address::zeroed()),
)
.await
.unwrap();
// second trove with normal amounts
borrow_operations_abi::open_trove(
&borrow_operations_wallet2,
&contracts.asset_contracts[0].oracle,
&contracts.asset_contracts[0].mock_pyth_oracle,
&contracts.asset_contracts[0].mock_redstone_oracle,
&contracts.asset_contracts[0].asset,
&contracts.usdf,
&contracts.fpt_staking,
&contracts.sorted_troves,
&contracts.asset_contracts[0].trove_manager,
&contracts.active_pool,
5_000 * PRECISION,
2_000 * PRECISION,
Identity::Address(Address::zeroed()),
Identity::Address(Address::zeroed()),
)
.await
.unwrap();
let protocol_manager_wallet1 = ContractInstance::new(
ProtocolManager::new(
contracts.protocol_manager.contract.contract_id().clone(),
wallet1.clone(),
),
contracts.protocol_manager.implementation_id.clone(),
);
let protocol_manager_wallet2 = ContractInstance::new(
ProtocolManager::new(
contracts.protocol_manager.contract.contract_id().clone(),
wallet2.clone(),
),
contracts.protocol_manager.implementation_id.clone(),
);
// First redemption - this should trigger the early return without resetting the lock
let _result = protocol_manager_abi::redeem_collateral(
&protocol_manager_wallet1,
900 * PRECISION, // Amount that would leave debt below MIN_NET_DEBT
1, // max_iterations
0, // partial_redemption_hint
Some(Identity::Address(Address::zeroed())),
Some(Identity::Address(Address::zeroed())),
&contracts.usdf,
&contracts.fpt_staking,
&contracts.coll_surplus_pool,
&contracts.default_pool,
&contracts.active_pool,
&contracts.sorted_troves,
&contracts.asset_contracts,
)
.await;
// Second redemption - this should fail due to the stuck lock
let result = protocol_manager_abi::redeem_collateral(
&protocol_manager_wallet2,
1_000 * PRECISION, // Normal redemption amount
1, // max_iterations
0, // partial_redemption_hint
Some(Identity::Address(Address::zeroed())),
Some(Identity::Address(Address::zeroed())),
&contracts.usdf,
&contracts.fpt_staking,
&contracts.coll_surplus_pool,
&contracts.default_pool,
&contracts.active_pool,
&contracts.sorted_troves,
&contracts.asset_contracts,
)
.await;
// The second redemption will fail with the lock error!
// ("Internal redeem collateral from trove is locked")
}