#35767 [SC-Critical] constanct value is used to check `price.confidence`
Submitted on Oct 7th 2024 at 09:07:06 UTC by @jasonxiale for IOP | Swaylend
Report ID: #35767
Report Type: Smart Contract
Report severity: Critical
Target: https://github.com/Swaylend/swaylend-monorepo/blob/develop/contracts/market/src/main.sw
Impacts:
Protocol insolvency
Description
Brief/Intro
In [Market.get_price_internal] (https://github.com/Swaylend/swaylend-monorepo/blob/34ada63c18efd163ef80694c404d0573d49d46b4/contracts/market/src/main.sw#L1017-L1050), while validating the Pyth's price, the function checks the `price.confidence` in main.sw#L1045-L1048.
The issue is that `get_price_internal` will be used to get price for different `price_fee_id`, but the returned price will be checked against the same constant value
Vulnerability Details
As shown in main.sw#L1048-L1051, Market.get_price_internal uses constant values to check `price.confidence`
```Rust 1017 #[storage(read)] 1018 fn get_price_internal(price_feed_id: PriceFeedId) -> Price { 1019 let contract_id = storage.pyth_contract_id.read(); 1020 require( 1021 contract_id != ContractId::zero(), 1022 Error::OracleContractIdNotSet, 1023 ); 1024 ... 1045 require( 1046 u256::from(price.confidence) <= (u256::from(price.price) * ORACLE_MAX_CONF_WIDTH / ORACLE_CONF_BASIS_POINTS), 1047 Error::OraclePriceValidationError, 1048 ); <<<--- constant value is checked here 1049 1050 price 1051 } ```
Impact Details
`Market.get_price_internal` is important, because it is used while borrow/liquidate assets:
if the constant value is too wide for some price-feeds, the user/protocol might execute the tx at a bad price
if the constant value is too restrict for some price-feeds, the tx might always revert
References
Add any relevant links to documentation or code
Proof of Concept
Proof of Concept
According to the technical walkthrough at 185s, the derivate BTC might be used,
And from Pyth's network, we will take `BTC/USD` and `TBTC/USD` pair as an example
At the moment: >BTC/USD: price $63620, confidence: +- $16
>CBBTC/USD: price $63610, confidence: +- $509
To prove the point, we'll use the following code to simulate the check in main.sw#L1045-L1048
Please put the following code into a new file named `check.rs` and run: ```bash root@4d98affa1474:/in/temp# rustc check.rs ; ./check BTC check: true CBBTC check: false ```
As we can see, BTC/USD can pass the check, and CBBTC/USD can't pass the check
```Rust pub const ORACLE_MAX_CONF_WIDTH: u64 = 20; pub const ORACLE_CONF_BASIS_POINTS: u64 = 10_000; fn main() { let btc_price : u64 = 63620; let btc_confidence: u64 = 16;
} ```