Lybra Finance - totomanov'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: 43/132

Findings: 2

Award: $212.43

QA:
grade-b

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Findings Information

🌟 Selected for report: Kenshin

Also found by: 0xNightRaven, Breeje, totomanov

Labels

bug
2 (Med Risk)
satisfactory
duplicate-794

Awards

202.5014 USDC - $202.50

External Links

Lines of code

https://github.com/code-423n4/2023-06-lybra/blob/main/contracts/lybra/configuration/LybraConfigurator.sol#L110

Vulnerability details

Impact

Denial of service to distributeRewards(), potentially delaying reward distribution indefinitely.

Proof of Concept

Trade slippage in a Curve StableSwap pool increases with size (relative to liquidity) and decreases with the amplification factor A.

0.2% is practically too low to work under all market conditions. When

  • Allow for distributeRewards() to be called on a part of the rewards.
  • Make slippage configurable by the DAO or calculate it dynamically depending on size and A.

Assessed type

DoS

#0 - c4-pre-sort

2023-07-08T15:12:22Z

JeffCX marked the issue as duplicate of #841

#1 - c4-judge

2023-07-25T20:28:59Z

0xean changed the severity to QA (Quality Assurance)

#2 - c4-judge

2023-07-26T13:00:19Z

This previously downgraded issue has been upgraded by 0xean

#3 - c4-judge

2023-07-26T13:00:35Z

0xean marked the issue as duplicate of #794

#4 - c4-judge

2023-07-28T15:37:54Z

0xean marked the issue as satisfactory

Awards

9.931 USDC - $9.93

Labels

bug
disagree with severity
downgraded by judge
grade-b
primary issue
QA (Quality Assurance)
sponsor acknowledged
Q-42

External Links

Lines of code

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

Vulnerability details

Impact

Each unstaking operation results in an expected loss of 3.9M wei LBR for the user. The loss cumulates with every consecutive unstaking operations.

Proof of Concept

When a user calls unstake, a quantity unstakeRatio is calculated for the user, which represents the LBR unlocked per second, such that after exitCycle (90 days or 7776000 seconds), the claimable LBR is 100% of the stake.

// ProtocolRewardsPool.sol#L95 unstakeRatio[msg.sender] = total / exitCycle;

Due to Solidity's integer division unstakeRatio may have a remainder of up to 7775999, which will be unaccounted for. In practice this means that calculating the claimable LBR at any point in time between 0 and 90 days will on average make the user lose 3888000 wei of dust.

This loss wiull cumulate if the user intermittently calls unstake while waiting for another unstake to finish. After n intermittent calls a user will (roughly) lose on average n*3888000 wei.

All losses are irrecoverable for the user.

PoC Test

The following test file demonstrates that residuals can cumulate. In the test case an aggregate loss of 311,839,927 wei is reached after 79 consecutive unstaking operations.

// test/poc.test.js
const { ethers } = require("hardhat");
const { time } = require("@nomicfoundation/hardhat-network-helpers");

const { parseEther, formatEther } = ethers.utils;

const days = (n) => Math.floor(n * 24 * 60 * 60);

it('ProtocolRewardsPool.sol residual stacking PoC', async function () {    
    const [owner, user] = await ethers.getSigners();

    // 1. Setup
    const dao = await ethers.deployContract("GovernanceTimelock", [1, [owner.address], [owner.address], owner.address]);
    const configurator = await ethers.deployContract("Configurator", [dao.address, ethers.constants.AddressZero]);
    const esLBR = await ethers.deployContract("esLBR", [configurator.address]);
    const LBR = await ethers.deployContract("LBR", [configurator.address, 18, ethers.constants.AddressZero]);
    const esLBRBoost = await ethers.deployContract("esLBRBoost", []);
    const pool = await ethers.deployContract("ProtocolRewardsPool", [configurator.address]);

    await pool.setTokenAddress(esLBR.address, LBR.address, esLBRBoost.address);
    await configurator.setProtocolRewardsPool(pool.address);
    await configurator.setTokenMiner([owner.address, pool.address], [true, true]);

    // 2. Stake 100 LBR locked for 30 days
    await LBR.mint(user.address, parseEther("100"));
    await esLBRBoost.connect(user).setLockStatus(0);
    await LBR.connect(user).approve(pool.address, ethers.constants.MaxUint256);
    await pool.connect(user).stake(parseEther("100"));

    await time.increase(days(30));

    // 3. Stake and unstake lots of times
    const unstakeAmt = parseEther('0.001');
    const timeStep = days(1);
    const repetitions = 79;
    for(let i = 0; i < repetitions; i++) {
        await pool.connect(user).unstake(unstakeAmt);
        await time.increase(timeStep);
    }

    await time.increase(days(90));

    // 4. Withdraw all LBR and check loss
    await pool.withdraw(user.address);

    const expectedLBR = unstakeAmt.mul(repetitions);
    const actualLBR = await LBR.balanceOf(user.address);
    const absoluteLoss = expectedLBR.sub(actualLBR);

    // Absolute loss 311839927 after 79x unstake(0.001)
    console.log(`Absolute loss ${absoluteLoss.toString()} after ${repetitions}x unstake(${formatEther(unstakeAmt)})`);
});

Tools Used

Hardhat, mocha

Increase the precision of unstakeRatio by multiplying the numerator by 1e18, for example.

Assessed type

Math

#0 - c4-pre-sort

2023-07-10T12:58:23Z

JeffCX marked the issue as primary issue

#1 - c4-sponsor

2023-07-18T06:12:44Z

LybraFinance marked the issue as disagree with severity

#2 - LybraFinance

2023-07-18T06:12:50Z

Loss can be negligible.

#3 - c4-judge

2023-07-26T17:02:41Z

0xean changed the severity to QA (Quality Assurance)

#4 - c4-judge

2023-07-28T17:17:43Z

0xean marked the issue as grade-b

#5 - c4-sponsor

2023-07-29T11:25:41Z

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