Lybra Finance - DelerRH'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: 57/132

Findings: 4

Award: $137.45

QA:
grade-b

🌟 Selected for report: 1

πŸš€ Solo Findings: 0

Awards

18.4208 USDC - $18.42

Labels

bug
3 (High Risk)
satisfactory
edited-by-warden
duplicate-704

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/26915a826c90eeb829863ec3851c3c785800594b/contracts/lybra/configuration/LybraConfigurator.sol#L85-L88 https://github.com/code-423n4/2023-06-lybra/blob/26915a826c90eeb829863ec3851c3c785800594b/contracts/lybra/configuration/LybraConfigurator.sol#L90-L92

Vulnerability details

Vulnerability Details

Configurator contract has external functions with onlyRole and checkRole modifiers, in both cases the modifier implementation is flawed as there isn’t any check for a require or revert, the comparison will silently return false and let the execution continue:

https://github.com/code-423n4/2023-06-lybra/blob/26915a826c90eeb829863ec3851c3c785800594b/contracts/lybra/governance/GovernanceTimelock.sol#L25-L27

https://github.com/code-423n4/2023-06-lybra/blob/26915a826c90eeb829863ec3851c3c785800594b/contracts/lybra/governance/GovernanceTimelock.sol#L29-L31

Impact

Any account without any role can change protocol parameters in the Configurator contract.

Proof of Concept

contract LybraConfiguratorTest is Test {
    address deployer;
    address attacker;

    address[] proposers;
    address[] executors;

    GovernanceTimelock governance;
    mockCurve curvePool;
    Configurator configurator;

    function setUp() public {
        deployer = makeAddr("deployer");
        attacker = makeAddr("attacker");
        vm.startPrank(deployer);
        proposers.push(deployer);
        executors.push(deployer);
        governance = new GovernanceTimelock(2, proposers, executors, deployer);
        curvePool = new mockCurve();
        configurator = new Configurator(address(governance), address(0));
        vm.stopPrank();
    }

    function test_revertIfUserHasNotAnyRole() public {
        vm.startPrank(attacker);
        vm.expectRevert();
        // Include all Configurator contract external calls with only onlyRole or checkRole modifier
        configurator.setPremiumTradingEnabled(true);
        vm.stopPrank();
    }
}

Tools Used

  • Foundry

Modifiers should require that the caller has a role or not:

modifier onlyRole(bytes32 role) {
        require(GovernanceTimelock.checkOnlyRole(role, msg.sender), "Forbidden");
        _;
}
 modifier checkRole(bytes32 role) {
        require(GovernanceTimelock.checkRole(role, msg.sender), "Forbidden");
        _;
}

Assessed type

Access Control

#0 - c4-pre-sort

2023-07-08T23:34:16Z

JeffCX marked the issue as duplicate of #704

#1 - c4-judge

2023-07-28T17:18:58Z

0xean marked the issue as satisfactory

Findings Information

🌟 Selected for report: georgypetrov

Also found by: CrypticShepherd, DelerRH, Kenshin, LuchoLeonel1, SpicyMeatball, bart1e, ktg, pep7siup

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
edited-by-warden
duplicate-882

Awards

53.1445 USDC - $53.14

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/configuration/LybraConfigurator.sol#L28-L30 https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/configuration/LybraConfigurator.sol#L198-L206

Vulnerability details

Vulnerability Details

Vault doesn't implement the vaultType() external function, however, the setSafeCollateralRatio function in the Configurator contract checks the private state of the pool by vaultType() instead of getVaultType() and reverts.

Impact

Timelock can't change safe collateral ratios.

Proof of Concept

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import {Test, console} from "forge-std/Test.sol";
import {GovernanceTimelock} from "contracts/lybra/governance/GovernanceTimelock.sol";
import {mockCurve} from "contracts/mocks/mockCurve.sol";
import {Configurator} from "contracts/lybra/configuration/LybraConfigurator.sol";
import {LybraWBETHVault} from "contracts/lybra/pools/LybraWbETHVault.sol";
import {PeUSDMainnet} from "contracts/lybra/token/PeUSDMainnetStableVision.sol";

contract LybraConfiguratorTest is Test {
    address goerliEndPoint = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address wbETH = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address deployer;
    address attacker;

    address[] proposers;
    address[] executors;

    GovernanceTimelock governance;
    mockCurve curvePool;
    Configurator configurator;
    PeUSDMainnet peUsdMainnet;
    LybraWBETHVault wbETHVault;

    function setUp() public {
        deployer = makeAddr("deployer");
        attacker = makeAddr("attacker");
        vm.startPrank(deployer);
        proposers.push(deployer);
        executors.push(deployer);
        governance = new GovernanceTimelock(2, proposers, executors, deployer);
        curvePool = new mockCurve();
        configurator = new Configurator(address(governance), address(0));
        peUsdMainnet = new PeUSDMainnet(
            address(configurator),
            8,
            goerliEndPoint
        );
        // Ether oracle has no impact on this test
        wbETHVault =
        new LybraWBETHVault(address(peUsdMainnet), makeAddr("NonImportantMockForEtherOracle"), wbETH, address(configurator));
        vm.stopPrank();
    }

    function test_canChangeSafeCollateralRatio() public {
        vm.startPrank(deployer);

        // Set safe collateral ratio - 160%
        configurator.setSafeCollateralRatio(address(wbETHVault), 160 * 1e18);
        // ... Reverted
        assertEq(configurator.getSafeCollateralRatio(address(wbETHVault)), 160 * 1e18);

        vm.stopPrank();
    }
}

Tools Used

  • Foundry

1- Implements IVault correctly:

interface IVault {
    function getVaultType() external pure returns (uint8);
}

2- Use getVaultType() instead of vaultType()

function setSafeCollateralRatio(address pool, uint256 newRatio) external checkRole(TIMELOCK) {
        if (IVault(pool).getVaultType() == 0) {
            // EUSD vault base
            require(newRatio >= 160 * 1e18, "eUSD vault safe collateralRatio should more than 160%");
        } else {
            // peUSD vault base
            require(
                newRatio >= vaultBadCollateralRatio[pool] + 1e19,
                "PeUSD vault safe collateralRatio should more than bad collateralRatio"
            );
        }
        vaultSafeCollateralRatio[pool] = newRatio;
        emit SafeCollateralRatioChanged(pool, newRatio);
    }

Assessed type

DoS

#0 - c4-pre-sort

2023-07-10T14:00:37Z

JeffCX marked the issue as duplicate of #494

#1 - c4-pre-sort

2023-07-11T21:29:22Z

JeffCX marked the issue as not a duplicate

#2 - c4-pre-sort

2023-07-11T21:29:40Z

JeffCX marked the issue as duplicate of #882

#3 - c4-judge

2023-07-28T15:36:26Z

0xean marked the issue as satisfactory

#4 - c4-judge

2023-07-28T19:43:25Z

0xean changed the severity to 2 (Med Risk)

Findings Information

🌟 Selected for report: DelerRH

Also found by: DelerRH, HE1M, LaScaloneta, No12Samurai, RedTiger, adeolu, ayden, bart1e, f00l, pep7siup

Labels

bug
2 (Med Risk)
downgraded by judge
high quality report
satisfactory
duplicate-828

Awards

55.9611 USDC - $55.96

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L234-L237

Vulnerability details

Vulnerability Details

The notifyRewardAmount external function calculates decimals rewardPerTokenStored incorrectly when the reward token type equals 1 and the rewardPerTokenStored value increase unreal.

Impact

This bug can break the protocol rewards distribution.

Proof of Concept

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import {Test, console} from "forge-std/Test.sol";
import {GovernanceTimelock} from "contracts/lybra/governance/GovernanceTimelock.sol";
import {mockCurve} from "contracts/mocks/mockCurve.sol";
import {Configurator} from "contracts/lybra/configuration/LybraConfigurator.sol";
import {LybraWBETHVault} from "contracts/lybra/pools/LybraWbETHVault.sol";
import {PeUSDMainnet} from "contracts/lybra/token/PeUSDMainnetStableVision.sol";
import {ProtocolRewardsPool} from "contracts/lybra/miner/ProtocolRewardsPool.sol";
import {EUSDMock} from "contracts/mocks/MockEUSD.sol";
import {LBR} from "contracts/lybra/token/LBR.sol";
import {esLBR} from "contracts/lybra/token/esLBR.sol";
import {esLBRBoost} from "contracts/lybra/miner/esLBRBoost.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// 6 decimal USDC mock
contract mockUSDC is ERC20 {
    constructor() ERC20("USDC", "USDC") {
        _mint(msg.sender, 1000000 * 1e6);
    }

    function claim() external returns (uint256) {
        _mint(msg.sender, 10000 * 1e6);
        return 10000 * 1e6;
    }

    function decimals() public view virtual override returns (uint8) {
        return 6;
    }
}

contract ProtocolRewardsPoolTest is Test {
    address goerliEndPoint = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address wbETH = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address deployer;
    address attacker;
    address alice;

    address[] proposers;
    address[] executors;
    address[] minerContracts;
    bool[] minerContractsBools;

    GovernanceTimelock governance;
    mockCurve curvePool;
    Configurator configurator;
    PeUSDMainnet peUsdMainnet;
    EUSDMock eUSD;
    mockUSDC usdc;
    LBR lbr;
    esLBR eslbr;
    esLBRBoost boost;
    LybraWBETHVault wbETHVault;
    ProtocolRewardsPool rewardsPool;

    function setUp() public {
        deployer = makeAddr("deployer");
        attacker = makeAddr("attacker");
        alice = makeAddr("alice");
        vm.startPrank(deployer);
        proposers.push(deployer);
        executors.push(deployer);
        governance = new GovernanceTimelock(2, proposers, executors, deployer);
        curvePool = new mockCurve();
        configurator = new Configurator(address(governance), address(curvePool));
        peUsdMainnet = new PeUSDMainnet(
            address(configurator),
            8,
            goerliEndPoint
        );
        eUSD = new EUSDMock(address(configurator));
        // 6 decimal USDC token
        usdc = new mockUSDC();
        lbr = new LBR(address(configurator), 8, goerliEndPoint);
        eslbr = new esLBR(address(configurator));
        boost = new esLBRBoost();
        rewardsPool = new ProtocolRewardsPool(address(configurator));
        rewardsPool.setTokenAddress(address(eslbr), address(lbr), address(boost));
        // Ether oracle has no impact on this test
        wbETHVault =
        new LybraWBETHVault(address(peUsdMainnet), makeAddr("NonImportantMockForEtherOracle"), wbETH, address(configurator));
        configurator.setMintVault(deployer, true);
        configurator.initToken(address(eUSD), address(peUsdMainnet));
        configurator.setProtocolRewardsPool(address(rewardsPool));
        configurator.setProtocolRewardsToken(address(usdc));
        curvePool.setToken(address(eUSD), address(usdc));

        // Set minters
        minerContracts.push(address(deployer));
        minerContracts.push(address(rewardsPool));
        minerContractsBools.push(true);
        minerContractsBools.push(true);
        configurator.setTokenMiner(minerContracts, minerContractsBools);

        // Fund curve pool eusd/usdc
        eUSD.mint(address(curvePool), 10000 * 1e18);
        usdc.transfer(address(curvePool), 10000 * 1e6);

        // Fund ALice
        lbr.mint(address(alice), 100 * 1e18);
        vm.stopPrank();
    }

    function test_notifyRewardAmountRewardCalculation() public {
        // Alice stake LBR
        vm.startPrank(alice);
        rewardsPool.stake(100 * 1e18);
        assertEq(eslbr.balanceOf(alice), 100 * 1e18);
        vm.stopPrank();

        // Notify reward amount
        vm.startPrank(deployer);
        eUSD.mint(address(configurator), 3000 * 1e18);
        configurator.setPremiumTradingEnabled(true);

        uint256 eusdPreBalance = eUSD.balanceOf(address(configurator));
        configurator.distributeRewards();
        curvePool.setPrice(1010000);
        uint256 price = curvePool.get_dy_underlying(0, 2, 1e18);
        uint256 outUSDC = eusdPreBalance * price * 998 / 1e21;
        assertEq(eUSD.sharesOf(address(rewardsPool)), 0);
        assertEq(usdc.balanceOf(address(rewardsPool)), outUSDC);

        configurator.distributeRewards();
        vm.stopPrank();

        uint256 newRewardPerTokenstored =
            outUSDC * 1e36 / (10 ** ERC20(configurator.stableToken()).decimals()) / eslbr.totalSupply();

        assertEq(rewardsPool.rewardPerTokenStored(), newRewardPerTokenstored);
        assertEq(rewardsPool.earned(alice), eslbr.balanceOf(alice) * (newRewardPerTokenstored - 0) / 1e18);
    }
}

Tools Used

  • Foundry

Convert decimals correctly when tokenType equals 1:

function notifyRewardAmount(uint amount, uint tokenType) external {
        require(msg.sender == address(configurator));
        if (totalStaked() == 0) return;
        require(amount > 0, "amount = 0");
        if(tokenType == 0) {
            uint256 share = IEUSD(configurator.getEUSDAddress()).getSharesByMintedEUSD(amount);
            rewardPerTokenStored = rewardPerTokenStored + (share * 1e18) / totalStaked();
        } else if(tokenType == 1) {
            ERC20 token = ERC20(configurator.stableToken());
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e36 / (10 ** token.decimals()) / totalStaked();
        } else {
            rewardPerTokenStored = rewardPerTokenStored + (amount * 1e18) / totalStaked();
        }
    }

Assessed type

Decimal

#0 - c4-pre-sort

2023-07-10T01:37:42Z

JeffCX marked the issue as primary issue

#1 - c4-pre-sort

2023-07-10T01:37:49Z

JeffCX marked the issue as high quality report

#2 - c4-pre-sort

2023-07-13T13:44:15Z

JeffCX marked the issue as duplicate of #501

#3 - c4-judge

2023-07-28T15:40:19Z

0xean marked the issue as satisfactory

#4 - c4-judge

2023-07-28T19:46:50Z

0xean changed the severity to 2 (Med Risk)

Findings Information

🌟 Selected for report: DelerRH

Also found by: DelerRH, HE1M, LaScaloneta, No12Samurai, RedTiger, adeolu, ayden, bart1e, f00l, pep7siup

Labels

bug
2 (Med Risk)
downgraded by judge
primary issue
satisfactory
selected for report
sponsor confirmed
M-05

Awards

55.9611 USDC - $55.96

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L190-L218 https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/miner/ProtocolRewardsPool.sol#L209

Vulnerability details

Vulnerability Details

The getReward external function can't calculate and distribute rewards correctly for an account because of the reasons below:

  • Transferring EUSD while the contract EUSD balance is insufficient and reverting
  • Bad implementation of prioritized token rewards distribution when converting reward decimal for transfer stablecoin

Impact

Users can't get rewards and rewards freezes.

Proof of Concept

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import {Test, console} from "forge-std/Test.sol";
import {GovernanceTimelock} from "contracts/lybra/governance/GovernanceTimelock.sol";
import {mockCurve} from "contracts/mocks/mockCurve.sol";
import {Configurator} from "contracts/lybra/configuration/LybraConfigurator.sol";
import {LybraWBETHVault} from "contracts/lybra/pools/LybraWbETHVault.sol";
import {PeUSDMainnet} from "contracts/lybra/token/PeUSDMainnetStableVision.sol";
import {ProtocolRewardsPool} from "contracts/lybra/miner/ProtocolRewardsPool.sol";
import {EUSDMock} from "contracts/mocks/MockEUSD.sol";
import {LBR} from "contracts/lybra/token/LBR.sol";
import {esLBR} from "contracts/lybra/token/esLBR.sol";
import {esLBRBoost} from "contracts/lybra/miner/esLBRBoost.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// 6 decimal USDC mock
contract mockUSDC is ERC20 {
    constructor() ERC20("USDC", "USDC") {
        _mint(msg.sender, 1000000 * 1e6);
    }

    function claim() external returns (uint256) {
        _mint(msg.sender, 10000 * 1e6);
        return 10000 * 1e6;
    }

    function decimals() public view virtual override returns (uint8) {
        return 6;
    }
}

contract ProtocolRewardsPoolTest is Test {
    address goerliEndPoint = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address wbETH = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address deployer;
    address attacker;
    address alice;

    address[] proposers;
    address[] executors;
    address[] minerContracts;
    bool[] minerContractsBools;

    GovernanceTimelock governance;
    mockCurve curvePool;
    Configurator configurator;
    PeUSDMainnet peUsdMainnet;
    EUSDMock eUSD;
    mockUSDC usdc;
    LBR lbr;
    esLBR eslbr;
    esLBRBoost boost;
    LybraWBETHVault wbETHVault;
    ProtocolRewardsPool rewardsPool;

    function setUp() public {
        deployer = makeAddr("deployer");
        attacker = makeAddr("attacker");
        alice = makeAddr("alice");
        vm.startPrank(deployer);
        proposers.push(deployer);
        executors.push(deployer);
        governance = new GovernanceTimelock(2, proposers, executors, deployer);
        curvePool = new mockCurve();
        configurator = new Configurator(address(governance), address(curvePool));
        peUsdMainnet = new PeUSDMainnet(
            address(configurator),
            8,
            goerliEndPoint
        );
        eUSD = new EUSDMock(address(configurator));
        // 6 decimal USDC token
        usdc = new mockUSDC();
        lbr = new LBR(address(configurator), 8, goerliEndPoint);
        eslbr = new esLBR(address(configurator));
        boost = new esLBRBoost();
        rewardsPool = new ProtocolRewardsPool(address(configurator));
        rewardsPool.setTokenAddress(address(eslbr), address(lbr), address(boost));
        // Ether oracle has no impact on this test
        wbETHVault =
        new LybraWBETHVault(address(peUsdMainnet), makeAddr("NonImportantMockForEtherOracle"), wbETH, address(configurator));
        configurator.setMintVault(deployer, true);
        configurator.initToken(address(eUSD), address(peUsdMainnet));
        configurator.setProtocolRewardsPool(address(rewardsPool));
        configurator.setProtocolRewardsToken(address(usdc));
        curvePool.setToken(address(eUSD), address(usdc));

        // Set minters
        minerContracts.push(address(deployer));
        minerContracts.push(address(rewardsPool));
        minerContractsBools.push(true);
        minerContractsBools.push(true);
        configurator.setTokenMiner(minerContracts, minerContractsBools);

        // Fund curve pool eusd/usdc
        eUSD.mint(address(curvePool), 10000 * 1e18);
        usdc.transfer(address(curvePool), 10000 * 1e6);

        // Fund ALice
        lbr.mint(address(alice), 100 * 1e18);
        vm.stopPrank();
    }

    function test_canGetReward() public {
        // Alice stake LBR
        vm.startPrank(alice);
        rewardsPool.stake(100 * 1e18);
        assertEq(eslbr.balanceOf(alice), 100 * 1e18);
        vm.stopPrank();

        // Notify reward amount
        vm.startPrank(deployer);
        eUSD.mint(address(configurator), 3000 * 1e18);
        configurator.setPremiumTradingEnabled(true);

        uint256 eusdPreBalance = eUSD.balanceOf(address(configurator));
        configurator.distributeRewards();
        curvePool.setPrice(1010000);
        uint256 price = curvePool.get_dy_underlying(0, 2, 1e18);
        uint256 outUSDC = eusdPreBalance * price * 998 / 1e21;
        assertEq(eUSD.sharesOf(address(rewardsPool)), 0);
        assertEq(usdc.balanceOf(address(rewardsPool)), outUSDC);

        configurator.distributeRewards();
        vm.stopPrank();

        uint256 newRewardPerTokenstored =
            outUSDC * 1e36 / (10 ** ERC20(configurator.stableToken()).decimals()) / eslbr.totalSupply();

        assertEq(rewardsPool.rewardPerTokenStored(), newRewardPerTokenstored);
        assertEq(rewardsPool.earned(alice), eslbr.balanceOf(alice) * (newRewardPerTokenstored - 0) / 1e18);
        uint256 earnedUSDC = rewardsPool.earned(alice);

        vm.startPrank(alice);
        rewardsPool.getReward();
        // earnedUSDC / 1e12 -> Because rewardsPool.earned output has 18 decimal and USDC has 6 decimals
        assertEq(usdc.balanceOf(alice), earnedUSDC / 1e12);
        vm.stopPrank();
    }
}

Tools Used

  • Foundry

Assessed type

Math

#0 - c4-pre-sort

2023-07-10T01:48:47Z

JeffCX marked the issue as duplicate of #501

#1 - c4-judge

2023-07-28T15:40:18Z

0xean marked the issue as satisfactory

#2 - c4-judge

2023-07-28T19:44:12Z

0xean changed the severity to 2 (Med Risk)

#3 - c4-judge

2023-07-28T20:39:20Z

0xean marked the issue as selected for report

#4 - c4-sponsor

2023-07-29T09:01:33Z

LybraFinance marked the issue as sponsor confirmed

Awards

9.931 USDC - $9.93

Labels

bug
downgraded by judge
grade-b
QA (Quality Assurance)
satisfactory
edited-by-warden
duplicate-108
Q-27

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/7b73ef2fbb542b569e182d9abf79be643ca883ee/contracts/lybra/configuration/LybraConfigurator.sol#L126-L130

Vulnerability details

Vulnerability Details

Configurator contract has external functions to set vaultBadCollateralRatio and vaultSafeCollateralRatio of pools, In the peUSD pool type vaultSafeCollateralRatio of the pool checks incorrectly.

Impact

DAO can set vaultSafeCollateralRatio of peUSD base pools lower than vaultBadCollateralRatio.

Proof of Concept

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import {Test, console} from "forge-std/Test.sol";
import {GovernanceTimelock} from "contracts/lybra/governance/GovernanceTimelock.sol";
import {mockCurve} from "contracts/mocks/mockCurve.sol";
import {Configurator} from "contracts/lybra/configuration/LybraConfigurator.sol";
import {LybraWBETHVault} from "contracts/lybra/pools/LybraWbETHVault.sol";
import {PeUSDMainnet} from "contracts/lybra/token/PeUSDMainnetStableVision.sol";

contract LybraConfiguratorTest is Test {
    address goerliEndPoint = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address wbETH = 0xbfD2135BFfbb0B5378b56643c2Df8a87552Bfa23;
    address deployer;
    address attacker;

    address[] proposers;
    address[] executors;

    GovernanceTimelock governance;
    mockCurve curvePool;
    Configurator configurator;
    PeUSDMainnet peUsdMainnet;
    LybraWBETHVault wbETHVault;

    function setUp() public {
        deployer = makeAddr("deployer");
        attacker = makeAddr("attacker");
        vm.startPrank(deployer);
        proposers.push(deployer);
        executors.push(deployer);
        governance = new GovernanceTimelock(2, proposers, executors, deployer);
        curvePool = new mockCurve();
        configurator = new Configurator(address(governance), address(0));
        peUsdMainnet = new PeUSDMainnet(
            address(configurator),
            8,
            goerliEndPoint
        );
        // Ether oracle has no impact on this test
        wbETHVault =
        new LybraWBETHVault(address(peUsdMainnet), makeAddr("NonImportantMockForEtherOracle"), wbETH, address(configurator));
        vm.stopPrank();
    }

    function test_revertIfSafeDifferenceIsLowerThan10Percent() public {
        // Deployer had DAO role
        vm.startPrank(deployer);

        // Set safe collateral ratio - 120%
        configurator.setSafeCollateralRatio(address(wbETHVault), 120 * 1e18);
        assertEq(configurator.getSafeCollateralRatio(address(wbETHVault)), 120 * 1e18);

        // Set bad collateral ratio  - 130%
        vm.expectRevert();
        configurator.setBadCollateralRatio(address(wbETHVault), 130 * 1e18);
        vm.stopPrank();
    }
}

Tools Used

  • Foundry

Should validate vaultSafeCollateralRatio[pool] correctly:

function setBadCollateralRatio(address pool, uint256 newRatio) external onlyRole(DAO) {
        require(
            newRatio >= 130 * 1e18 && newRatio <= 150 * 1e18 && newRatio <= vaultSafeCollateralRatio[pool] - 1e19, "LNA"
        );
        vaultBadCollateralRatio[pool] = newRatio;
        emit SafeCollateralRatioChanged(pool, newRatio);
    }

Assessed type

Invalid Validation

#0 - c4-pre-sort

2023-07-10T21:41:29Z

JeffCX marked the issue as duplicate of #108

#1 - c4-judge

2023-07-28T15:44:36Z

0xean marked the issue as satisfactory

#2 - c4-judge

2023-07-28T20:47:21Z

0xean changed the severity to QA (Quality Assurance)

#3 - c4-judge

2023-07-28T20:48:39Z

0xean marked the issue as grade-b

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