The implementation of cvxPerVotium()
contains an edge case that causes it to return an invalid zero value price.
The cvxPerVotium()
function present in the VotingStrategy contract is used to measure the number of held CVX tokens per vAfEth.
144: function cvxPerVotium() public view returns (uint256) {
145: uint256 supply = totalSupply();
146: uint256 totalCvx = cvxInSystem();
147: if (supply == 0 || totalCvx == 0) return 1e18;
148: return ((totalCvx - cvxUnlockObligations) * 1e18) / supply;
149: }
The implementation basically divides the number of deposited CVX tokens over the total supply of vAfEth, while carefully subtracting any balance that is pending to be withdrawn (cvxUnlockObligations
).
However, if all CVX tokens are currently pending withdrawal, then cvxUnlockObligations == totalCvx
, causing cvxPerVotium()
to return zero. This case should be similar to totalCvx == 0
and should return 1e18
instead of zero.
The cvxPerVotium()
function is used in several places throughout the codebase:
- In
VotingStrategy::deposit()
, to calculate the number of minted tokens. - In
VotingStrategy::requestWithdraw()
, to calculate the amount of owed CVX tokens. - In
VotingStrategy::price()
, to calculate the price in ETH of the vAfEth token.- The VotiumStrategy price is then used in other functions, carrying the error forward:
- In
AfEth::price()
, to calculate the price in ETH of the AfEth token. - In
AfEth::deposit()
, to calculate the number of minted tokens of new deposits.
- In
- The VotiumStrategy price is then used in other functions, carrying the error forward:
- In
AfEth::depositRewards()
, to calculate the Votium strategy TVL.
This means that under this scenario, all these other critical functions will be affected.
The implementation of cvxPerVotium()
can be fixed by subtracting the CVX obligations before doing the totalCvx == 0
comparison. The edge case is then removed, since now totalCvx
should be zero if all CVX is pending to be withdrawn.
function cvxPerVotium() public view returns (uint256) {
uint256 supply = totalSupply();
- uint256 totalCvx = cvxInSystem();
+ uint256 totalCvx = cvxInSystem() - cvxUnlockObligations;
if (supply == 0 || totalCvx == 0) return 1e18;
- return ((totalCvx - cvxUnlockObligations) * 1e18) / supply;
+ return totalCvx * 1e18 / supply;
}