Lybra Finance - turvy_fuzz's results

A protocol building the first interest-bearing omnichain stablecoin backed by LSD.

General Information

Platform: Code4rena

Start Date: 23/06/2023

Pot Size: $60,500 USDC

Total HM: 31

Participants: 132

Period: 10 days

Judge: 0xean

Total Solo HM: 10

Id: 254

League: ETH

Lybra Finance

Findings Distribution

Researcher Performance

Rank: 3/132

Findings: 2

Award: $2,247.11

Gas:
grade-a

🌟 Selected for report: 1

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: turvy_fuzz

Also found by: SpicyMeatball

Labels

bug
3 (High Risk)
primary issue
satisfactory
selected for report
sponsor confirmed
H-02

Awards

2166.6817 USDC - $2,166.68

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L127 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L161

Vulnerability details

Impact

doesn't calculate the current borrowing amount for the provider, including the provider's borrowed shares and accumulated fees.

Proof of Concept

Borrowers collateralRatio in the liquidation() function is calculated by:

uint256 onBehalfOfCollateralRatio = (depositedAsset[onBehalfOf] * assetPrice * 100) / getBorrowedOf(onBehalfOf);

notice it calls the getBorrowedOf() function, which calculate the current borrowing amount for the borrower, including the borrowed shares and accumulated fees and not just the borrowed amount https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L253

function getBorrowedOf(address user) public view returns (uint256) {
        return borrowed[user] + feeStored[user] + _newFee(user);
    }

However, the providers collateralRatio in the rigidRedemption() function is calculated by: https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L161

uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / borrowed[provider];

here the deposit asset is divided by just the borrowed amount, missing out the borrowed shares and accumulated fees

Tools Used

Visual Studio Code

Be consistent with collateralRatio calculation

Assessed type

Other

#0 - c4-pre-sort

2023-07-11T19:13:55Z

JeffCX marked the issue as primary issue

#1 - c4-sponsor

2023-07-14T09:09:36Z

LybraFinance marked the issue as sponsor confirmed

#2 - c4-judge

2023-07-25T23:13:42Z

0xean marked the issue as satisfactory

#3 - c4-judge

2023-07-28T20:41:24Z

0xean marked the issue as selected for report

Awards

80.434 USDC - $80.43

Labels

bug
G (Gas Optimization)
grade-a
high quality report
sponsor acknowledged
edited-by-warden
G-13

External Links

cache storage variable peUSDExtraRatio before looping to avoid SLOAD(100) opcode in every loop

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/EUSDMiningIncentives.sol#L142

To save repeated SLOAD(100) gas cost, cache result of time2fullRedemption[user] and lastWithdrawTime[user]

3 instance of this: https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L92 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L156 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L162

save result of:

store result of borrowed[_onBehalfOf] + totalFee to avoid repeating same operation and SLOAD(100)

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L196

save the result of configurator.vaultKeeperRatio(address(this)) * 1e18 to avoid repeating same operation and SLOAD(100)

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L204 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L205

save result of reward - eUSDShare rather than repeating same operation

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/ProtocolRewardsPool.sol#L201

save result of payAmount - income rather than repeating same operation

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L75 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L78

The result of function getDutchAuctionDiscountPrice() should be cached rather than re-calling the function

uint256 payAmount = (((realAmount * getAssetPrice()) / 1e18) * getDutchAuctionDiscountPrice()) / 10000;
emit LSDValueCaptured(realAmount, payAmount, getDutchAuctionDiscountPrice(), block.timestamp);

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L66 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/LybraStETHVault.sol#L94

rewardPerToken() is executed twice in the same single transaction

In the modifier updateReward() , rewardPerToken() is called twice first here:

rewardPerTokenStored = rewardPerToken();

and still called in the earned() function

modifier updateReward(address _account) {
        rewardPerTokenStored = rewardPerToken();
        updatedAt = lastTimeRewardApplicable();

        if (_account != address(0)) {
            rewards[_account] = earned(_account);
            userRewardPerTokenPaid[_account] = rewardPerTokenStored;
            userUpdatedAt[_account] = block.timestamp;
        }
        _;
    }
function earned(address _account) public view returns (uint256) {
        return ((balanceOf[_account] * getBoost(_account) * (rewardPerToken() - userRewardPerTokenPaid[_account])) / 1e38) + rewards[_account];
    }

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/stakerewardV2pool.sol#L56

cache storage variable totalsupply

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/miner/stakerewardV2pool.sol#L75

These state variables should be set to constant

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/LBR.sol#L15 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/token/esLBR.sol#L20

Dead code, if condition will always pass since amount is already calculated to be always >= totalFee

This block of code will never get executed

 else {
            feeStored[_onBehalfOf] = totalFee - amount;
            PeUSD.transferFrom(_provider, address(configurator), amount);
        }

rather, only the if block will ever get executed because amount will never be less than totalFee due to the fact that it was already checked before assigning, here:

uint256 amount = borrowed[_onBehalfOf] + totalFee >= _amount ? _amount : borrowed[_onBehalfOf] + totalFee;

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L196 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraPeUSDVaultBase.sol#L201

cache the value of depositedAsset[onBehalfOf] rather than re executing SLOAD repeatedly

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L156 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L159

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L190 https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/pools/base/LybraEUSDVaultBase.sol#L192

#0 - c4-pre-sort

2023-07-27T22:32:01Z

JeffCX marked the issue as high quality report

#1 - c4-judge

2023-07-27T23:42:16Z

0xean marked the issue as grade-a

#2 - c4-sponsor

2023-07-29T10:20:31Z

LybraFinance marked the issue as sponsor acknowledged

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter