Skip to Content

Pull Oracle

DORA V2, our latest version of data feeds protocol, uses a combination of Web2 and Web3 methods to achieve ultra-low latency when sending data from Supra to destination chains. First, Web2 methods are used to retrieve data from Supra, while Web3 smart contracts are utilised for cryptographically verifying and writing it on-chain where it lives on immutable ledgers.

Please refer to the following resources for a better understanding of DORA price feeds.

  • Data Feeds - This explains how Supra calculates the S-Value for data feeds.
  • Data Feeds Index - This provides a list of data feeds currently offered by Supra.
  • Available Networks - This lists available networks and Supra contract addresses.

Important: Please make sure you read and understand Terms of Use  before start using Supra products and services.

The first step of the process would be to set up your web2 code to interact with DORA’s pull oracle and sync with Supra’s consumer smart contract address.


Now, let’s move on to set up your Web3 components:


Step 1: Create The S-Value Pull Interface to verify the price data received.

Add the following code to the solidity smart contract that you wish to retrieve an S-Value.

// SPDX-License-Identifier: MIT pragma solidity 0.8.19; interface ISupraOraclePull { //Below does not have the timestamp or the round. struct PriceData { uint256[] pairs; // prices[i] is the price of pairs[i] uint256[] prices; // decimals[i] is the decimals of pairs[i] uint256[] decimals; } // If timestamp or round is required please use the below struct PriceInfo { uint256[] pairs; // prices[i] is the price of pairs[i] uint256[] prices; // timestamp[i] is the timestamp of pairs[i] uint256[] timestamp; // decimals[i] is the decimals of pairs[i] uint256[] decimal; // round[i] is the round of pairs[i] uint256[] round; } //Below function requests price data with round function verifyOracleProof(bytes calldata _bytesproof) external returns (PriceData memory); //Below function requests price data with round and timestamp function verifyOracleProofV2(bytes calldata _bytesProof) external returns (PriceInfo memory); }

This creates the interface that you will later apply in order to verify and fetch S-Values from Supra’s Pull Contract.

Step 2: Configure The S-Value Feed Address

To verify and fetch the S-Value from a Supra Pull smart contract, first find the S-Value Pull Contract address for the preferred chain. When you have the address, create an instance of the ISupraOraclePull using the interface we previously defined.

Supra contracts for each network can be found in our network addresses list.

// Mock contract which can consume oracle pull data contract MockOracleClient { /// @notice The oracle contract ISupraOraclePull internal oracle; /// @notice Event emitted when a pair price is received event PairPrice(uint256 pair, uint256 price, uint256 decimals); constructor(address oracle_) { oracle = ISupraOraclePull(oracle_); } }

Step 3: Receive and Verify the S-Value

You can refer to: GitHub Example  to get the Proof <_bytesProof> of S-Values in a Web2 environment.

Next, copy the following code to the smart contract to verify the price data received:

// Get the price of a pair from oracle data received from supra pull model function GetPairPrice(bytes calldata _bytesProof, uint256 pair) external returns(uint256){ ISupraOraclePull.PriceData memory prices = oracle.verifyOracleProof(_bytesProof); uint256 price = 0; uint256 decimals = 0; for (uint256 i = 0; i < prices.pairs.length; i++) { if (prices.pairs[i] == pair) { price = prices.prices[i]; decimals = prices.decimals[i]; break; } } require(price != 0, "Pair not found"); return price; } // Get the price of a pair from oracle data with round and timestamp function GetPairPriceV2(bytes calldata _bytesProof, uint256 pair) external returns(uint256,uint256,uint256){ ISupraOraclePull.PriceInfo memory prices = oracle.verifyOracleProofV2(_bytesProof); uint256 price = 0; uint256 decimals = 0; uint256 timestamp = 0; uint256 round = 0; for (uint256 i = 0; i < prices.pairs.length; i++) { if (prices.pairs[i] == pair) { price = prices.prices[i]; timestamp = prices.timestamp[i]; round = prices.round[i]; decimals = prices.decimal[i]; break; } } require(price != 0, "Pair not found"); return (price,timestamp,round); }

Thats it. Done!

Now you are ready to consume fast, low latency, and highly accurate data from Supra’s Pull oracle.

Recommended Best Practices

Create a function with access control that updates the oracle using the function: updatePullAddress()

This will allow you to update the address of the Supra Pull contract after deployment, allowing you to future proof your contract. Access control is mandatory to prevent the undesired modification of the address.

function updatePullAddress(ISupraOraclePull oracle_) external onlyOwner { oracle = oracle_; }

Example Implementation

Here’s an example of what your implementation should look like:

// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.24; import "@openzeppelin/contracts/access/Ownable.sol"; interface ISupraOraclePull { struct PriceData { uint256[] pairs; uint256[] prices; uint256[] decimals; } struct PriceInfo { uint256[] pairs; uint256[] prices; uint256[] timestamp; uint256[] decimal; uint256[] round; } //Below function(DORA1) verify the price and throws error if the proof is invalid. _bytesproof is the oracle proof to extract the pairs from Last Updated PriceData struct that does not contain timestamp and round. function verifyOracleProof(bytes calldata _bytesproof) external returns (PriceData memory); // Below function (DORA2) verify the price and throws error if the proof is invalid. _bytesproof is the oracle proof to extract the pairs from Last Updated PriceData struct that does contain timestamp and round. Stale prices can be determined using unixtimestamp. function verifyOracleProofV2(bytes calldata _bytesproof) external returns (PriceInfo memory); } // Mock contract which can consume oracle pull data contract MockOracleClient is Ownable { ISupraOraclePull public oracle; // Event emitted when a pair price is received event PairPrice(uint256 pair, uint256 price, uint256 decimals); constructor(ISupraOraclePull oracle_) Ownable(msg.sender){ oracle = oracle_; } //Get the price of a pair from oracle data without round and timestamp function GetPairPrice(bytes calldata _bytesProof, uint256 pair) external returns(uint256){ ISupraOraclePull.PriceData memory prices = oracle.verifyOracleProof(_bytesProof); uint256 price = 0; uint256 decimals = 0; for (uint256 i = 0; i < prices.pairs.length; i++) { if (prices.pairs[i] == pair) { price = prices.prices[i]; decimals = prices.decimals[i]; break; } } require(price != 0, "Pair not found"); return price; } //Get the price of a pair from oracle data with round and timestamp function GetPairPriceV2(bytes calldata _bytesProof, uint256 pair) external returns(uint256,uint256,uint256){ ISupraOraclePull.PriceInfo memory prices = oracle.verifyOracleProofV2(_bytesProof); uint256 price = 0; uint256 decimals = 0; uint256 timestamp = 0; uint256 round = 0; for (uint256 i = 0; i < prices.pairs.length; i++) { if (prices.pairs[i] == pair) { price = prices.prices[i]; timestamp = prices.timestamp[i]; round = prices.round[i]; decimals = prices.decimal[i]; break; } } require(price != 0, "Pair not found"); return (price,timestamp,round); } function updatePullAddress(ISupraOraclePull oracle_) external onlyOwner { oracle = oracle_; } }
Last updated on