Skip to content

Latest commit

 

History

History
236 lines (181 loc) · 11.6 KB

sep-0040.md

File metadata and controls

236 lines (181 loc) · 11.6 KB

Preamble

SEP: 0040
Title: Oracle Consumer Interface
Authors: Alex Mootz <@mootz12>, OrbitLens <@orbitlens>, Markus Paulson-Luna <@markus_0_>
Track: Standard
Status: Draft
Created: 2023-02-17
Updated: 2023-05-13
Version: 0.1.0
Discussion: https://groups.google.com/g/stellar-dev/c/KV2XaQzcPPQ

Simple Summary

A standard interface for oracles transmitting price information.

Motivation

Oracles, specifically price feed oracles, are used extensively in DeFi across blockchain ecosystems. Smart contracts consume prices from oracles to assist in valuing on-chain tokens. A standard price feed interface allows smart contracts to fetch prices from any oracle provider. Without this, smart contracts are forced to use adapter contracts that attempt to normalize the price consumption mechanism to avoid being locked into whatever oracle implementation they are consuming.

In Soroban, cross-contract calls are relatively expensive, so an extensive adapter contract network is both inefficient for the blockchain and expensive for the end user.

This SEP proposes a standard price oracle interface, such that smart contracts can implement a uniform consumption mechanism.

Smart contracts that follow standard interface described in this SEP also get the flexibility of switching between standard oracle price feeds without internal logic changes. This increases the overall safety and flexibility of the Soroban smart contract ecosystem.

Abstract

Oracles report a wide variety of data to the blockchain, enabling on-chain contracts to access the reported data. Oracles reporting price data have become one of the most important use cases of oracles to-date. Implementing a standard consumption model for smart contracts looking to access price data improves smart contract safety, flexibility, and reliability.

This SEP does not define how providers report price information, only how consumer smart contracts can access it via a standard interface. The proposal is focused mainly on price feed for on-chain and off-chain assets. Other oracle types are outside the scope of this SEP.

Specification

Oracle feed providers report all price feeds denominated in the base asset to a single price feed aggregator contract, regardless of the number of asset price feeds being reported.

Interface

The price feed contract should follow the PriceFeedTrait interface defined here:

/// Price data for an asset at a specific timestamp
#[contracttype]
pub struct PriceData {
    price: i128,
    timestamp: u64
}

#[contracttype]
enum Asset {
   Stellar(Address),
   Other(Symbol),
}

/// Oracle feed interface description
pub trait PriceFeedTrait {
    /// Return the base asset the price is reported in
    fn base(
        env: soroban_sdk::Env,
    ) -> Asset;

    /// Return all assets quoted by the price feed
    fn assets(
        env: soroban_sdk::Env,
    ) -> Vec<Asset>;

    /// Return the number of decimals for all assets quoted by the oracle
    fn decimals(
        env: soroban_sdk::Env
    ) -> u32;

    /// Return default tick period timeframe (in seconds)
    fn resolution(
        env: soroban_sdk::Env
    ) -> u32;

    /// Get price in base asset at specific timestamp
    fn price(
        env: soroban_sdk::Env,
        asset: Asset,
        timestamp: u64
    ) -> Option<PriceData>;

    /// Get last N price records
    fn prices(
        env: soroban_sdk::Env,
        asset: Asset,
        records: u32
    ) -> Option<Vec<PriceData>>;

    /// Get the most recent price for an asset
    fn lastprice(
        env: soroban_sdk::Env,
        asset: Asset,
    ) -> Option<PriceData>;
}

Assets are represented as the struct Asset, in the following formats:

  • Stellar assets: Asset::Stellar containing the asset unique address in Soroban network, obtained when an asset from Stellar Classic is deployed to Soroban via the Stellar Asset Contract. E.g. d93f5c7bb0ebc4a9c8f727c5cebc4e41194d38257e1d0d910356b43bfc528813.
  • Other assets (e.g. fiat off-chain currencies, mutual funds tokens, etc): Asset::Other containing the currency or token name. Since Asset::Other can be any Symbol, each Oracle can define it's own format for representing off-chain assets.

Contract Address

The price feed aggregator contract should have a stable contract address. That is, it must be upgradeable without changing the contract address. Smart contract consumers should only be expected to change their price feed aggregator address if they would like to change oracle providers.

Design Rationale

PriceData represents a datapoint describing a price of an asset at a specific timestamp. Price is encoded as an i128 number where last N digits designate the fractional part of the price stored with precision defined by decimals() of the given oracle feed. So the actual price can be calculated as price/10^decimals. Note that using this conversion directly in the Soroban environment will result in the loss of a fractional part since only integer division is supported, so only safe integer arithmetics should be used when consuming oracle prices.

Parameter timestamp in PriceData refers to Unix Timestamp calculated as floor(unix_now()/resolution)*resolution to provide uniform price feed timeframes trimmed to oracle resolution. It is important for consumer contracts to check the timestamp field of the returned values against the current ledger timestamp to make sure that the reported price value is not stale.

Method last_price(env, asset) returns the last known price ("current price") for a given asset provided by the oracle. Often developers need to fetch last N values to apply some averaging logic to the feed values. For instance, time-weighted average price (TWAP) formula, which is the measure of an asset's average price over a predetermined period of time. Correspondingly, prices(env, asset, records) method can be used to retrieve the desired number of records.

For other logic developers may utilize prices(env, asset, timestamp) method which returns price information for a given point in time, under condition that the historical data for a requested timestamp is available, or throws an error otherwise.

Smart contracts implementing this trait should not throw specific errors in case of invalid request input (e.g. unknown asset identifier or timestamp out of oracle history range). Instead, a contract should return None on such function calls in order to delegate error handling logic directly to consumer contracts.

Timeframe Resolution and Price Feed Precision

Particular values of data timeframe resolution and decimals precision is out of scope of this SEP. Once the price feed aggregation contract is deployed, these values should never change. It is impossible to guarantee that consumer contracts can react to any updates in these values.

Timestamps Instead of Rounds

Some other well-known oracle implementations utilize generic round id instead of timestamps to identify price feed data points and allow non-uniform sampling rates. However, this brings additional complexity on the consumer side. Moreover, downstream contracts need a timestamp attached to each price update round either way, because it is often used for averaging and price staleness checks.

Primary motivation of using uniform timestamps as price feed entry identifiers is to get rid of excessive generic identifiers and ensure that consumers can apply averaging algorithms (such as widely-utilized TWAP) directly to the data received from the oracle feed, without the need to preprocess and normalize timestamps.

This approach also implies that an oracle should update its data feed regularly, or it should have the ability to extrapolate fragmented reported data in a way to present a normalized monotonic output in response to price history calls following the described uniform sampling rate approach.

Data Reporting

The exact mechanism of data reporting mechanism utilized by an oracle is out of scope of this SEP. Nevertheless, it is recommended to avoid relaying on centralized data sources or centralized reporting entity. Ideally, this process should be controlled by smart contract quoting the data directly or a set of independent off-chain nodes which can come to the agreement on the feed updates by utilizing some kind of on-chain or off-chain consensus.

Data Retention

History retention period is individually configured by an oracle provider. This SEP does not provide specific guidelines in this regard.

Security Concerns

This SEP does not create any security concerns for the Stellar protocol.

Oracle Price Manipulations

Oracles can be exposed to various manipulations depending on the mechanism of price reporting.

On-chain price providers are vulnerable to manipulations via spot price spoofing. For example, an attacker can can take out a flash loan to drain the liquidity from one side of the orderbook or liquidity pool. As a result, the protocol's internal price is directly controlled by the attacker, which can inflate the price, subsequently execute arbitrage trade or take an undercollateralized loan in a smart contract relying solely on the manipulated protocol, and then repay a loan, getting away with profit.

Off-chain oracles can suffer from a temporary outage, misconfiguration, or errors of external data API. The nodes software itself is potentially vulnerable to attacks on access control, cryptographic implementation, transport, and database security.

Operators of centralized oracles may to report malicious data and abuse the trust of consuming contracts if the incentive is high enough. On the other hand, nodes in the decentralized oracle network may misbehave by reporting invalid, malformed, or not properly validated data, as well as simply coping the feed from other nodes without verification (in this case the resilience of the corresponding decentralized oracle network may degrade significantly up to the point where a single entity controls the network).

Best Practices for Consumer Smart Contract Developers

  • Always check retrieved price data for staleness by comparing the quoted timestamp with current date.
  • If available, utilize several oracle providers to mitigate the risks of service denial. Do not rely on a single oracle in mission-critical use-cases.
  • Apply TWAP whenever possible. Consuming data without averaging may expose a contract to risks of high assets volatility or malicious artificial oracle price spoofing.
  • Consumer protocols can set up additional safeguards for noticeable anomalies in the consumed oracles feed values (e.g. sudden deviations of stablecoin prices).
  • Avoid using oracles with illiquid assets and markets as they are more prone to manipulations.
  • If it's not critical to the functionality of the protocol, set up artificial delays to prevent flash-loan transactions attacks.
  • To assess possible risk of trusting to the particular oracle, check the contact ownership, authorization required to update the feed, declaration about underlying data sources and security guarantees.
  • In case of templated consumer contracts deployment or if a consumer contract is not upgradeable by design, it is advisable to use a proxy oracle contract with the same interface described above. Such a proxy contract can act as an intermediary between a consumer and a price feed contract, or may contain some advanced logic – price aggregation from several oracle providers, custom precision/resolution transforms, cross-price calculation, etc. It can be updated to point to other oracle feed (or to change internal aggregation logic) transparently to data consumers.

Implementations