About Oracles
The Oracle Program delivers accurate price data to the protocol by integrating with trusted oracle providers. It calculates exchange rates by combining multiple price sources in a structured sequence, ensuring reliable asset valuations.
The Oracle program has been audited by Zenith and Offside.
Oracle Program Address: jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc
Hop-Based Oracle System
The system computes exchange rates by processing prices from up to four sources in a sequential chain. Each source contributes to the final rate through multiplication or division, with the option to invert values as needed. Currently, Pyth is the supported source type.
For example, to derive the JUPSOL/SOL rate, the system combines JUPSOL/USD and SOL/USD feeds, inverting the latter to obtain USD/SOL, resulting in an accurate exchange rate.
This design enables the system to:
- Aggregate rates from multiple feeds, reducing dependency on any single provider
- Adjust for varying units or scales using predefined multipliers and divisors
- Validate data integrity at each step
Freshness Enforcement
To ensure prices reflect current market conditions, the system enforces strict time-based validity checks:
- User Operations: Prices must be no older than 60 seconds to be considered valid for actions like borrowing or supplying assets
- Liquidations: Prices can be up to 1800 seconds (30 minutes) old, allowing liquidations to proceed during temporary oracle delays while avoiding reliance on outdated data
These checks prevent the protocol from using stale prices for liquidation or health factor calculations.
Confidence Interval Validation
The system evaluates the confidence interval provided by Pyth price feeds to ensure data reliability:
- User Operations: The confidence interval must be within 2% of the reported price
- Liquidations: The confidence interval must be within 4% of the reported price
If a price feed's confidence exceeds these thresholds, it is rejected.
Providers
The program currently utilizes Pyth push feeds for price data. Integration with Chainlink streams is underway to broaden the range of sources.
Oracle Verification Script
Use this script to verify oracle prices by providing a nonce:
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { Connection, PublicKey } from "@solana/web3.js";
import axios from "axios";
const NONCE = 2; // Change this to test different oracles
const ORACLE_PROGRAM_ID = new PublicKey("jupnw4B6Eqs7ft6rxpzYLJZYSnrpRgPcr589n5Kv4oc");
const IDL_URL = "https://raw.githubusercontent.com/jup-ag/jupiter-lend/refs/heads/main/target/idl/oracle.json";
const RPC_URL = "<RPC URL>";
class OracleReader {
private program: Program;
private constructor(program: Program) {
this.program = program;
}
static async create(): Promise<OracleReader> {
const connection = new Connection(RPC_URL);
const response = await axios.get(IDL_URL);
const idl = response.data;
const wallet = {
signTransaction: () => { throw new Error("Read-only"); },
signAllTransactions: () => { throw new Error("Read-only"); },
publicKey: new PublicKey("11111111111111111111111111111111"),
};
const provider = new AnchorProvider(connection, wallet, {});
const program = new Program(idl, provider);
return new OracleReader(program);
}
private findOraclePDA(nonce: number): PublicKey {
const [pda] = PublicKey.findProgramAddressSync(
[
Buffer.from("oracle"),
Buffer.from(new Uint8Array(new Uint16Array([nonce]).buffer)),
],
ORACLE_PROGRAM_ID
);
return pda;
}
async getPrice(nonce: number) {
const oraclePDA = this.findOraclePDA(nonce);
const oracleAccount = await this.program.account.oracle.fetch(oraclePDA);
const remainingAccounts = oracleAccount.sources.map((source: any) => ({
pubkey: source.source,
isWritable: false,
isSigner: false,
}));
const price = await this.program.methods
.getExchangeRateOperate(nonce)
.accounts({ oracle: oraclePDA })
.remainingAccounts(remainingAccounts)
.view();
return {
nonce,
oraclePDA: oraclePDA.toString(),
price: price.toString(),
sources: oracleAccount.sources.length
};
}
}
async function main() {
const reader = await OracleReader.create();
const result = await reader.getPrice(NONCE);
console.log(result);
}
main().catch(console.error);