# EVM Oracle

# Overview

The Nibiru EVM Oracle precompile provides smart contracts with access to exchange rate data and ChainLink price feeds. This allows developers to query real-time price information for various asset pairs directly on-chain. From the Nibiru codebase itself, IOracle.sol (opens new window) provides the contract interface used to interact with the Oracle.

The precompile is accessible at a fixed address:

Copy address constant ORACLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801;

# Interface

The Oracle interface provides two main functions:

# 1. queryExchangeRate

This function returns the latest exchange rate for a given asset pair.

# Parameters

  • pair (string memory): The asset pair to query. Example values:
    • "ubtc:uusd" - USD price of BTC
    • "unibi:uusd" - USD price of NIBI

# Returns

  • price (uint256): The exchange rate for the given pair.
  • blockTimeMs (uint64): The timestamp (in milliseconds) when the price was last updated.
  • blockHeight (uint64): The block height when the price was last updated.

# Example Usage

Copy IOracle constant NIBIRU_ORACLE = IOracle(ORACLE_PRECOMPILE_ADDRESS); function getPrice(string memory pair) public view returns (uint256 price) { (price,,) = NIBIRU_ORACLE.queryExchangeRate(pair); }

# 2. chainLinkLatestRoundData

Fetches the latest price data from ChainLink for a given asset pair.

# Parameters

  • pair (string memory): The asset pair to query.

# Returns

  • roundId (uint80): The ID of the price round.
  • answer (int256): The latest price of the asset pair.
  • startedAt (uint256): Timestamp when the round started.
  • updatedAt (uint256): Timestamp when the price was last updated.
  • answeredInRound (uint80): The round in which the answer was computed.

# Example Usage

Copy function getChainLinkPrice(string memory pair) public view returns (int256 price) { (,price,,,) = NIBIRU_ORACLE.chainLinkLatestRoundData(pair); }

The Oracle precompile integrates with ChainLink's AggregatorV3Interface, allowing access to decentralized price feeds.

# Interface Methods

Copy interface ChainLinkAggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }

# Fetching Active Oracle Pairs

The Nibiru Oracle supports multiple asset pairs, which can be queried dynamically. The currently active markets can be retrieved using the Nibiru CLI with the following command:

Copy nibid query oracle actives | jq

# Example Response

Copy { "actives": [ "uatom:uusd", "ubtc:uusd", "ueth:uusd", "unibi:uusd", "uusdc:uusd", "uusdt:uusd" ] }

Any of these asset pairs can be used as input to the queryExchangeRate function of the Oracle precompile. If the pair is valid, the function returns the exchange rate; otherwise, it returns an error.

# Full Contract Example

Below is a complete smart contract that interacts with the Nibiru EVM Oracle precompile:

Copy // SPDX-License-Identifier: MIT pragma solidity >=0.8.19; interface IOracle { function queryExchangeRate(string memory pair) external view returns (uint256 price, uint64 blockTimeMs, uint64 blockHeight); function chainLinkLatestRoundData(string memory pair) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); } contract OracleTest { address constant ORACLE_PRECOMPILE_ADDRESS = 0x0000000000000000000000000000000000000801; IOracle constant NIBIRU_ORACLE = IOracle(ORACLE_PRECOMPILE_ADDRESS); function getExchangeRate(string memory pair) public view returns (uint256 price, uint64 blockTimeMs, uint64 blockHeight) { return NIBIRU_ORACLE.queryExchangeRate(pair); } function getChainLinkPrice(string memory pair) public view returns (int256 price, uint80 roundId, uint256 updatedAt) { (roundId, price,, updatedAt,) = NIBIRU_ORACLE.chainLinkLatestRoundData(pair); } }

# Full Example with Direct Communication to the Precompile

To communicate directly with the Nibiru EVM Oracle precompile, you can use the ethers.js library to interact with the contract. The script you provided already demonstrates how to query the Oracle for exchange rates and Chainlink prices using the correct contract address and ABI. Here's a breakdown of the necessary steps to ensure a smooth communication with the precompile, along with some refinements:

Copy const { ethers } = require("hardhat"); async function main() { const provider = new ethers.JsonRpcProvider( "https://evm-rpc.testnet-2.nibiru.fi/" // Nibiru Testnet RPC URL ); const precompileAddress = "0x0000000000000000000000000000000000000801"; // Oracle Precompile Address // Define the IOracle ABI for querying the exchange rate and ChainLink data const abi = [ "function queryExchangeRate(string pair) view returns (uint256 price, uint64 blockTimeMs, uint64 blockHeight)", "function chainLinkLatestRoundData(string pair) view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)", ]; // Connect to the Oracle precompile contract using the ABI and provider const oracle = new ethers.Contract(precompileAddress, abi, provider); // Define asset pairs to query the exchange rates const pairs = [ "uatom:uusd", "ubtc:uusd", "ueth:uusd", "uusdc:uusd", "uusdt:uusd", ]; // Loop through the pairs and query the Oracle for exchange rates and ChainLink prices for (const pair of pairs) { try { // Query the exchange rate for the asset pair console.log(`Querying exchange rate for ${pair}...`); const [price, blockTimeMs, blockHeight] = await oracle.queryExchangeRate(pair); console.log(`Pair: ${pair}`); console.log(`Exchange Rate: ${ethers.formatUnits(price, 18)}`); // Format the price to 18 decimals console.log(`Block Time (ms): ${blockTimeMs.toString()}`); console.log(`Block Height: ${blockHeight.toString()}`); } catch (error) { console.error(`Error querying exchange rate for ${pair}:`, error); } try { // Query the ChainLink price for the asset pair console.log(`Querying ChainLink price for ${pair}...`); const [roundId, answer, startedAt, updatedAt, answeredInRound] = await oracle.chainLinkLatestRoundData(pair); console.log(`Pair: ${pair}`); console.log(`ChainLink Price: ${ethers.formatUnits(answer, 18)}`); // Format the price to 18 decimals console.log(`Round ID: ${roundId.toString()}`); console.log(`Started At: ${startedAt.toString()}`); console.log(`Updated At: ${updatedAt.toString()}`); console.log(`Answered In Round: ${answeredInRound.toString()}`); } catch (error) { console.error(`Error querying ChainLink price for ${pair}:`, error); } } } // Execute the script main().catch(console.error);

# Explanation of Key Components

# Provider Setup

The script uses the Nibiru Testnet RPC URL (https://evm-rpc.testnet-2.nibiru.fi/) to connect to the Ethereum-compatible chain.

# ABI Definition

The ABI defines the available functions for interacting with the Oracle precompile (queryExchangeRate and chainLinkLatestRoundData).

# Contract Interaction

The ethers.Contract is used to create an instance of the Oracle contract, allowing you to call its functions.

# Asset Pairs

The pairs (e.g., uatom:uusd, ubtc:uusd) are the asset pairs supported by the Oracle for querying exchange rates or ChainLink prices.

# Error Handling

The script uses try-catch blocks to handle errors in case the Oracle or ChainLink query fails for any asset pair.

# Formatted Output

Prices are formatted to 18 decimals using ethers.formatUnits() to handle the precision typically used in Ethereum-based tokens.


# How This Script Works

  1. The script connects to the Nibiru EVM Oracle precompile using the predefined address and ABI.
  2. It queries the queryExchangeRate function to get the price, block time, and block height for each asset pair.
  3. It also queries the chainLinkLatestRoundData function to fetch the latest ChainLink price data.
  4. The results are logged to the console for each pair, with appropriate formatting for clarity.

This approach directly communicates with the Nibiru Oracle precompile, and you can extend this to include other queries or add additional functionality as needed.