Yieldy contest - cccz's results

A protocol for gaining single side yields on various tokens.

General Information

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

Yieldy

Findings Distribution

Researcher Performance

Rank: 5/99

Findings: 5

Award: $2,790.65

🌟 Selected for report: 2

🚀 Solo Findings: 2

Findings Information

🌟 Selected for report: WatchPug

Also found by: BowTiedWardens, cccz, minhquanym, parashar, pashov, shung, zzzitron

Labels

bug
duplicate
3 (High Risk)

Awards

241.4803 USDC - $241.48

External Links

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L428-L447

Vulnerability details

Impact

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); }

Proof of Concept

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L428-L447

Tools Used

None

Consider only allowing users to stake tokens for themselves

#1 - toshiSat

2022-06-28T15:33:23Z

duplicate #187 and #245

Findings Information

🌟 Selected for report: 0xDjango

Also found by: BowTiedWardens, Metatron, cccz, hansfriese, shung, ych18, zzzitron

Labels

bug
duplicate
2 (Med Risk)

Awards

72.4441 USDC - $72.44

External Links

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L157-L160

Vulnerability details

Impact

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.

Proof of Concept

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L157-L160

Tools Used

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

Findings Information

🌟 Selected for report: cccz

Labels

bug
2 (Med Risk)
sponsor acknowledged

Awards

1211.7009 USDC - $1,211.70

External Links

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L701-L719

Vulnerability details

Impact

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; } } }

Proof of Concept

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L701-L719

Tools Used

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.

Findings Information

🌟 Selected for report: cccz

Labels

bug
2 (Med Risk)
resolved
sponsor confirmed

Awards

1211.7009 USDC - $1,211.70

External Links

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L674-L696

Vulnerability details

Impact

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.

Proof of Concept

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

Tools Used

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.

Lines of code

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L167-L170

Vulnerability details

Impact

The owner can set affiliateFee to 10000 in the setAffiliateFee function of the Staking contract, which may frontrun the claimFromToken function.

Proof of Concept

https://github.com/code-423n4/2022-06-yieldy/blob/524f3b83522125fb7d4677fa7a7e5ba5a2c0fe67/src/contracts/Staking.sol#L167-L170

Tools Used

None

Consider limiting affiliateFee in setAffiliateFee function

#0 - toshiSat

2022-06-27T23:19:17Z

duplicate #211

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter