#37276 [SC-Medium] Redstone's price feed is used incorrectly.

Submitted on Dec 1st 2024 at 11:03:07 UTC by @jasonxiale for IOP | Fluid Protocol

  • Report ID: #37276

  • Report Type: Smart Contract

  • Report severity: Medium

  • Target: https://github.com/Hydrogen-Labs/fluid-protocol/tree/main/contracts/oracle-contract/src/main.sw

  • Impacts:

    • Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield

Description

Brief/Intro

In current implementation for oracle-contract.get_price, while function is_pyth_price_stale_or_outside_confidence return true in main.sw#L88, Redstone price feed will be used.

However there is a flaw when using Redstone price feed that might lead stale price being used.

Vulnerability Details

According to oracle-contract.get_price, when redstone is used, redstone.read_timestamp will be used as the price 's publish timestamp.

And redstone.read_timestamp will be checked against with current_time in main.sw#L107

The issue is that redstone.read_timestamp uses Unix timestamp, but Fule's timestamp function returns TAI64 format timestamp

From above code, we can see that timestamp() returns [The TAI64 timestamp of the current block.] (https://github.com/FuelLabs/sway/blob/6d9065b8d762a39eb475562426a2d4ed17d92d00/sway-lib-std/src/block.sw#L47)

  1. According to redstone_adapter.read_timestamp, the function uses storage.timestamp as return value.

  2. And storage.timestamp is written by redstone_adapter.sw#L139 in redstone_adapter.overwrite_prices function.

  3. redstone_adapter.overwrite_prices is called by redstone_adapter.write_prices in redstone_adapter.sw#L96

  4. Now we'll check how timestamp is returned in redstone_adapter.sw#L95

  5. In redstone_adapter.process_payload, a config var is created with config.block_timestamp set to get_unix_timestamp() get_unix_timestamp() is defined as:

From here, we know that config.block_timestamp is UNIX time.

  1. Back to redstone_adapter.process_payload, in redstone_adapter.sw#L150, process_input is called, and process_input is defined in processor.process_input.

  1. In processor.sw#L32, timestamp is returned by config.validate_timestamps function.

  2. config.validate_timestamps is defined in config_validation.sw#L27-L43

  1. In config_validation.sw#L28-L29, the payload's timestamp and self.block_timestamp(which is UNIX timestamp) are passed to validate_timestamp function.

  2. According to validate_timestamp's implemention, we can get see that payload's timestamp in step9 should be UNIX timestamp format.

Impact Details

As I described above, when is_pyth_price_stale_or_outside_confidence returns true, Redstone can't work as expected.

References

Add any relevant links to documentation or code

Proof of Concept

Proof of Concept

The following POC is used to demonstrate that when is_pyth_price_stale_or_outside_confidence returns true, Redstone's oracle will be used.

Please put the following patch in contracts/oracle-contract/tests/authorization.rs and run

Last updated

Was this helpful?