Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

Bank Run Vulnerability in RAAC Protocol

Description

The RAAC protocol's current withdrawal mechanism implements a "first come, first served" model in withdraw(), which coupled with the fact that there is no mechanism of socilaization of loss, could lead to a bank run scenario.

The fundamental issue arises from underwater positions and extreme price movements:

  1. Liquidation Mechanics

    • When a borrower's debt value exceeds their collateral value, they should be liquidated

    • During liquidation, the protocol should recover the borrower's debt value

    • However, if collateral value < debt value, there's an unrecoverable loss

    • Example:

      • Borrower has 100 crvUSD debt and NFT worth 150 crvUSD

      • NFT price crashes to 80 crvUSD

      • Liquidation recovers only 80 crvUSD, leaving 20 crvUSD deficit

  2. Lack of Loss Socialization Mechanism Among Existing Depositors

    • These unrecoverable losses create a deficit in the protocol

    • First withdrawers can still get full value

    • Last withdrawers bear the entire system loss

  3. Price Movement Amplification

    • Large market movements can create multiple underwater positions simultaneously

    • Each liquidation potentially adds to the system deficit

    • As losses accumulate, risk of a bank run increases

    • Users who recognize this will rush to withdraw, triggering the run

Impact

The vulnerability creates several serious risks:

  1. Inequality Among Users

    • Early withdrawers receive full value

    • Later withdrawers may receive nothing

    • Advantages users with:

      • Better network connectivity

      • Higher gas fee capability

      • Automated monitoring tools

  2. Market Destabilization

    • Self-fulfilling prophecy where fear of a bank run causes a bank run

    • Could trigger cascading liquidations

Mitigation

Some possible ways to handle this:

  • Implement Withdrawal Rate Limiting

uint256 public constant DAILY_WITHDRAWAL_CAP = 20_00; // 20%
mapping(uint256 => uint256) public dailyWithdrawals;
function withdraw(uint256 amount) external {
uint256 today = block.timestamp / 1 days;
uint256 dailyLimit = (totalAssets * DAILY_WITHDRAWAL_CAP) / 100_00;
require(dailyWithdrawals[today] + amount <= dailyLimit,
"Daily limit exceeded");
dailyWithdrawals[today] += amount;
// ... rest of withdrawal logic
}
  • Proportional Withdrawals

function withdraw(uint256 amount) external {
uint256 availableLiquidity = crvUSD.balanceOf(address(this)) +
vaultBalance;
uint256 proportionalAmount = (amount * availableLiquidity) /
totalRTokenSupply;
// ... process withdrawal with proportionalAmount
}
  • Withdrawal Queue System

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool vulnerable to bank run as first-come-first-served withdrawal model lacks loss socialization, allowing early withdrawers to escape while late ones bear losses

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

LendingPool vulnerable to bank run as first-come-first-served withdrawal model lacks loss socialization, allowing early withdrawers to escape while late ones bear losses

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.