Open sherlock-admin4 opened 2 months ago
1 comment(s) were left on this issue during the judging contest.
panprog commented:
high, loss of additional reward from convex. Since it accumulates over 30-days period, the loss will be significant. Both
balanceOf
andsafeTransfer
are incorrect - they should be called onrewardToken().token()
. Different dups of this mention eitherbalanceOf
orsafeTransfer
, #294 mentions both (but this one is more detailed), but I consider them to be the same root cause, so all are dups.
escalate
I would escalate this to a solo issue.
Lets compare this one with #479 (and all current dups):
OCY_Convex_C
is affected.OCY_Convex
.StashTokenWrapper
.VirtualBalanceRewardPool
.OCY_Convex_C
, with different external calls.//#477 root cause: convex would wrap rewards into a StashTokenWrapper
uint256 rewardAmount = IBaseRewardPool_OCY_Convex_C(rewardContract).rewardToken().balanceOf(address(this));
//#479 and all dups root cause: convex rewardContract is a VirtualBalanceRewardPool
if (rewardAmount > 0) { IERC20(rewardContract).safeTransfer(OCT_YDL, rewardAmount); }
So, why should we duplicate two valid issues when they have different root causes, different fixes, different impacts, and the root cause of both issues do not have any code reuse?
I believe Lead judge's statement
The core reason is still incorrect address for these functions.
should not be the reason to duplicate this issue. These two different incorrect addresses are obtained through different improper external calls towards different external contracts. I don't understand why this issue was considered as dup of #479.
escalate
I would escalate this to a solo issue.
Lets compare this one with #479 (and all current dups):
477 only works with convex pool id 151+ , so only
OCY_Convex_C
is affected.479 works with ALL three
OCY_Convex
.477 points out a incorrect external call with convex's
StashTokenWrapper
.479 points out a incorrect external call with convex's
VirtualBalanceRewardPool
.477 and #479 have different fixes. The fix for one cannot fix the other.
477 and #479 have different impacts.
- If we fix 479 , In 477 , claimRewards will still not forward any extrareward to YDL.
- If we fix 477 , In 479 , claimRewards will still always revert.
477 and #479 happens on different lines in
OCY_Convex_C
, with different external calls.//#477 root cause: convex would wrap rewards into a StashTokenWrapper uint256 rewardAmount = IBaseRewardPool_OCY_Convex_C(rewardContract).rewardToken().balanceOf(address(this)); //#479 and all dups root cause: convex rewardContract is a VirtualBalanceRewardPool if (rewardAmount > 0) { IERC20(rewardContract).safeTransfer(OCT_YDL, rewardAmount); }
So, why should we duplicate two valid issues when they have different root causes, different fixes, different impacts, and the root cause of both issues do not have any code reuse?
I believe Lead judge's statement
The core reason is still incorrect address for these functions.
should not be the reason to duplicate this issue. These two different incorrect addresses are obtained through different improper external calls towards different external contracts. I don't understand why this issue was considered as dup of #479.
You've created a valid escalation!
To remove the escalation from consideration: Delete your comment.
You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.
The core reason is still incorrect address for these functions. The fix is different for different contracts, some issues describe it in more details, some in less. Since balanceOf
and transfer
have to happen on the same address, I don't think separate issues for balanceOf
or transfer
warrant it being a separate group. The same with _A or _C - it's basically the same, even if the fix in each of them is different.
I agree that your issue describes everything much better and the recommendation is more correct compared to dups, but still the core reason is the same. If these nuances warrant a separate group - this will have to be decided by Sherlock. I keep my decision here.
As for the severity: DAO can't pull these tokens, because the only function to pull tokens is overriden for this locker and only allows to pull convex pool token and nothing else.
I'm confused by the final recommendation for fixes here, can someone provide detailed explanation of fixes for appropriate contracts based on all findings?
I'm confused by the final recommendation for fixes here, can someone provide detailed explanation of fixes for appropriate contracts based on all findings?
@pseudonaut , the address of the extra reward token is:
address = IBaseRewardPool_OCY_Convex_A(rewardContract).rewardToken()
address = IBaseRewardPool_OCY_Convex_A(rewardContract).rewardToken().token()
(you'll need to add token() in the reward token interface)Both balanceOf
and safeTransfer
should be done on this address. Something like
if (extra) {
uint256 extraRewardsLength = IBaseRewardPool_OCY_Convex_A(convexRewards).extraRewardsLength();
for (uint256 i = 0; i < extraRewardsLength; i++) {
address rewardContract = IBaseRewardPool_OCY_Convex_A(convexRewards).extraRewards(i);
IERC20 rewardToken = convexPoolID < 151 ? IBaseRewardPool_OCY_Convex_A(rewardContract).rewardToken() : IBaseRewardPool_OCY_Convex_A(rewardContract).rewardToken().token();
uint256 rewardAmount = rewardToken.balanceOf(
address(this)
);
if (rewardAmount > 0) { IERC20(rewardToken).safeTransfer(OCT_YDL, rewardAmount); }
}
}
Firstly, I agree there are two issues:
rewardAmount
is called on the rewardToken
, when it should be called on rewardToken().token
safeTransfer
is called on rewardContract
when it should be called on rewardToken
.Fixing one, doesn't fix the other. I agree that general core issue is in fact using the wrong address, but the fixes and impacts are different (for 1 is not paying out the extra rewards and they're kept in the contract, for 2 is the revert of the call). Hence, I believe they should be treated separately. There is only 1 report that describes both of these vulnerabilities, which is #294, therefore, I believe it would be fair to make the following issue families:
Getting rewardAmount
on rewardToken
:
Calling safeTransfer
on rewardContract
:
The reason why #294 is in the 1st family is because it mentions both issues, but since one report cannot get 2 rewards, it'll be fair to include it in the family with less reports.
Lastly, I believe in both situations there are no extensive external limitations leading to a loss of funds. Please correct me if any assumption above is wrong.
The protocol team fixed this issue in the following PRs/commits: https://github.com/Zivoe/zivoe-core-foundry/pull/273
@WangSecurity Agree with your decision. Keeping #294 a dup of this also looks correct.
Result: High Has duplicates
The Lead Senior Watson signed off on the fix.
BoRonGod
high
Protocol unable to get extra Rewards in OCY_Convex_C
Summary
Convex would wrap
rewardToken
for pools with IDs 151+, but the counting logic inOCY_Convex_C.sol
makes it impossible for zivoe to forward yield.Vulnerability Detail
In
OCY_Convex_C.sol
, A convex pool with id270
is used:In the following logic,
rewardContract
is defaulted to the address of extraRewards. This assumption is fine for pools with PoolId < 150, but would not work for IDs 151+.According to convex doc,
Just check
convexRewards
of pool 270:https://etherscan.io/address/0xc583e81bB36A1F620A804D8AF642B63b0ceEb5c0#readContract#F5
For index 0, it returns a
VirtualBalanceRewardPool
with rewardtoken = 0x85D81Ee851D36423A5784CD3Cb6f1a1193Cb5978. This contract is aStashTokenWrapper
, which is consistent with what the convex documentation says.And, when
IBaseRewardPool_OCY_Convex_C(convexRewards).getReward();
is triggered, reward tokens will be unwrapped and send to caller, so rewardAmount will always return 0, means such yield cannot be claimed for zivoe.Impact
Users will lose extra rewards from convex pools with IDs 151+.
Code Snippet
https://github.com/sherlock-audit/2024-03-zivoe/blob/main/zivoe-core-foundry/src/lockers/OCY/OCY_Convex_C.sol#L210-L219 https://docs.convexfinance.com/convexfinanceintegration/baserewardpool
Tool used
Manual Review
Recommendation
Change the logic above to: