Oracles

Explore how oracles on the Juchain network enable smart contracts to seamlessly access real-world data, such as asset prices and external events.

Oracles act as a bridge between the blockchain and off-chain data, ensuring smart contracts can execute based on accurate, decentralized information. This integration expands the capabilities of smart contracts, allowing for more complex and dynamic applications within the Juchain ecosystem.

The JuChain Oracle service provides reliable on-chain price data for decentralized applications (dApps). The JU-USDT Price Oracle specifically delivers real-time pricing of the JU token relative to USDT, supporting DeFi applications, trading platforms, and other smart contracts requiring precise price data.

External Oracles with Juchain

Name
Description

APRO Oracle is the top decentralized oracle in the Bitcoin ecosystem, offering Bitcoin-level security and rapid response through its innovative Oracle 3.0 standard.

Mainnet Deployment Information

  • Network: JuChain Mainnet

  • Contract Address: 0x49E5c7f25711abe668F404307b27f4bE4836B0e7

  • Deployer Address: 0x7389F1B4717F5152B6Cc107bce4A42a11dC0b76E

  • Owner: 0x5021A15FaAFEFEC1daCB1c8b24FFE3F3E3f7277b

  • Updater Permission Address: 0xa6F32fe2920AcF559699825AFaC493aa4F49Ac1D

  • Deployment Transaction Hash: 0x233654f76766fb0f9fd1377a573bed11c60a44c0cdc8e59340ffda333d191140

Testnet Deployment Information

This document also includes information about the JuChain Testnet environment, where developers can test oracle functionality.

The current public chain Solidity version compilation supports only <= 0.8.8. Support for subsequent versions will be gradually rolled out based on official announcements.


Contract Information

Mainnet Contract Information

  • Network: JuChain Mainnet

  • Contract Address: 0x49E5c7f25711abe668F404307b27f4bE4836B0e7

  • Deployer Address: 0x7389F1B4717F5152B6Cc107bce4A42a11dC0b76E

  • Owner: 0x5021A15FaAFEFEC1daCB1c8b24FFE3F3E3f7277b

  • Updater Permission Address: 0xa6F32fe2920AcF559699825AFaC493aa4F49Ac1D

  • Deployment Transaction Hash: 0x233654f76766fb0f9fd1377a573bed11c60a44c0cdc8e59340ffda333d191140

  • Update Frequency: Every 1 minute

  • Price Source: Aggregated data from JU-USDT trading pairs across multiple centralized and decentralized exchanges

Testnet Contract Information

  • Network: JuChain Testnet

  • Contract Address: 0x70D3Fc0bcf1ffD64111FC0C708DA407d9732Ab95

  • Deployer Address: 0xa01d5Be3fDea4Fd8f1C35Ced0919353036De15d0

  • Updater Permission Address: 0x4878683a8C3007258278824228a92aC4E072F050 (only this address can update prices)

  • Deployment Transaction Hash: 0x7e42454909c3ea9b52af4af84217149a87aa71aa08f129e11a01d5cea0989659

  • Update Frequency: Every 1 minute

  • Price Source: Aggregated data from JU-USDT trading pairs across multiple centralized and decentralized exchanges

Permission Details

  • Deployer (Owner):

    • Mainnet: 0x5021A15FaAFEFEC1daCB1c8b24FFE3F3E3f7277b, responsible for contract management and permission allocation.

    • Testnet: 0xa01d5Be3fDea4Fd8f1C35Ced0919353036De15d0, responsible for contract management and permission allocation.

  • Authorized Updater:

    • Mainnet: 0xa6F32fe2920AcF559699825AFaC493aa4F49Ac1D, the only account authorized to call updatePrice to update prices.

    • Testnet: 0x4878683a8C3007258278824228a92aC4E072F050, the only account authorized to call updatePrice to update prices.

  • Design Change: The new oracle version separates the deployer and price updater roles to enhance security.


Key Methods

getLatestPrice

  • Description: Retrieves the latest price information for the JU-USDT trading pair.

  • Returns:

    • string: Trading pair symbol (e.g., "JU/USDT").

    • uint256: Latest price (must be divided by the precision value).

    • uint256: Last update timestamp (Unix timestamp).

  • Call Example:

    (string memory symbol, uint256 price, uint256 timestamp) = oracle.getLatestPrice();

latestPrice

  • Description: Retrieves the latest price value (without additional information).

  • Returns:

    • uint256: Latest price value.

pricePrecision

  • Description: Retrieves the price precision value used to calculate the actual price.

  • Returns:

    • uint256: Price precision (e.g., 1e8, actual price = returned price / precision).

lastUpdatedAt

  • Description: Retrieves the timestamp of the last price update.

  • Returns:

    • uint256: Unix timestamp.

symbol

  • Description: Retrieves the trading pair symbol.

  • Returns:

    • string: Trading pair symbol (e.g., "JU/USDT").

updatePrice

  • Description: Updates the JU-USDT price, callable only by the authorized updater.

  • Parameters:

    • uint256 _price: New price value.

  • Permission: Only 0x4878683a8C3007258278824228a92aC4E072F050 can call this.

setAuthorizedUpdater

  • Description: Sets a new authorized updater, callable only by the Owner.

  • Parameters:

    • address newAuthorizedUpdater: New authorized updater address.

  • Permission: Only 0xa01d5Be3fDea4Fd8f1C35Ced0919353036De15d0 can call this.

owner

  • Description: Retrieves the current contract owner address.

  • Returns:

    • address: Owner address.


ABI

Below is the complete ABI for the JU-USDT Price Oracle:

[
  {
    "inputs": [
      {"internalType": "address", "name": "initialOwner", "type": "address"},
      {"internalType": "address", "name": "initialAuthorizedUpdater", "type": "address"}
    ],
    "stateMutability": "nonpayable",
    "type": "constructor"
  },
  {"inputs": [{"internalType": "address", "name": "owner", "type": "address"}], "name": "OwnableInvalidOwner", "type": "error"},
  {"inputs": [{"internalType": "address", "name": "account", "type": "address"}], "name": "OwnableUnauthorizedAccount", "type": "error"},
  {
    "anonymous": false,
    "inputs": [
      {"indexed": false, "internalType": "address", "name": "previousUpdater", "type": "address"},
      {"indexed": false, "internalType": "address", "name": "newUpdater", "type": "address"}
    ],
    "name": "AuthorizedUpdaterChanged",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {"indexed": true, "internalType": "address", "name": "previousOwner", "type": "address"},
      {"indexed": true, "internalType": "address", "name": "newOwner", "type": "address"}
    ],
    "name": "OwnershipTransferred",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {"indexed": false, "internalType": "uint256", "name": "price", "type": "uint256"},
      {"indexed": false, "internalType": "uint256", "name": "timestamp", "type": "uint256"},
      {"indexed": false, "internalType": "address", "name": "updater", "type": "address"}
    ],
    "name": "PriceUpdated",
    "type": "event"
  },
  {"inputs": [], "name": "renounceOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
  {"inputs": [{"internalType": "address", "name": "newAuthorizedUpdater", "type": "address"}], "name": "setAuthorizedUpdater", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
  {"inputs": [{"internalType": "address", "name": "newOwner", "type": "address"}], "name": "transferOwnership", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
  {"inputs": [{"internalType": "uint256", "name": "_price", "type": "uint256"}], "name": "updatePrice", "outputs": [], "stateMutability": "nonpayable", "type": "function"},
  {"inputs": [], "name": "authorizedUpdater", "outputs": [{"internalType": "address", "name": "", "type": "address"}], "stateMutability": "view", "type": "function"},
  {"inputs": [], "name": "getLatestPrice", "outputs": [{"internalType": "string", "name": "", "type": "string"}, {"internalType": "uint256", "name": "", "type": "uint256"}, {"internalType": "uint256", "name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
  {"inputs": [], "name": "lastUpdatedAt", "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
  {"inputs": [], "name": "latestPrice", "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
  {"inputs": [], "name": "owner", "outputs": [{"internalType": "address", "name": "", "type": "address"}], "stateMutability": "view", "type": "function"},
  {"inputs LuaTeX error (string "[]"):1: unexpected symbol near ']'.
": [], "name": "pricePrecision", "outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}], "stateMutability": "view", "type": "function"},
  {"inputs": [], "name": "symbol", "outputs": [{"internalType": "string", "name": "", "type": "string"}], "stateMutability": "view", "type": "function"}
]

Usage Examples

Web3.js Example

The following code demonstrates how to interact with the oracle using Web3.js:

const { Web3 } = require('web3');

// Mainnet RPC
const mainnetRpc = 'https://rpc.juchain.org';
// Testnet RPC
const testnetRpc = 'https://testnet-rpc.juchain.org';

// Select network (Mainnet/Testnet)
const web3 = new Web3(mainnetRpc); // or testnetRpc

// Mainnet contract address
const mainnetContractAddress = '0x49E5c7f25711abe668F404307b27f4bE4836B0e7';
// Testnet contract address
const testnetContractAddress = '0x70D3Fc0bcf1ffD64111FC0C708DA407d9732Ab95';

// Select contract address
const contractAddress = mainnetContractAddress; // or testnetContractAddress
const contract = new web3.eth.Contract(abi, contractAddress);

// Get complete price information
async function getCompletePrice() {
  try {
    const [symbol, price, timestamp] = await contract.methods.getLatestPrice().call();
    const precision = await contract.methods.pricePrecision().call();
    console.log('Complete Price Information:');
    console.log(`Trading Pair: ${symbol}`);
    console.log(`Price: ${price / precision} JU/USDT`);
    console.log(`Timestamp: ${timestamp} (${new Date(Number(timestamp) * 1000).toLocaleString()})`);
    return { symbol, price, timestamp, precision };
  } catch (error) {
    console.error('Failed to fetch price information:', error);
  }
}

// Get latest price
async function getLatestPrice() {
  try {
    const price = await contract.methods.latestPrice().call();
    const precision = await contract.methods.pricePrecision().call();
    console.log(`Latest Price: ${price / precision} JU/USDT`);
    return price;
  } catch (error) {
    console.error('Failed to fetch latest price:', error);
  }
}

// Get authorized updater
async function getAuthorizedUpdater() {
  try {
    const updater = await contract.methods.authorizedUpdater().call();
    console.log(`Authorized Updater: ${updater}`);
    return updater;
  } catch (error) {
    console.error('Failed to fetch authorized updater:', error);
  }
}

// Update price (only for authorized updater)
async function updatePrice(newPrice, privateKey) {
  try {
    const account = web3.eth.accounts.privateKeyToAccount(privateKey);
    web3.eth.accounts.wallet.add(account);
    const tx = contract.methods.updatePrice(newPrice);
    const gas = await tx.estimateGas({ from: account.address });
    const receipt = await tx.send({ from: account.address, gas });
    console.log(`Price updated successfully, Transaction Hash: ${receipt.transactionHash}`);
    return receipt;
  } catch (error) {
    console.error('Failed to update price:', error);
  }
}

// Execute all queries
async function getAllInfo() {
  await getCompletePrice();
  await getLatestPrice();
  await getAuthorizedUpdater();
}

getAllInfo();

Solidity Example

Below is an example of a smart contract utilizing the oracle:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IJUOracle {
    function getLatestPrice() external view returns (string memory, uint256, uint256);
    function pricePrecision() external view returns (uint256);
}

contract JUPriceConsumer {
    IJUOracle public oracle;

    constructor(address _oracleAddress) {
        oracle = IJUOracle(_oracleAddress);
    }

    function getJUPrice() public view returns (uint256) {
        (, uint256 price, ) = oracle.getLatestPrice();
        uint256 precision = oracle.pricePrecision();
        return price; // Frontend needs to divide by precision
    }

    function getFormattedPrice() public view returns (string memory symbol, uint256 price, uint256 precision, uint256 timestamp) {
        (symbol, price, timestamp) = oracle.getLatestPrice();
        precision = oracle.pricePrecision();
        return (symbol, price, precision, timestamp);
    }
}

Important Notes

  • Network Selection:

    • Mainnet: For production environments, contract address is 0x49E5c7f25711abe668F404307b27f4bE4836B0e7.

    • Testnet: For development and testing, contract address is 0x70D3Fc0bcf1ffD64111FC0C708DA407d9732Ab95.

  • Price Precision: The price returned by the contract must be divided by the value from pricePrecision() (e.g., 1e8 indicates 8 decimal precision).

  • Update Permissions:

    • Mainnet: Only 0xa6F32fe2920AcF559699825AFaC493aa4F49Ac1D can call updatePrice.

    • Testnet: Only 0x4878683a8C3007258278824228a92aC4E072F050 can call updatePrice.

    • The Owner can change the updater via setAuthorizedUpdater.

  • Update Delay: Prices are updated every 1 minute. Check lastUpdatedAt to ensure data freshness before use.

  • Price Volatility: Cryptocurrency prices may fluctuate significantly; consider implementing risk management mechanisms in your dApp.

  • Contract Upgrades: The oracle contract may be updated. Stay tuned to official announcements for the latest addresses and features.

  • Mainnet Considerations:

    • Operations on the mainnet incur real costs; exercise caution.

    • Thoroughly test on the testnet before deploying to the mainnet.

Last updated