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 7 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 7 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.

Give us feedback!