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
Rank: 10/132
Findings: 4
Award: $1,559.50
🌟 Selected for report: 1
🚀 Solo Findings: 1
80.4648 USDC - $80.46
When getting proposal information in proposals(), supportVotes[0] means forVotes and supportVotes[1] means againstVotes.
function proposals(uint256 proposalId) external view returns (uint256 id, address proposer, uint256 eta, uint256 startBlock, uint256 endBlock, uint256 forVotes, uint256 againstVotes, uint256 abstainVotes, bool canceled, bool executed) { id = proposalId; eta = proposalEta(proposalId); startBlock = proposalSnapshot(proposalId); endBlock = proposalDeadline(proposalId); proposer = proposalProposer(proposalId); forVotes = proposalData[proposalId].supportVotes[0]; againstVotes = proposalData[proposalId].supportVotes[1]; abstainVotes = proposalData[proposalId].supportVotes[2];
However, in _quorumReached() and _voteSucceeded(), supportVotes[0] is used for againstVotes and supportVotes[1] for forVotes.
function _quorumReached(uint256 proposalId) internal view override returns (bool){ return proposalData[proposalId].supportVotes[1] + proposalData[proposalId].supportVotes[2] >= quorum(proposalSnapshot(proposalId)); } function _voteSucceeded(uint256 proposalId) internal view override returns (bool){ return proposalData[proposalId].supportVotes[1] > proposalData[proposalId].supportVotes[0]; }
This inconsistency may cause users to make opposite votes on proposals, so that malicious proposals are executed and correct ones are against
https://github.com/code-423n4/2023-06-lybra/blob/5d70170f2c68dbd3f7b8c0c8fd6b0b2218784ea6/contracts/lybra/governance/LybraGovernance.sol#L59-L68 https://github.com/code-423n4/2023-06-lybra/blob/5d70170f2c68dbd3f7b8c0c8fd6b0b2218784ea6/contracts/lybra/governance/LybraGovernance.sol#L112-L122
None
Change to
function _quorumReached(uint256 proposalId) internal view override returns (bool){ - return proposalData[proposalId].supportVotes[1] + proposalData[proposalId].supportVotes[2] >= quorum(proposalSnapshot(proposalId)); + return proposalData[proposalId].supportVotes[0] + proposalData[proposalId].supportVotes[2] >= quorum(proposalSnapshot(proposalId)); } /** * @dev Is the proposal successful or not. */ function _voteSucceeded(uint256 proposalId) internal view override returns (bool){ - return proposalData[proposalId].supportVotes[1] > proposalData[proposalId].supportVotes[0]; + return proposalData[proposalId].supportVotes[0] > proposalData[proposalId].supportVotes[1]; }
Error
#0 - c4-pre-sort
2023-07-09T12:58:07Z
JeffCX marked the issue as duplicate of #15
#1 - c4-judge
2023-07-28T15:33:04Z
0xean marked the issue as satisfactory
🌟 Selected for report: cccz
1444.4545 USDC - $1,444.45
In LybraPeUSDVaultBase, the return value of getBorrowedOf represents the user's debt, while borrowed only represents the user's borrowed funds and does not include fees. Using borrowed instead of getBorrowedOf in rigidRedemption results in
function rigidRedemption(address provider, uint256 peusdAmount) external virtual { require(configurator.isRedemptionProvider(provider), "provider is not a RedemptionProvider"); require(borrowed[provider] >= peusdAmount, "peusdAmount cannot surpass providers debt"); uint256 assetPrice = getAssetPrice(); uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / borrowed[provider]; require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%"); _repay(msg.sender, provider, peusdAmount); uint256 collateralAmount = (((peusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000; depositedAsset[provider] -= collateralAmount; collateralAsset.transfer(msg.sender, collateralAmount); emit RigidRedemption(msg.sender, provider, peusdAmount, collateralAmount, block.timestamp); }
None
Change to
function rigidRedemption(address provider, uint256 peusdAmount) external virtual { require(configurator.isRedemptionProvider(provider), "provider is not a RedemptionProvider"); - require(borrowed[provider] >= peusdAmount, "peusdAmount cannot surpass providers debt"); + require(getBorrowedOf(provider) >= peusdAmount, "peusdAmount cannot surpass providers debt"); uint256 assetPrice = getAssetPrice(); - uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / borrowed[provider]; + uint256 providerCollateralRatio = (depositedAsset[provider] * assetPrice * 100) / getBorrowedOf(provider); require(providerCollateralRatio >= 100 * 1e18, "provider's collateral ratio should more than 100%"); _repay(msg.sender, provider, peusdAmount); uint256 collateralAmount = (((peusdAmount * 1e18) / assetPrice) * (10000 - configurator.redemptionFee())) / 10000; depositedAsset[provider] -= collateralAmount; collateralAsset.transfer(msg.sender, collateralAmount); emit RigidRedemption(msg.sender, provider, peusdAmount, collateralAmount, block.timestamp); }
Error
#0 - c4-pre-sort
2023-07-11T00:03:59Z
JeffCX marked the issue as primary issue
#1 - c4-sponsor
2023-07-18T07:41:37Z
LybraFinance marked the issue as sponsor confirmed
#2 - c4-judge
2023-07-26T13:08:00Z
0xean marked the issue as satisfactory
#3 - c4-judge
2023-07-28T20:42:36Z
0xean marked the issue as selected for report
🌟 Selected for report: hl_
Also found by: 0xRobocop, Co0nan, CrypticShepherd, DedOhWale, Iurii3, Kenshin, Musaka, OMEN, RedOneN, SpicyMeatball, Toshii, Vagner, bytes032, cccz, gs8nrv, hl_, kenta, lanrebayode77, mahdikarimi, max10afternoon, peanuts, pep7siup
5.5262 USDC - $5.53
In LybraPeUSDVaultBase, the user has to repay the borrowings(borrowed
) and fees(feeStored
).
When the user repays in the _repay function, the fees is repaid first and the borrowings is repaid later. However, the repaid fees is not subtracted from the _amount, and then the _amount is subtracted directly from the borrowed
, which results in the user not having to repay the fees.
function _repay(address _provider, address _onBehalfOf, uint256 _amount) internal virtual { try configurator.refreshMintReward(_onBehalfOf) {} catch {} _updateFee(_onBehalfOf); uint256 totalFee = feeStored[_onBehalfOf]; uint256 amount = borrowed[_onBehalfOf] + totalFee >= _amount ? _amount : borrowed[_onBehalfOf] + totalFee; if(amount >= totalFee) { feeStored[_onBehalfOf] = 0; PeUSD.transferFrom(_provider, address(configurator), totalFee); PeUSD.burn(_provider, amount - totalFee); } else { feeStored[_onBehalfOf] = totalFee - amount; PeUSD.transferFrom(_provider, address(configurator), amount); } try configurator.distributeRewards() {} catch {} borrowed[_onBehalfOf] -= amount; poolTotalPeUSDCirculation -= amount;
Consider Alice borrowing 10000 PeUSD and the fee is 100 PeUSD. Alice has to repay a total of 10,100 PeUSD. Alice calls burn to repay 10,000 PeUSD, and in _repay, the 100 PeUSD fee is repaid first, but since amount is not subtracted from 100, borrowed is then directly subtracted from 10,000. Alice ends up repaying the 10,100 PeUSD debt with 10,000 PeUSD.
None
Change to
if(amount >= totalFee) { feeStored[_onBehalfOf] = 0; PeUSD.transferFrom(_provider, address(configurator), totalFee); PeUSD.burn(_provider, amount - totalFee); - amount -= totalFee; } else { feeStored[_onBehalfOf] = totalFee - amount; PeUSD.transferFrom(_provider, address(configurator), amount); - amount = 0; } try configurator.distributeRewards() {} catch {} borrowed[_onBehalfOf] -= amount; poolTotalPeUSDCirculation -= amount;
Math
#0 - c4-pre-sort
2023-07-10T11:31:40Z
JeffCX marked the issue as duplicate of #532
#1 - c4-judge
2023-07-28T15:39:33Z
0xean marked the issue as satisfactory
#2 - c4-judge
2023-07-28T19:41:43Z
0xean changed the severity to 2 (Med Risk)
🌟 Selected for report: Musaka
Also found by: 0xhacksmithh, 0xnev, CrypticShepherd, LuchoLeonel1, T1MOH, bytes032, cccz, devival, josephdara, ktg, squeaky_cactus
29.0567 USDC - $29.06
In LybraGovernance, the votingPeriod is 3, which means that for a proposal, users only have 3 block times to vote, which is too short and leads to the possibility that users may not be able to vote due to lack of time.
function votingPeriod() public pure override returns (uint256){ return 3; } function votingDelay() public pure override returns (uint256){ return 1; }
None
Consider extending the votingPeriod
Context
#0 - c4-pre-sort
2023-07-04T14:11:47Z
JeffCX marked the issue as duplicate of #268
#1 - c4-judge
2023-07-28T15:43:53Z
0xean marked the issue as satisfactory