The Wildcat protocol manages lending markets, tracking their state using the MarketState struct (). A key aspecat of this state is the scaleFactor, which reflects accrued interest over time. When lenders deposit or withdraw funds, their balances are scaled according to the scaleFactor to accurately represent their share of the market's assets (). The protocol also handles withdrawals in batches, allowing for delayed processing and potential partial payments if liquidity is insufficient ().
A critical issue exists in handling interest accrual when a withdrawal batch expires. The protocol aims to apply accrued interest up to the batch expiry time before processing the batch. However, the logic for updating the scaleFactor and processing expired batches can lead to inaccurate interest calculations and, consequently, incorrect balances for lenders.
Details
The protocol uses a pendingWithdrawalExpiry timestamp in the MarketState to track the expiry of a withdrawal batch (). When this timestamp is reached, the protocol attempts to process the batch, burning market tokens and paying out underlying assets to lenders who requested withdrawals.
To ensure accuracy, the protocol intends to calculate and apply any accrued interest up to the pendingWithdrawalExpiry timestamp before processing the expired batch (). This is crucial to reflect the interest earned by lenders up to the point of their withdrawal.
The current implementation in the _calculateCurrentState function, responsible for calculating the updated market state, first handles the expired withdrawal batch by updating the scaleFactor and applying any pending interest (). However, this update doesn't consider that the batch has expired, potentially leading to interest accrual beyond the intended expiry time.
After processing the expired batch, the _calculateCurrentState function proceeds to update the scaleFactor again, this time using the current block timestamp (). While seemingly correct, this second update, combined with the earlier update that didn't consider batch expiry, results in an inconsistent scaleFactor and, consequently, inaccurate balance calculations for lenders.
Impact
This inconsistency in interest accrual can lead to:
Lender Disadvantage: If the scaleFactor is updated incorrectly, lenders might receive a lower payout than what they are entitled to based on the accrued interest up to the withdrawal batch expiry.
Borrower Advantage: Conversely, borrowers might benefit from this discrepancy by effectively paying less interest than they should have, given the actual time elapsed.
Accounting Errors: The inconsistent scaleFactor can propagate to other parts of the protocol that rely on the MarketState for calculations, leading to cascading errors in accounting and reporting.
Scenario
A withdrawal batch is created with a pendingWithdrawalExpiry set to a future timestamp.
Lenders deposit and accrue interest until the pendingWithdrawalExpiry is reached.
The _calculateCurrentState function is called to determine the updated market state.
The function first processes the expired batch, updating the scaleFactor using the pendingWithdrawalExpiry timestamp. However, this update doesn't account for the fact that the batch has expired, potentially accruing interest beyond the intended time.
The function then updates the scaleFactor again, this time using the current block timestamp. This second update, combined with the inaccurate first update, results in an inconsistent scaleFactor.
When lenders attempt to withdraw, their balances are calculated using this inconsistent scaleFactor, leading to incorrect payouts.
Fix
Ensure accurate interest accrual up to the withdrawal batch expiry time by modifying the _calculateCurrentState function:
Modify Expired Batch Handling: Within the _calculateCurrentState function, when processing an expired batch, update the scaleFactor twice:
First Update: Use the pendingWithdrawalExpiry timestamp to accrue interest up to the exact expiry time of the batch.
Second Update: After processing the batch (burning tokens and potentially making partial payments), update the scaleFactor again, this time using the current block timestamp. This reflects any interest accrued since the batch expiry.
Lines of code
https://github.com/code-423n4/2024-08-wildcat/blob/fe746cc0fbedc4447a981a50e6ba4c95f98b9fe1/src/market/WildcatMarketBase.sol#L476
Vulnerability details
Summary
The Wildcat protocol manages lending markets, tracking their state using the MarketState struct (). A key aspecat of this state is the scaleFactor, which reflects accrued interest over time. When lenders deposit or withdraw funds, their balances are scaled according to the scaleFactor to accurately represent their share of the market's assets (). The protocol also handles withdrawals in batches, allowing for delayed processing and potential partial payments if liquidity is insufficient ().
A critical issue exists in handling interest accrual when a withdrawal batch expires. The protocol aims to apply accrued interest up to the batch expiry time before processing the batch. However, the logic for updating the scaleFactor and processing expired batches can lead to inaccurate interest calculations and, consequently, incorrect balances for lenders.
Details
The protocol uses a pendingWithdrawalExpiry timestamp in the MarketState to track the expiry of a withdrawal batch (). When this timestamp is reached, the protocol attempts to process the batch, burning market tokens and paying out underlying assets to lenders who requested withdrawals.
To ensure accuracy, the protocol intends to calculate and apply any accrued interest up to the pendingWithdrawalExpiry timestamp before processing the expired batch (). This is crucial to reflect the interest earned by lenders up to the point of their withdrawal.
The current implementation in the _calculateCurrentState function, responsible for calculating the updated market state, first handles the expired withdrawal batch by updating the scaleFactor and applying any pending interest (). However, this update doesn't consider that the batch has expired, potentially leading to interest accrual beyond the intended expiry time.
After processing the expired batch, the _calculateCurrentState function proceeds to update the scaleFactor again, this time using the current block timestamp (). While seemingly correct, this second update, combined with the earlier update that didn't consider batch expiry, results in an inconsistent scaleFactor and, consequently, inaccurate balance calculations for lenders.
Impact
This inconsistency in interest accrual can lead to:
Lender Disadvantage: If the scaleFactor is updated incorrectly, lenders might receive a lower payout than what they are entitled to based on the accrued interest up to the withdrawal batch expiry.
Borrower Advantage: Conversely, borrowers might benefit from this discrepancy by effectively paying less interest than they should have, given the actual time elapsed.
Accounting Errors: The inconsistent scaleFactor can propagate to other parts of the protocol that rely on the MarketState for calculations, leading to cascading errors in accounting and reporting.
Scenario
Fix
Ensure accurate interest accrual up to the withdrawal batch expiry time by modifying the _calculateCurrentState function:
Modify Expired Batch Handling: Within the _calculateCurrentState function, when processing an expired batch, update the scaleFactor twice:
First Update: Use the pendingWithdrawalExpiry timestamp to accrue interest up to the exact expiry time of the batch.
Second Update: After processing the batch (burning tokens and potentially making partial payments), update the scaleFactor again, this time using the current block timestamp. This reflects any interest accrued since the batch expiry.
Assessed type
Context