The Standard

The Standard
DeFiHardhat
20,000 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of validation to check whether or not L2 Sequencer is active

Summary

Within the LiquidationPool#distributeAssets(), there is no validation to check whether or not L2 Sequencer is active. This lead to getting a stale price of both $EUROs and asset in $USD in the LiquidationPool#distributeAssets().

Vulnerability Details

When a liquidator would liquidate a SmartVault, the liquidator would call the LiquidationPoolManager#runLiquidation().

Within the LiquidationPoolManager#runLiquidation(), the LiquidationPool#distributeAssets() would be called.
https://github.com/Cyfrin/2023-12-the-standard/blob/main/contracts/LiquidationPoolManager.sol#L80

Within the LiquidationPool#distributeAssets(), the current $EUROs price in $USD (priceEurUsd) and the current asset price in $USD would be retrieved via the Chainlink's AggregatorV3Interface#latestRoundData() like this:
https://github.com/Cyfrin/2023-12-the-standard/blob/main/contracts/LiquidationPool.sol#L207
https://github.com/Cyfrin/2023-12-the-standard/blob/main/contracts/LiquidationPool.sol#L218

function distributeAssets(ILiquidationPoolManager.Asset[] memory _assets, uint256 _collateralRate, uint256 _hundredPC) external payable {
consolidatePendingStakes();
(,int256 priceEurUsd,,,) = Chainlink.AggregatorV3Interface(eurUsd).latestRoundData(); /// @audit - $EURO price via the Chainlink Oracle
uint256 stakeTotal = getStakeTotal();
uint256 burnEuros;
uint256 nativePurchased;
for (uint256 j = 0; j < holders.length; j++) {
Position memory _position = positions[holders[j]];
uint256 _positionStake = stake(_position);
if (_positionStake > 0) {
for (uint256 i = 0; i < _assets.length; i++) {
ILiquidationPoolManager.Asset memory asset = _assets[i];
if (asset.amount > 0) {
(,int256 assetPriceUsd,,,) = Chainlink.AggregatorV3Interface(asset.token.clAddr).latestRoundData(); ///<--------- @audit
uint256 _portion = asset.amount * _positionStake / stakeTotal;
...

According to the "Compatibilities" part in the README, The Standard protocol has already been deployed on Arbitrum and the protocol is going to be launched on any EVM chains in the future like this:

The live version of these contracts (deployed to Arbitrum One) have some key external dependencies:
...

Compatibilities:
Blockchains:
- Any EVM chains with live Chainlink data feeds and live Uniswap pools

Chainlink recommends to check if L2 sequencer is active in order to get fresh prices. Otherwise stale prices can be fetched.

Since The Standard protocol is going to be launched on multiple L2 networks (i.e. Optimism, Arbitrum, BASE, etc), a validation to check whether or not L2 Sequencer is active is supposed to be implemented in LiquidationPool#distributeAssets() as well.

However, there is no validation to check whether or not L2 Sequencer is active.
This lead to getting a stale price of both $EUROs and asset in $USD in the LiquidationPool#distributeAssets().

(As a actual case, Arbitrum's sequencer was recently down)

Impact

This lead to getting a stale price of both $EUROs and asset in $USD in the LiquidationPool#distributeAssets().

Tools Used

  • Manual review

Recommendations

Within the LiquidationPool#distributeAssets(), consider adding a validation to check whether or not L2 Sequencer is active.

Here is an example validation code of Chainlink's L2 Sequencer Uptime Feeds:https://docs.chain.link/data-feeds/l2-sequencer-feeds#example-code

Updates

Lead Judging Commences

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Arbitrum-sequncer

hrishibhat Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
Assigned finding tags:

Arbitrum-sequncer

Support

FAQs

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