In VotiumStrategy.sol, the price of vAfEth is determined by the price() function:
function price() external view override returns (uint256) {
return (cvxPerVotium() * ethPerCvx(false)) / 1e18;
}
As seen from above, it calls ethPerCVX() with false to determine the price of CVX in ETH. ethPerCVX() relies on a Chainlink oracle to fetch the CVX / ETH price:
VotiumStrategyCore.sol#L156-L183
function ethPerCvx(bool _validate) public view returns (uint256) {
ChainlinkResponse memory cl;
try chainlinkCvxEthFeed.latestRoundData() returns (
uint80 roundId,
int256 answer,
uint256 /* startedAt */,
uint256 updatedAt,
uint80 /* answeredInRound */
) {
cl.success = true;
cl.roundId = roundId;
cl.answer = answer;
cl.updatedAt = updatedAt;
} catch {
cl.success = false;
}
// verify chainlink response
if (
(!_validate ||
(cl.success == true &&
cl.roundId != 0 &&
cl.answer >= 0 &&
cl.updatedAt != 0 &&
cl.updatedAt <= block.timestamp &&
block.timestamp - cl.updatedAt <= 25 hours))
) {
return uint256(cl.answer);
} else {
If ethPerCvx() is called with _validate = false, no validation will be performed on Chainlink’s price feed. This means that ethPerCVX() could return an invalid price if:
Note that scenario 2 could occur if Chainlink’s multisigs decide to block access to the CVX / ETH price feed, as described here:
> While currently there’s no whitelisting mechanism to allow or disallow contracts from reading prices, powerful multisigs can tighten these access controls. In other words, the multisigs can immediately block access to price feeds at will. Therefore, to prevent denial of service scenarios, it is recommended to query ChainLink price feeds using a defensive approach with Solidity’s try/catch structure.
This becomes problematic as the price of vAfEth is used when calculating the price of AfEth in its price() function:
uint256 vEthValueInEth = (vEthStrategy.price() *
vEthStrategy.balanceOf(address(this))) / 1e18;
return ((vEthValueInEth + safEthValueInEth) * 1e18) / totalSupply();
Where:
As seen from above, the price of AfETH is determined as the sum of safETH and vAfEth value divided by totalSupply(), which uses vAfEth’s price (vEthStrategy.price()) in its calculation.
AfEth’s price is used to determine how much AfEth is minted to the user when deposit() is called:
totalValue +=
(sMinted * ISafEth(SAF_ETH_ADDRESS).approxPrice(true)) +
(vMinted * vStrategy.price());
if (totalValue == 0) revert FailedToDeposit();
uint256 amountToMint = totalValue / priceBeforeDeposit;
Where:
However, since no validation is performed for Chainlink’s price feed when vStrategy.price() is called, users can still call deposit() even when ethPerCvx() returns an incorrect price.
Should this occur, depositors will receive less AfEth for their deposit than expected, since both vStrategy.price() and priceBeforeDeposit will be smaller than usual. In extreme scenarios, if ethPerCvx() returns 0, vMinted * vStrategy.price() will also be 0, therefore the depositor will only receive AfEth for the amount used to buy safETH.
Since Chainlink’s CVX / ETH price feed is not validated when users call deposit(), users that call deposit() while the price feed is stale/blocked will receive less afEth than expected, resulting in a loss of funds.
In the price() function of VotiumStrategy.sol, consider adding a parameter that determines if the response from Chainlink’s price feed is validated:
- function price() external view override returns (uint256) {
- return (cvxPerVotium() * ethPerCvx(false)) / 1e18;
+ function price(bool _validate) external view override returns (uint256) {
+ return (cvxPerVotium() * ethPerCvx(_validate)) / 1e18;
}
The price() function in AfEth.sol should then use the validated price of vAfEth in its calculations, and revert if it the price is incorrect:
- uint256 vEthValueInEth = (vEthStrategy.price() *
+ uint256 vEthValueInEth = (vEthStrategy.price(true) *
vEthStrategy.balanceOf(address(this))) / 1e18;
return ((vEthValueInEth + safEthValueInEth) * 1e18) / totalSupply();
Oracle
The text was updated successfully, but these errors were encountered:
All reactions