SIP-329: Pull Oracle Update Node

Author
StatusDraft
TypeGovernance
NetworkEthereum & Optimism
ImplementorTBD
ReleaseTBD
Created2023-06-16

Simple Summary

This proposal entails adding a new node type to the oracle manager such that clients can automatically replace a transaction with a multicall that prepends fresh price updates from Pyth’s pull-based price oracles as necessary.

Abstract

This new node type involves a combination of logic from the existing Pyth Node and the Staleness Circuit Breaker Node. Rather than reading the latest available Pyth price and then reverting plainly when a price is too stale, it can point the client to an off-chain URI where a fresh price can be retrieved.

Motivation

Nearly the entire DeFi ecosystem has been dependent on push oracles, where decentralized oracle networks like Chainlink write prices on-chain at a regular interval (a “heartbeat”) or when significant price deviations are observed. Although this is ideal for on-chain composability, these prices may not be fresh enough for practical use cases. Also, even if these oracle networks transition to proof of stake blockchains, it’s still not obvious how revenue could be collected from consumers such that nodes could be profitable after paying the cost of gas to write prices on-chain.

Pyth’s On-Demand Updates (and Chainlink’s forthcoming Low-Latency Oracle Solution) offer an alternative pattern where price consumers must retrieve a signed price update off-chain, cryptographically verify it using smart contracts, pay the oracle network, and pay the cost to write the price data on-chain. This has been integrated into Synthetix V2 Perpetual Futures Markets, Synthetix V3 Spot Markets, and the work-in-progress Synthetix V3 Perpetual Futures Markets for asynchronous order settlement. They implement EIP-3668 to retrieve and use prices associated with the time orders are committed.

This proposal would add functionality such that client applications for protocols that are integrated with the oracle manager are able to anticipate the need for fresh price data and automatically queue price update calls at the top of a multicall before the desired transaction.

Note that a similar pattern could be used to implement cross-chain pool synthesis if a decentralized oracle network were able to provide a similar service to read data from arbitrary functions on other chains at specified timestamps (rather than prices).

Specification

Overview

The oracle manager is a standalone system in Synthetix V3 which allows users to specify a node (of a variety of types), add configuration parameters, and an array of parent nodes when relevant. This proposal involves the addition of a new node type to achieve the pattern described above.

Rationale

The oracle manager is used to retrieve the latest available price on-chain from a variety of sources, perform any pre-processing, and add safety checks. It is a stateless system.

The system may be called multiple times in a single transaction. (For instance, processing a liquidation request for an account in a perpetual futures market with positions in multiple markets with multiple collateral types could require fresh prices for many different assets.) Accordingly, we can’t rely on the EIP-3668 standard, as there wouldn’t be a sensible method to generate callback functions in very dynamic contexts.

Instead, this would require custom logic in client applications to prepare successful transactions. It would be possible to develop a simple SDK to automatically to identify and include any necessary price data. Pseudocode is provided in the Technical Specification section below. This also limits composability, though if price updates are being provided frequently enough, it would be possible to have other transactions succeed without an off-chain look up.

Usage of the node is optional. Deployments of the Synthetix core system and market implementations could choose to rely on it for only some price feeds. Notably, with this node available, it would be possible to create a fully-functional deployment of the Synthetix core system and markets to a chain with only Pyth support and no push oracles.

Technical Specification

The parameters for this node would consist of:

  • address pythAddress - This is the same as is currently implemented in the Pyth Node.
  • bytes32 priceFeedId - This is the same as is currently implemented in the Pyth Node.
  • bool useEma - This is the same as is currently implemented in the Pyth Node.
  • string offchainUri - This is the off-chain URI that can retrieve the latest price for the specified priceFeedId (e.g. https://xc-testnet.pyth.network/api/get_vaa_ccip?data={data})
  • uint stalenessTolerance - Like the Staleness Circuit Breaker Node, if the latest available on-chain price is older than the staleness tolerance, the node will revert. In this case, the revert data will contain the data above, such that the client application is able to stage necessary price updates. Otherwise, the latest price data will be provided.

Something like the following pseudocode could be implemented in a client application to leverage the pattern enabled by the node here:

function preparePricePrependedMulticall(originalTx) {
  let multicallTx = [originalTx]
  try {
    simulateTx(multicallTx)
    return multicallTx
  } catch (error) {
    if (error instanceof PythLookupError) {
      const price = fetchPrice(error.uri)
      const updateTx = priceUpdateTx(price)
      multicallTx.unshift(updateTx)
      return preparePricePrependedMulticall(multicallTx, uri)
    }
  }
}

This could be optimized to generate a flat multicall, rather than nested multicalls. Also, an event could also be emitted from the node with the revert data. Here, a transaction simulation could see that this node is in use (even if it is succeeding) and proactively stage price update transactions, avoiding potential latency issues.

This proposal could also be modified to upgrade the existing Pyth Node implementation, as the alpha mainnet deployment of Synthetix V3 does not yet appear to have any Pyth Nodes in use. A staleness tolerance of 0 could be used to bypass the staleness check.

Test Cases

Relevant tests will be developed during implementation.

Configurable Values (Via SCCP)

N/A

Copyright and related rights waived via CC0.