Yield-Convex contest - kenzo's results

Fixed-rate borrowing and lending on Ethereum

General Information

Platform: Code4rena

Start Date: 28/01/2022

Pot Size: $30,000 USDC

Total HM: 4

Participants: 22

Period: 3 days

Judge: GalloDaSballo

Total Solo HM: 2

Id: 80

League: ETH

Yield

Findings Distribution

Researcher Performance

Rank: 2/22

Findings: 3

Award: $5,796.20

🌟 Selected for report: 2

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: leastwood

Also found by: kenzo

Labels

bug
duplicate
3 (High Risk)
resolved
sponsor confirmed

Awards

3964.904 USDC - $3,964.90

External Links

Handle

kenzo

Vulnerability details

ConvexStakingWrapper saves data for reward calculation in dedicated variables for each user, such as reward.reward_integral_for[account]. These variables are not updated when transferring wrapped staked tokens. (Please note that Convex's original ConvexStakingWrapper does update the rewards state before transfer.)

Impact

Wrong accounting for rewards.

Proof of Concept

Let's say Roy hasn't claimed his rewards, and transfers tokens to Zora. Next time Roy will call getReward, it will call the _checkpointAndClaim method, which will call _getDepositedBalance to calculate Roy's balance. Since his balance is now 0, and he didn't have already saved pending rewards (as no checkpoint was created when transferring the tokens), the function will calculate that he has no pending rewards. His rewards have been lost in time, like tears in rain.

Add the following code, like Convex does:

function _beforeTokenTransfer(address _from, address _to, uint256 _amount) internal override { _checkpoint([_from, _to]); }

(Note: you can then remove the _checkpoint calls from wrap and unwrap).

#0 - iamsahu

2022-02-01T20:37:22Z

The solution provided won't work. The _checkpoint before a token transfer needs to be conditional otherwise it would end up allocating the wrapper contract some of the rewards.

#1 - alcueca

2022-02-02T12:12:07Z

Still, the issue is valid. Duplicate of #86, which we agreed to be Sev 3.

Findings Information

🌟 Selected for report: throttle

Also found by: 0x1f8b, TomFrenchBlockchain, WatchPug, cccz, defsec, hack3r-0m, hyh, kenzo, leastwood, sirhashalot, ye0lde

Labels

bug
duplicate
2 (Med Risk)

Awards

69.1238 USDC - $69.12

External Links

Handle

kenzo

Vulnerability details

When querying Chainlink for stable prices, Cvx3CrvOracle doesn't run sanity checks against stale or incomplete results. This is unlike Yield's ChainlinkMultiOracle, which does execute those checks.

Impact

Stale or incorrect results might be returned.

Proof of Concept

When querying Chainlink, Cvx3CrvOracle only checks that the price is bigger than 0:

(, int256 daiPrice, , , ) = DAI.latestRoundData(); (, int256 usdcPrice, , , ) = USDC.latestRoundData(); (, int256 usdtPrice, , , ) = USDT.latestRoundData(); require(daiPrice > 0 && usdcPrice > 0 && usdtPrice > 0, "Chainlink pricefeed reporting 0");

But further checks are needed to verify the price is not stale, as Yield's ChainlinkMultiOracle does:

(roundId, price,, updateTime, answeredInRound) = AggregatorV3Interface(source.source).latestRoundData(); require(price > 0, "Chainlink price <= 0"); require(updateTime != 0, "Incomplete round"); require(answeredInRound >= roundId, "Stale price");

Add the missing sanity checks.

#0 - iamsahu

2022-02-01T19:28:23Z

#136

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