Organization
- @steakhouse
Engagement Type
Cantina Reviews
Period
-
Repositories
Findings
Medium Risk
1 findings
1 fixed
0 acknowledged
Informational
2 findings
1 fixed
1 acknowledged
Medium Risk1 finding
Deviation may go undetected due to biased division base currentBackupPrice
Severity
- Severity: Medium
Submitted by
Optimum
Description
In both
initialize()andgetDeviation(),currentBackupPriceis used as the denominator as we can see in the following code snippets:// initialize()initialDeviation = (diff * 10**18) / initialBackupPrice;// getDeviation()return (diff * 10**18) / currentBackupPrice;The implicit assumption behind this code is that the expected deviation is always a drop in the price of backup oracle. While it might be the case in most deployments, it is still possible that in certain scenarios the price of the backup oracle will be greater than the price of the primary oracle. In this case, the deviation might not cross the predefined threshold although it should. To better understand, let's consider the following scenario:
- The primary oracle tracks the price of BTC/USD, assuming it is $100,000.
- The backup oracle tracks the price of cbBTC/USDC, assuming it is 100,000 USDC.
- The deviation threshold is 5%.
If at some point USDC depegs while cbBTC does not, we expect cbBTC/USDC to increase, assuming it is 105,000 USDC at the moment, this will not be considered deviated since
getDeviation()will return(105,000 - 100,000) / 105,000 ~ 4.76% < 5%.Recommendation
Consider changing the code to divide by the minimum value of the two price instead.
Cantina
The issue was fixed by taking the average between the two prices (primary and backup) instead.
Informational2 findings
initialize() implements logic that's duplicated in getDeviation()
Severity
- Severity: Informational
Submitted by
Optimum
Description
The following logic in
initialize()is duplicated fromgetDeviation():uint256 initialPrimaryPrice = _primaryOracle.price();uint256 initialBackupPrice = _backupOracle.price();uint256 initialDeviation;if (initialBackupPrice == 0) { initialDeviation = initialPrimaryPrice == 0 ? 0 : type(uint256).max;} else { uint256 diff; if (initialPrimaryPrice >= initialBackupPrice) { diff = initialPrimaryPrice - initialBackupPrice; } else { diff = initialBackupPrice - initialPrimaryPrice; } initialDeviation = (diff * 10**18) / initialBackupPrice;}Code duplication is considered bad practice as it increases maintenance effort, reduces readability, and expands the audit surface. It also risks inconsistencies when updating shared logic.
Recommendation
Refactor the shared logic into an internal view function (e.g.,
_getDeviation()) and call it from bothinitialize()andgetDeviation()to improve maintainability while preserving gas efficiency.The current mechanism will not detect the rare scenario of "double-depegging"
State
- Acknowledged
Severity
- Severity: Informational
Submitted by
Optimum
Description
As communicated with the team, the intended use of this system is to use the primary oracle to track the price of an asset against USD (BTC/USD for example), and the backup oracle to track the price of a wrapped version of that asset with a wrapped version of USD (cbBTC/USDC for example).
In the unlikely scenario of a depegging of both cbBTC and USDC, the backup oracle price might not deviate from the primary oracle price while using the current
getDeviation()function. Although this scenario is unlikely, in case both assets tracked by the backup oracle are backed by the same centralized entity it might be more likely than we think.Recommendation
Consider adding a third oracle that will track the price of USDC/USD.
Cantina
Issue was acknowledged without a fix since the described scenario does not violate the expected behavior of the system as in case of double depegging the ratio will be maintained.