Old Boysenberry Reindeer
High
A logical error in the currentValue function of the PythOracle contract allows stale prices to be considered valid while rejecting newer prices. This issue occurs because the condition incorrectly checks whether the price is too “new” rather than ensuring it is not stale. As a result, users may rely on outdated price data, leading to inaccurate calculations in the protocol.
In PythOracle.sol:29 https://github.com/sherlock-audit/2024-11-oku/blob/main/oku-custom-order-types/contracts/oracle/External/PythOracle.sol#L29
This condition allows prices that are older than noOlderThan seconds but rejects newer prices. The correct validation should ensure the price is at least as recent as block.timestamp - noOlderThan, rejecting any prices that are too old.
- price.publishTime is set to a timestamp older than block.timestamp - noOlderThan.
- noOlderThan is configured (e.g., 3 days = 259200 seconds).
- The function is invoked to fetch the current price value.
- The oracle provides a price with a publishTime older than the configured noOlderThan period.
- The protocol relies on the oracle’s currentValue for subsequent calculations, assuming the price is valid.
- Step 1: A price with a stale publishTime is returned by the oracle. • Example: publishTime = 15th January, block.timestamp = 20th January, noOlderThan = 3 days. • Calculation: 15 < 20 - 3 = 17 → Passes as valid, though it is stale.
- Step 2: A price with a newer publishTime fails validation. • Example: publishTime = 15th January, block.timestamp = 17th January, noOlderThan = 3 days. • Calculation: 15 < 17 - 3 = 14 → Fails as invalid, though it is recent.
- Step 3: Inaccurate price data is used in calculations, leading to potential financial losses or incorrect protocol behavior.
Stale prices will be accepted, while recent, valid prices may be rejected. This can lead to: • Inaccurate financial calculations. • Increased risk of economic attacks based on outdated data.
function testOracle() external {
uint256 publishTime = 15 days;
uint256 timestamp_1 = 20 days; // should be invalide
uint256 timestamp_2 = 17 days; // should be valide
uint256 noOlderThan = 3 days;
if (publishTime < timestamp_1 - noOlderThan) {
console.log("Stale price is valid in 1st case");
} else {
console.log("New price is invalid in 1st case");
}
if (publishTime < timestamp_2 - noOlderThan) {
console.log("Stale price is valid in 1st case");
} else {
console.log("New price is invalid in 1st case");
}
if (publishTime + noOlderThan >= timestamp_1) {
console.log("New price is valid in 2nd case" );
} else {
console.log("Stale price is invalid in 2nd case");
}
if (publishTime + noOlderThan >= timestamp_2) {
console.log("New price is valid in 2nd case" );
} else {
console.log("Stale price is invalid in 2nd case" );
}
}
Replace the incorrect validation with the correct condition to ensure only recent prices are accepted:
require( price.publishTime + noOlderThan >= block.timestamp, "Stale Price" );