Platform: Code4rena
Start Date: 21/06/2022
Pot Size: $50,000 USDC
Total HM: 31
Participants: 99
Period: 5 days
Judges: moose-code, JasoonS, denhampreen
Total Solo HM: 17
Id: 139
League: ETH
Rank: 5/99
Findings: 5
Award: $2,790.65
🌟 Selected for report: 2
🚀 Solo Findings: 2
🌟 Selected for report: WatchPug
Also found by: BowTiedWardens, cccz, minhquanym, parashar, pashov, shung, zzzitron
In the stake function of the Staking contract, anyone can stake tokens for others. And each time a token is staked, the lock time of all tokens is increased. This allows an attacker to stake few tokens for others to increase the lock time of others' tokens.
if (warmUpPeriod == 0) { IYieldy(YIELDY_TOKEN).mint(_recipient, _amount); } else { // create a claim and mint tokens so a user can claim them once warm up has passed warmUpInfo[_recipient] = Claim({ amount: info.amount + _amount, credits: info.credits + IYieldy(YIELDY_TOKEN).creditsForTokenBalance(_amount), expiry: epoch.number + warmUpPeriod }); IYieldy(YIELDY_TOKEN).mint(address(this), _amount); }
None
Consider only allowing users to stake tokens for themselves
#0 - JustDravee
2022-06-27T17:22:52Z
#1 - toshiSat
2022-06-28T15:33:23Z
duplicate #187 and #245
🌟 Selected for report: 0xDjango
Also found by: BowTiedWardens, Metatron, cccz, hansfriese, shung, ych18, zzzitron
The setCurvePool function of the Staking contract is used to update the CURVE_POOL variable, but the TOKE_POOL is not approved to the new CURVE_POOL in the setCurvePool function, which causes the instantUnstakeCurve function to not work.
None
function setCurvePool(address _curvePool) external onlyOwner { + IERC20(TOKE_POOL).approve(CURVE_POOL, 0); CURVE_POOL = _curvePool; + IERC20(TOKE_POOL).approve(CURVE_POOL, type(uint256).max); setToAndFromCurve(); }
#0 - JustDravee
2022-06-27T17:13:15Z
Same as https://github.com/code-423n4/2022-06-yieldy-findings/issues/264
Little suggestion for the warden, you can use the ```diff
syntax highlighting to get some colors:
function setCurvePool(address _curvePool) external onlyOwner { + IERC20(TOKE_POOL).approve(CURVE_POOL, 0); CURVE_POOL = _curvePool; + IERC20(TOKE_POOL).approve(CURVE_POOL, type(uint256).max); setToAndFromCurve(); }
Some tutorial: https://welearncode.com/create-diff-markdown/
#1 - toshiSat
2022-06-28T16:51:08Z
duplicate #165
🌟 Selected for report: cccz
1211.7009 USDC - $1,211.70
In the staking contract, the rebase function can only be called once per epoch. In the rebase function, the rewards of the current epoch are used in the next epoch, which can cause the rewards to be updated incorrectly and lead to incorrect distribution of user rewards.
function rebase() public { // we know about the issues surrounding block.timestamp, using it here will not cause any problems if (epoch.endTime <= block.timestamp) { IYieldy(YIELDY_TOKEN).rebase(epoch.distribute, epoch.number); // 懒更新 epoch.endTime = epoch.endTime + epoch.duration; epoch.timestamp = block.timestamp; epoch.number++; uint256 balance = contractBalance(); uint256 staked = IYieldy(YIELDY_TOKEN).totalSupply(); if (balance <= staked) { epoch.distribute = 0; } else { epoch.distribute = balance - staked; } } }
None
Put IYieldy(YIELDY_TOKEN).rebase after epoch.distribute update
function rebase() public { // we know about the issues surrounding block.timestamp, using it here will not cause any problems if (epoch.endTime <= block.timestamp) { uint256 balance = contractBalance(); uint256 staked = IYieldy(YIELDY_TOKEN).totalSupply(); if (balance <= staked) { epoch.distribute = 0; } else { epoch.distribute = balance - staked; } IYieldy(YIELDY_TOKEN).rebase(epoch.distribute, epoch.number); epoch.endTime = epoch.endTime + epoch.duration; epoch.timestamp = block.timestamp; epoch.number++; } }
#0 - toshiSat
2022-06-28T15:38:51Z
This is how the system is designed
#1 - JasoonS
2022-07-30T14:34:51Z
Changing to medium. It makes sense that the rebase happens after rewards so that those who enter later don't affect the distribution of rewards before they joined.
🌟 Selected for report: cccz
1211.7009 USDC - $1,211.70
In the Yieldy contract, functions such as balanceOf/creditsForTokenBalance/tokenBalanceForCredits/transfer/transferFrom/burn/mint will use the rebasingCreditsPerToken variable, so before calling these functions in the Staking contract, make sure that the rebase of this epoch has occurred. Therefore, the rebase function should also be called in the unstake/claim/claimWithdraw function of the Staking contract.
https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L674-L696 https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L465-L508
None
function claim(address _recipient) public { Claim memory info = warmUpInfo[_recipient]; + rebase(); ... function claimWithdraw(address _recipient) public { Claim memory info = coolDownInfo[_recipient]; + rebase(); ... function unstake(uint256 _amount, bool _trigger) external { // prevent unstaking if override due to vulnerabilities asdf require(!isUnstakingPaused, "Unstaking is paused"); - if (_trigger) { rebase(); - }
#0 - JasoonS
2022-07-29T08:37:39Z
Yes, seems like a logic flaw, makes sense as Medium.
🌟 Selected for report: IllIllI
Also found by: 0x1337, 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xc0ffEE, 0xf15ers, 0xmint, Bnke0x0, BowTiedWardens, Chom, ElKu, FudgyDRS, Funen, GalloDaSballo, GimelSec, JC, Kaiziron, Lambda, Limbooo, Metatron, MiloTruck, Noah3o6, Picodes, PumpkingWok, PwnedNoMore, Sm4rty, StErMi, TomJ, TrungOre, UnusualTurtle, Waze, _Adam, aga7hokakological, ak1, antonttc, berndartmueller, cccz, cryptphi, csanuragjain, defsec, delfin454000, dipp, elprofesor, exd0tpy, fatherOfBlocks, hake, hansfriese, hubble, joestakey, kenta, ladboy233, mics, oyc_109, pashov, pedr02b2, reassor, robee, samruna, scaraven, shung, sikorico, simon135, sseefried, tchkvsky, unforgiven, zzzitron
53.3348 USDC - $53.33
The owner can set affiliateFee to 10000 in the setAffiliateFee function of the Staking contract, which may frontrun the claimFromToken function.
None
Consider limiting affiliateFee in setAffiliateFee function
#0 - toshiSat
2022-06-27T23:19:17Z
duplicate #211