Platform: Code4rena
Start Date: 22/09/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 133
Period: 3 days
Judge: 0xean
Total Solo HM: 2
Id: 165
League: ETH
Rank: 21/133
Findings: 2
Award: $156.96
π Selected for report: 0
π Solo Findings: 0
π Selected for report: __141345__
Also found by: Bahurum, Ch_301, Chom, Respx, Trust, datapunk, ronnyx2017
128.9427 USDC - $128.94
https://github.com/corddry/ERC4626/blob/643cd044fac34bcbf64e1c3790a5126fec0dbec1/src/xERC4626.sol#L45 https://github.com/corddry/ERC4626/blob/643cd044fac34bcbf64e1c3790a5126fec0dbec1/src/xERC4626.sol#L78
rewards is not accurately reflected in totalAssets() once block.timestamp has passed rewardsCycledEnd, as it will simply add the lastReward, but not linearly interpolated the nextReward. This becomes more of an issue, if syncRewards() calls have been skipped for multiple cycles. When this happens, rewards will be unfairly distributed among depositors, benefitting late depositors.
https://github.com/corddry/ERC4626/blob/643cd044fac34bcbf64e1c3790a5126fec0dbec1/src/xERC4626.sol#L45 https://github.com/corddry/ERC4626/blob/643cd044fac34bcbf64e1c3790a5126fec0dbec1/src/xERC4626.sol#L78 assume syncRewards() has not been called for multiple rewardsCycleLength, the cumulated nextRewards can grow quite large, yet not included in the totalAssets() calculation.
totalAssets() are relied upom for assets/shares conversion. For these cases, the best solution is to call syncRewards() before calling totalAssets(), which will give correct cumulated nextRewards, and totalAssets() will then linearly interpolated nextRewards. Note, this is still not the most accurate solution, since no syncRewards catchup calls can be made. The accummulated rewards cannot be immediately accounted for in totalAssets(), but rather gradually through next cycle.
#0 - 0xean
2022-10-13T23:46:04Z
rough dupe of #110
π Selected for report: rotcivegaf
Also found by: 0x040, 0x1f8b, 0x4non, 0xNazgul, 0xSmartContract, 0xf15ers, 8olidity, Aymen0909, B2, Bahurum, Bnke0x0, Ch_301, CodingNameKiki, Deivitto, Diana, Funen, IllIllI, JC, JLevick, KIntern_NA, Lambda, OptimismSec, PaludoX0, RockingMiles, Rolezn, Sm4rty, Soosh, Tagir2003, Tointer, TomJ, Triangle, Trust, V_B, Waze, Yiko, __141345__, a12jmx, ajtra, asutorufos, ayeslick, aysha, bbuddha, bharg4v, bobirichman, brgltd, bytera, c3phas, cryptostellar5, cryptphi, csanuragjain, datapunk, delfin454000, durianSausage, exd0tpy, gogo, got_targ, jag, joestakey, karanctf, ladboy233, leosathya, lukris02, mics, millersplanet, natzuu, neko_nyaa, obront, oyc_109, parashar, peritoflores, rbserver, ret2basic, rokinot, ronnyx2017, rvierdiiev, sach1r0, seyni, sikorico, slowmoses, tnevler, yasir, yongskiws
28.0172 USDC - $28.02
not checking address(0) in frxEth constructor
check
require(_creator_address!=address(0))
require(_timelock_address!=address(0))
both timelock_address and owner have admin privilages.
https://github.com/code-423n4/2022-09-frax/blob/55ea6b1ef3857a277e2f47d42029bc0f3d6f9173/src/ERC20/ERC20PermitPermissionedMint.sol#L41 https://github.com/code-423n4/2022-09-frax/blob/55ea6b1ef3857a277e2f47d42029bc0f3d6f9173/src/OperatorRegistry.sol#L45
need to make sure both addresses are multi-sig
To avoid potentially assigning to the wrong time_lock address, pull model can be implemented instead
https://github.com/code-423n4/2022-09-frax/blob/55ea6b1ef3857a277e2f47d42029bc0f3d6f9173/src/ERC20/ERC20PermitPermissionedMint.sol#L94 https://github.com/code-423n4/2022-09-frax/blob/55ea6b1ef3857a277e2f47d42029bc0f3d6f9173/src/OperatorRegistry.sol#L202
assign new time_lock to pending_time_lock, let pending_time_lock claim explicitly
removeValidator() and keeping order can be made more gas efficient
function orderedArray(uint index) public{ for(uint i = remove_idx; i < validators.length-1; ){ validators[i] = validators[i+1]; } validators.pop(); unchecked { ++i; } }
gas saving for loops
as one example https://github.com/code-423n4/2022-09-frax/blob/55ea6b1ef3857a277e2f47d42029bc0f3d6f9173/src/OperatorRegistry.sol#L84
check to
for (uint256 i; i < times; ++i) { validators.pop(); unchecked { ++i; } }
gas saving from revert with ERROR instead of require
as one example https://github.com/code-423n4/2022-09-frax/blob/55ea6b1ef3857a277e2f47d42029bc0f3d6f9173/src/OperatorRegistry.sol#L46
change to
if (msg.sender != timelock_address && msg.sender != owner) revert NOT_OWNER_OR_TIMELOCK()");