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: 9/62
Findings: 3
Award: $2,280.15
π Selected for report: 0
π Solo Findings: 0
π Selected for report: cccz
Also found by: minhquanym
https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/farming/LPFarming.sol#L190 https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/farming/LPFarming.sol#L300
In both function _updatePool
and pendingReward
, lpSupply
of that pool
is calculated as pool.lpToken.balanceOf(address(this))
.
But balance of lpToken
can increase not only by calling deposit
in that pool
but also by deposit
in another pool which has the same lpToken
or simply by transfering lpToken
directly to LPFarming
contract.
In that case, accRewardPerShare
is calculated using wrong amount of lpToken
supply and its value will be lower than expected and total reward all users able to claim will decrease.
https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/farming/LPFarming.sol#L190 https://github.com/code-423n4/2022-04-jpegd/blob/e72861a9ccb707ced9015166fbded5c97c6991b6/contracts/farming/LPFarming.sol#L300
Consider the following scenario
LPFarming
currently has 2 pools A and B with the same lpToken
X. Both have the same allocPoint = 10
. Total reward is 5000
JPEG. So each pool is allocated 2500
JPEG reward.500
token X into A, now lpSupply
of both A and B is 500
.500
token X into B, now lpSupply
of both A and B is 1000
.5000
token X to address of LPFarming
, now lpSupply
of both A and B is 6000
.2500
JPEG reward. But actually she only received 2500 * 500 / 6000 = 208.33
JPEG.Manual code review
Add 1 more variable into PoolInfo
struct to keep track of lpSupply
.
struct PoolInfo { IERC20 lpToken; uint256 allocPoint; uint256 lastRewardBlock; uint256 accRewardPerShare; uint256 lpSupply; }
And change line 190 and 300 to
uint256 lpSupply = pool.lpSupply;
#0 - spaghettieth
2022-04-11T17:29:40Z
Duplicate of #1
π Selected for report: AuditsAreUS
Also found by: minhquanym
1064.3207 USDC - $1,064.32
In function NFTVault._calculateAdditionalInterest
we should multiply by elapsedTime
first then divide by 365 days
for better accuracy.
We are working with uint
so the result of operator β/β is the quotient.
For example:
(5 / 2) * 4 = 2 * 4 = 8
(5 * 4) / 2 = 20 / 2 = 10
Manual code review
Change line 593 - 595 to
return (interestPerYear * elapsedTime) / 365 days;
#0 - spaghettieth
2022-04-14T14:27:11Z
#1 - dmvt
2022-04-26T16:34:12Z
Duplicate of #97
π 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
151.5077 USDC - $151.51
Function FungibleAssetVaultForDAO.withdraw
in line 201 uses native transfer
function to send ETH to msg.sender
.
This is unsafe as transfer has hard coded gas budget (2300 gas) and can fail when the user is a smart contract. Especially when this contract is for DAO and ecosystem contracts as documentation.
Manual code review
All functions have a nonReentrant
modifier already, so reentrancy is not an issue and transfer()
can be replaced.
Using low-level call.value(amount) with the corresponding result check or using the OpenZeppelin Address.sendValue is advised https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L60
#0 - spaghettieth
2022-04-11T17:33:59Z
The DAO is a gnosis safe which can receive ether with the transfer
function. Severity should be 0.
#1 - spaghettieth
2022-04-14T14:28:32Z
#2 - dmvt
2022-04-26T14:12:26Z
Worst case scenario, a normal address could be whitelisted and the funds recovered. It would be annoying to deal with but not a real problem. This is QA.
#3 - JeeberC4
2022-05-02T19:12:29Z
Generating QA Report as judge downgraded issue. Preserving original title: Using transfer for native token do not work when recipient is smart contract.