Omniscia Swisscoast Audit
PythCaller Manual Review Findings
PythCaller Manual Review Findings
PCR-01M: Improper Revert Error
Type | Severity | Location |
---|---|---|
Logical Fault | PythCaller.sol:L65-L67 |
Description:
The PythCaller::convertToUint
function will revert
if the conversion cannot occur due to a misbehaving Pyth oracle instead of bubbling up the error to the PythCaller::getPythCurrentValue
caller and thus permitting them to f.e. invoke the SupraCaller
as an alternative.
Impact:
The PriceFeed
may never fallback to the Supra oracle system if the Pyth oracle misbehaves with an incorrect exponent or a negative price.
Example:
35function getPythCurrentValue(bytes32 HBARUSD, bytes32 USHCHF)36external37view38override39returns (40 bool ifRetrieve,41 uint256 value,42 uint256 _timestampRetrieved43)44{45 (int64 priceHBARUSD, , int32 expoHBARUSD, uint publishTimeHBARUSD) = pyth.getPriceUnsafe(HBARUSD);46 (int64 priceUSHCHF, , int32 expoUSHCHF, uint publishTimeUSHCHF ) = pyth.getPriceUnsafe(USHCHF);47
48 uint256 basePriceHBARUSD = convertToUint(priceHBARUSD, expoHBARUSD, 8);49 uint256 basePriceUSHCHF = convertToUint(priceUSHCHF, expoUSHCHF, 8);50
51 uint256 hbarChfPrice = basePriceHBARUSD * basePriceUSHCHF;52
53 // Using the smaller of the two timestamps as reference54 uint256 publishTime = publishTimeHBARUSD < publishTimeUSHCHF ? publishTimeHBARUSD : publishTimeUSHCHF;55
56 if (hbarChfPrice > 0) return (true, hbarChfPrice, publishTime);57 return (false, 0, publishTime);58}59
60function convertToUint(61 int64 price,62 int32 expo,63 uint8 targetDecimals64) private pure returns (uint256) {65 if (price < 0 || expo > 0 || expo < -255) {66 revert();67 }68
69 uint8 priceDecimals = uint8(uint32(-1 * expo));70
71 if (targetDecimals >= priceDecimals) {72 return73 uint(uint64(price)) *74 10 ** uint32(targetDecimals - priceDecimals);75 } else {76 return77 uint(uint64(price)) /78 10 ** uint32(priceDecimals - targetDecimals);79 }80}
Recommendation:
We advise the PythCaller::convertToUint
function to be revised, yielding a bool
indicating whether the conversion could be safely performed.
Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):
The code was updated to bubble up a false
value for the ifRetrieve
value of the top-level PythCaller::getPythCurrentValue
call, rendering this exhibit fully alleviated.
PCR-02M: Insecure Price Acquisition
Type | Severity | Location |
---|---|---|
External Call Validation | PythCaller.sol:L45, L46 |
Description:
The referenced function integrated will not apply data staleness checks which can significantly hurt the integrity of the yielded values.
Impact:
The data staleness check imposed by the PriceFeed
contract is insufficient in protecting against stale prices based on the Pyth Network's configuration, causing prices yielded by the PythCaller::getPythCurrentValue
function to potentially be stale.
Example:
35function getPythCurrentValue(bytes32 HBARUSD, bytes32 USHCHF)36external37view38override39returns (40 bool ifRetrieve,41 uint256 value,42 uint256 _timestampRetrieved43)44{45 (int64 priceHBARUSD, , int32 expoHBARUSD, uint publishTimeHBARUSD) = pyth.getPriceUnsafe(HBARUSD);46 (int64 priceUSHCHF, , int32 expoUSHCHF, uint publishTimeUSHCHF ) = pyth.getPriceUnsafe(USHCHF);47
48 uint256 basePriceHBARUSD = convertToUint(priceHBARUSD, expoHBARUSD, 8);49 uint256 basePriceUSHCHF = convertToUint(priceUSHCHF, expoUSHCHF, 8);50
51 uint256 hbarChfPrice = basePriceHBARUSD * basePriceUSHCHF;52
53 // Using the smaller of the two timestamps as reference54 uint256 publishTime = publishTimeHBARUSD < publishTimeUSHCHF ? publishTimeHBARUSD : publishTimeUSHCHF;55
56 if (hbarChfPrice > 0) return (true, hbarChfPrice, publishTime);57 return (false, 0, publishTime);58}
Recommendation:
We advise the normal IPyth::getPrice
function to be invoked instead, guaranteeing that the prices yielded are not stale.
Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):
The Swisscoast team has clarified that the HLiquity system is meant to impose its own set of data staleness checks, rendering validation of such a trait at the PythCaller
level incorrect.
We concur with this assessment and have thus marked the exhibit as nullified.
PCR-03M: Incorrect Accuracy of Yielded Value
Type | Severity | Location |
---|---|---|
Mathematical Operations | PythCaller.sol:L51 |
Description:
The PythCaller::getPythCurrentValue
function will yield a value with 1e16
accuracy instead of the expected 1e8
accuracy by the HLiquity system.
Impact:
Functions such as BorrowerOperations::_getUSDValue
will yield significantly higher values, causing the HCHF
token to be minted at abnormal amounts and thus to be devalued in relation to its "real" decimals.
Example:
26/*27* getPythCurrentValue()28*29* @dev Allows the user to get the latest value for the requestId specified30* @param _requestId is the requestId to look up the value for31* @return ifRetrieve bool true if it is able to retrieve a value, the value, and the value's timestamp32* @return value the value retrieved33* @return _timestampRetrieved the value's timestamp34*/35function getPythCurrentValue(bytes32 HBARUSD, bytes32 USHCHF)36external37view38override39returns (40 bool ifRetrieve,41 uint256 value,42 uint256 _timestampRetrieved43)44{45 (int64 priceHBARUSD, , int32 expoHBARUSD, uint publishTimeHBARUSD) = pyth.getPriceUnsafe(HBARUSD);46 (int64 priceUSHCHF, , int32 expoUSHCHF, uint publishTimeUSHCHF ) = pyth.getPriceUnsafe(USHCHF);47
48 uint256 basePriceHBARUSD = convertToUint(priceHBARUSD, expoHBARUSD, 8);49 uint256 basePriceUSHCHF = convertToUint(priceUSHCHF, expoUSHCHF, 8);50
51 uint256 hbarChfPrice = basePriceHBARUSD * basePriceUSHCHF;52
53 // Using the smaller of the two timestamps as reference54 uint256 publishTime = publishTimeHBARUSD < publishTimeUSHCHF ? publishTimeHBARUSD : publishTimeUSHCHF;55
56 if (hbarChfPrice > 0) return (true, hbarChfPrice, publishTime);57 return (false, 0, publishTime);58}
Recommendation:
We advise the result of the referenced multiplication to be divided by 1e8
, ensuring its accuracy is normalized.
Alleviation (04618e407bddce5b22e9cadd787fd3334bd3afe6):
The yielded price is properly divided by 1e8
as advised, ensuring the accuracy of the PythCaller
matches the one expected by the HLiquity system.