The functions getUsdPrice and getTokenPrice only check if the provided token address is equal to C.WETH. If the library intends to support multiple tokens in the future, it should implement proper input validation to ensure that the token is supported and valid
Lack of input validation: both functions assume that the provided token address is always a supported token (in this case C.WETH
However, they do not perform any validation to ensure that the token address passed as an argument is indeed a supported token
This oversight could lead to unexpected behavior or vulnerabilities if an unsupported token is passed to these functions.
POC below addressing the input validation concern in the getUsdPrice and getTokenPrice functions
* SPDX-License-Identifier: MIT
**/
pragma solidity =0.7.6;
pragma experimental ABIEncoderV2;
import {LibEthUsdOracle} from "./LibEthUsdOracle.sol";
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {C} from "contracts/C.sol";
/**
*
* @notice Contains functionality to fetch the manipulation-resistant USD price of different tokens.
* @dev Currently supports:
* - ETH/USD price
* - Additional tokens can be added with proper validation
**/
library LibUsdOracle {
using SafeMath for uint256;
mapping(address => bool) public supportedTokens;
event TokenAdded(address indexed token);
constructor()
supportedTokens[C.WETH] = true;
}
/**
* @dev Modifier to check if the token is supported
*/
modifier onlySupportedToken(address token) {
require(supportedTokens[token], "Oracle: Token not supported.")
_
}
function addSupportedToken(address token) external {
supportedTokens[token] = true
emit TokenAdded(token);
}
function removeSupportedToken(address token) external {
supportedTokens[token] = false
}
function isTokenSupported(address token) external view returns (bool) {
return supportedTokens[token];
}
function getUsdPrice(address token) internal view onlySupportedToken(token) returns (uint256) {
return getUsdPrice(token, 0);
}
/**
* @dev Returns the price of a given token in in USD with the option of using a lookback.
* `lookback` should be 0 if the instantaneous price is desired. Otherwise, it should be the
* TWAP lookback in seconds.
* If using a non-zero lookback, it is recommended to use a substantially large `lookback`
* (> 900 seconds) to protect against manipulation.
*/
function getUsdPrice(address token, uint256 lookback) internal view onlySupportedToken(token) returns (uint256) {
if (token == C.WETH) {
uint256 ethUsdPrice = LibEthUsdOracle.getEthUsdPrice(lookback);
if (ethUsdPrice == 0) return 0
return uint256(1e24).div(ethUsdPrice);
}
revert("Oracle: Token not supported.")
}
/**
* @notice Returns the price of a given token in USD.
* @dev If ETH returns 1000 USD, this function returns 1000 (ignoring decimal precision)
*/
function getTokenPrice(address token) internal view onlySupportedToken(token) returns (uint256) {
if (token == C.WETH) {
uint256 ethUsdPrice = LibEthUsdOracle.getEthUsdPrice();
if (ethUsdPrice == 0) return 0
return ethUsdPrice;
}
revert("Oracle: Token not supported.")
}
}
updated version of the code, I've introduced a mapping supportedTokens to keep track of supported tokens. The addSupportedToken function allows adding new supported tokens, while removeSupportedToken allows removing them. The isTokenSupported function checks if a token is supported.
I've added a modifier onlySupportedToken to ensure that the functions only execute if the provided token is supported. This modifier is applied to getUsdPrice and getTokenPrice functions.