31417 - [SC - Insight] Compound claiming transactions will revert if u...

Submitted on May 18th 2024 at 21:09:37 UTC by @savi0ur for Boost | Alchemix

Report ID: #31417

Report type: Smart Contract

Report severity: Insight

Target: https://github.com/alchemix-finance/alchemix-v2-dao/blob/main/src/RewardsDistributor.sol

Impacts:

  • Contract fails to deliver promised returns, but doesn't lose value

Description

Bug Description

When user tries to reinvest their claimed reward with _compound = true in RewardsDistributor.claim(uint256 _tokenId, bool _compound), it will always revert when users _tokenId is expired.

https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/RewardsDistributor.sol#L174-L189

if (_compound) {
	(uint256 wethAmount, uint256[] memory normalizedWeights) = amountToCompound(alcxAmount);

	require(
		msg.value >= wethAmount || WETH.balanceOf(msg.sender) >= wethAmount,
		"insufficient balance to compound"
	);

	// Wrap eth if necessary
	if (msg.value > 0) {
		WETH.deposit{ value: wethAmount }();
	} else IERC20(address(WETH)).safeTransferFrom(msg.sender, address(this), wethAmount);

	_depositIntoBalancerPool(wethAmount, alcxAmount, normalizedWeights);

	IVotingEscrow(votingEscrow).depositFor(_tokenId, IERC20(lockedToken).balanceOf(address(this))); //@audit

https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/VotingEscrow.sol#L667-L672

As we can see, while performing depositFor() from claim(), its checking for require(_locked.end > block.timestamp, "Cannot add to expired lock. Withdraw");. Which will revert user's claim tx with compounding when their veALCX token is expired.

Impact

Users compounding claim tx will always revert when their veALCX NFT token is expired. User should not allowed to call claim function with compounding once their veALCX token is expired.

Recommendation

Consider sending the claimed veALCX rewards to the owner of the veALCX if the veALCX's lock has already expired.

When executing code block of compounding, it should also check along with compounding check that whether veALCX NFT token is not expired as shown below.

References

  • https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/RewardsDistributor.sol#L174-L189

  • https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/VotingEscrow.sol#L667-L672

Proof Of Concept

Note: For PoC to work, we need to move time beyond 60 days which is hardcoded in RewardsDistributor contract as staleThreshold = 60 days at https://github.com/alchemix-finance/alchemix-v2-dao/blob/f1007439ad3a32e412468c4c42f62f676822dc1f/src/RewardsDistributor.sol#L118. Since time has moved beyond 60 days but oracle(priceFeed) has not updated during this time interval, its reverting with Price stale message. To mimic oracle update, we have updated updatedAt field returns from latestRoundData() to avoid Price stale revert using foundry cheatcode - load and store.

Steps to Run using Foundry:

  • Paste following foundry code in src/test/Minter.t.sol

  • Run using FOUNDRY_PROFILE=default forge test --fork-url $FORK_URL --fork-block-number 17133822 --match-contract MinterTest --match-test testCompoundRewardFailureOnVeALCXExpired -vv

Console Output:

Last updated

Was this helpful?