Bitter Rouge Alpaca
High
The nuAssetManager.ethToNuAsset()
pass the ETH decimal instead of the nuAsset decimal, leading incorrect output assetAmount
Due to incorrect decimal parameter, price conversion gets corrupted, alternatively, incorrect output assetAmount
being returned or used.
https://github.com/sherlock-audit/2024-12-numa-audit/blob/ae1d7781efb4cb2c3a40c642887ddadeecabb97d/Numa/contracts/nuAssets/nuAssetManager.sol#L195 https://github.com/sherlock-audit/2024-12-numa-audit/blob/ae1d7781efb4cb2c3a40c642887ddadeecabb97d/Numa/contracts/nuAssets/nuAssetManager.sol#L205
function ethToNuAsset(
address _nuAsset,
uint256 _amount
) public view returns (uint256 EthValue) {
require(contains(_nuAsset), "bad nuAsset");
nuAssetInfo memory info = getNuAssetInfo(_nuAsset);
(address priceFeed, uint128 heartbeat) = (info.feed, info.heartbeat);
return ethToToken(_amount, priceFeed, heartbeat, 18); // @audit-issue should pass nuAssetDecimal
}
function ethToNuAssetRoundUp(
address _nuAsset,
uint256 _amount
) public view returns (uint256 EthValue) {
require(contains(_nuAsset), "bad nuAsset");
nuAssetInfo memory info = getNuAssetInfo(_nuAsset);
(address priceFeed, uint128 heartbeat) = (info.feed, info.heartbeat);
return ethToTokenRoundUp(_amount, priceFeed, heartbeat, 18); // @audit-issue should pass nuAssetDecimal
}
The conversion of input ETH _amount
to nuAssetAmount is done by ethToToken/ethToTokenRoundUp()
function, where instead of nuAsset decimal, the 18
ETH decimal is passed. Let's see how it will cause issue,
considering ethLeftSide(_pricefeed)==false
, which returns token X price in ETH,
function ethToToken(
uint256 _ethAmount,
address _pricefeed,
uint128 _chainlink_heartbeat,
uint256 _decimals
) public view checkSequencerActive returns (uint256 tokenAmount) {
...snip...
//if ETH is on the left side of the fraction in the price feed
if (ethLeftSide(_pricefeed)) {
tokenAmount = FullMath.mulDiv(
_ethAmount,
uint256(price),
10 ** AggregatorV3Interface(_pricefeed).decimals()
);
} else {
tokenAmount = FullMath.mulDiv( // @audit (1)
_ethAmount,
10 ** AggregatorV3Interface(_pricefeed).decimals(),
uint256(price)
);
}
tokenAmount = tokenAmount * 10 ** (18 - _decimals); // @audit (2)
}
As can be seen above, the calculation (1) returns output tokenAmount in _ethAmount
, which is in 18 decimal. And later the decimal of tokenAmount
readjusted in (2) , since _decimal==18
, the tokenAmount
remains unchanged causing to return output token X to be in same 18 decimal.
Note that this issue is different than the other oracleUtils
ethToToken()
incorrect division of decimal issue that I've reported in separate report, and also both requires different fix in order to mitigate.
No response
No response
No response
This calculation error will lead incorrect accounting and transfer of tokens.
No response
Modify it as;
function ethToNuAsset(
address _nuAsset,
uint256 _amount
) public view returns (uint256 EthValue) {
require(contains(_nuAsset), "bad nuAsset");
nuAssetInfo memory info = getNuAssetInfo(_nuAsset);
(address priceFeed, uint128 heartbeat) = (info.feed, info.heartbeat);
- return ethToToken(_amount, priceFeed, heartbeat, 18);
+ return ethToToken(_amount, priceFeed, heartbeat, IERC20Metadata(nuAsset).decimals());
}
function ethToNuAssetRoundUp(
address _nuAsset,
uint256 _amount
) public view returns (uint256 EthValue) {
require(contains(_nuAsset), "bad nuAsset");
nuAssetInfo memory info = getNuAssetInfo(_nuAsset);
(address priceFeed, uint128 heartbeat) = (info.feed, info.heartbeat);
- return ethToTokenRoundUp(_amount, priceFeed, heartbeat, 18);
+ return ethToTokenRoundUp(_amount, priceFeed, heartbeat, IERC20Metadata(nuAsset).decimals());
}