Platform: Code4rena
Start Date: 07/04/2022
Pot Size: $100,000 USDC
Total HM: 20
Participants: 62
Period: 7 days
Judge: LSDan
Total Solo HM: 11
Id: 107
League: ETH
Rank: 11/62
Findings: 4
Award: $1,486.87
🌟 Selected for report: 0
🚀 Solo Findings: 0
An early depositor to yVault.sol
, preferably the first to deposit, will have the ability to steal funds from subsequent user deposits. The malicious user is able to do this by directly transferring tokens to either the yVault or Controller contracts following their deposit. If subsequent user deposits are less than the amount of token sent directly to the contracts, they will lose funds because their tokens will successfully transfer to the contract while they will be minted 0 shares
Steps to exploit:
1 wei
of token. User A receives 1 share since supply is currently 0 based on the following clause.if (supply == 0) { shares = _amount;
yVault.sol
which will inflate the balance of the contract. For this example, User A sends 10,000 ether
of token to the contract.5,000 ether
of token. Based on the following formula, they will receive 0 shares
even though the funds have already been transferred to the contract.} else { shares = (_amount * supply) / balanceBefore; }
Where balanceBefore
is equal to balance()
which is equal to token.balanceOf(address(this)) + controller.balanceOf(address(token))
The formula for the example looks like:
shares = (5,000 ether * 1 share) / 10,000 ether = 0 shares
withdraw()
. Since the contracts hold 15,000 ether
of tokens and User A holds the only share, they receive all 15,000 ether.Manual review.
Instead of relying on the balance of tokens of the two contracts, use internal accounting variables to represent the current balance of tokens that have been properly deposited and withdrawn from the vault. This will remove the ability to directly transfer tokens to the contracts to alter the share distribution logic.
#0 - spaghettieth
2022-04-14T12:25:58Z
Duplicate of #12
25.7805 USDC - $25.78
https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/vaults/FungibleAssetVaultForDAO.sol#L105 https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/vaults/NFTVault.sol#L459
In the various calls to the Chainlink oracle, the deprecated API function latestAnswer()
is used. This approach is vulnerable to price manipulation and stale prices according to the Chainlink documentation.
This vulnerability was marked as Medium severity in the following Halborn and Quantstamp audits.
Download link on page. Page 4 of report explains vulnerability. https://docs.clipper.exchange/how-clipper-works/audits
Manual review.
Per the Halborn audit:
Use the latestRoundData()
function to get the price instead. Add checks on the return data with proper revert messages if the price is stale or the round is uncomplete, for example:
( roundId , rawPrice , , updateTime , answeredInRound ) = AggregatorV3Interface ( XXXXX ) . latestRoundData () ; require ( rawPrice > 0 , " Chainlink price <= 0") ; require ( updateTime != 0 , " Incomplete round "); require ( answeredInRound >= roundId , " Stale price ");
#0 - spaghettieth
2022-04-13T12:14:53Z
Duplicate of #4
🌟 Selected for report: Dravee
Also found by: 0x1f8b, 0xDjango, 0xkatana, AuditsAreUS, Cityscape, Foundation, Funen, Hawkeye, IllIllI, JC, JMukesh, Jujic, Kthere, PPrieditis, Picodes, Ruhum, TerrierLover, TrungOre, WatchPug, berndartmueller, catchup, cccz, cmichel, delfin454000, dy, ellahi, hickuphh3, horsefacts, hubble, hyh, ilan, jayjonah8, kebabsec, kenta, minhquanym, pauliax, rayn, reassor, rfa, robee, samruna
604.2923 USDC - $604.29
All contracts contain a floating pragma. It is recommended to deploy all contracts with a single, specific compiler version to reduce the risk of compiler-specific bugs and contracts deployed with different versions.
A single example can be found here: https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/tokens/JPEG.sol#L2
In LPFarming.sol
, the claim()
and claimAll()
functions do not follow a proper checks-effects-interactions pattern. There is no vulnerability in these functions due to the nonReentrant
modifier and apparent lack of control flow transfer with the JPEG token, however, it is a simple security upgrade to move the userRewards[msg.sender] = 0
line above jpeg.safeTransfer(msg.sender, rewards);
As discussed in my previously submitted Medium finding called "Owner of LPFarming.sol can DOS rewards by stopping epoch at any time", there are two discrepancies between the comments of the newEpoch()
function and the code implementation.
endBlock
". There are no checks that the new epoch's start block is greater than the original epoch's end block.Lack of access control on initialize(), can be front-run requiring re-deployment.
Several addresses set in the following initialize function do not verify that the input address are not the zero address.
.decimals()
which may not be supported for all tokensDepending on the function used to deposit in yVault.sol
, a call to token.decimals()
may return errors since decimals()
is not and ERC20 standard function.
The setContractWhitelisted()
function explains that calling this function allows the owner to whitelist contracts. This is slightly untrue since the function does not include any logic to determine if the address is a contract or not. Therefore, EOA's can be added to the contract address. The logic to decipher if the account is a contract or EOA is used higher up in the same contract. Adding a check for only contracts will make the comments match the function execution.
Insurace should be spelled insurance.
It would make sense to order the conditions of the following statements in the same fashion to increase code clarity.
address(0) == positionOwner[_nftIndex]
positionOwner[_nftIndex] == address(0)
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xNazgul, 0xkatana, Cityscape, Cr4ckM3, FSchmoede, Foundation, Funen, Hawkeye, IllIllI, JMukesh, Meta0xNull, PPrieditis, Picodes, TerrierLover, Tomio, WatchPug, berndartmueller, catchup, delfin454000, dirk_y, ellahi, hickuphh3, ilan, kebabsec, kenta, nahnah, rayn, rfa, robee, rokinot, saian, securerodd, slywaters, sorrynotsorry
80.9074 USDC - $80.91
Multiple require statements consume less gas than &&. This example is true for most setter functions for rates in this contract.
This struct could benefit slightly from struct packing, e.g. address next to bool