import BN from 'bn.js'; import { array, struct, u16, u64, u8 } from '@coral-xyz/borsh'; import { Fraction, FractionLayout } from './fraction'; import { HUNDRED_PERCENT_BPS, USDC_DECIMALS, WSOL_DECIMALS } from '../constants'; export const MAX_ORACLES_PER_TOKEN = 4; export const MAX_ACCOUNTS_PER_ORACLE = 4; export type FormattedOracleType = "pyth" | "raydium_clmm" | "raydium_cpmm" | "switchboard" | "example"; export const ORACLE_TYPES_STRINGS: Map = new Map([ [0, "pyth"], [1, "raydium_clmm"], [2, "raydium_cpmm"], [3, "switchboard"], [255, "example"], ]); export interface OraclePriceOnChain { price: Fraction; conf: Fraction; updateTime: BN; // u64 }; export const OraclePriceLayout = struct([ FractionLayout.replicate('price'), FractionLayout.replicate('conf'), u64('updateTime'), ]); export enum OracleType { Pyth = 0, RaydiumClmm = 1, RaydiumCpmm = 2, SwitchBoard = 3, Example = 4, }; export enum Quote { Usdc = 0, Wsol = 1, }; export enum Side { Base = 0, Quote = 1, }; export interface OracleSettings { // type of the oracle (Pyth, CP swap, ...) oracleType: OracleType; // u8 numRequiredAccounts: number; // u8 // weight used in aggregation weight: number; // u16 // whether oracle is required for aggregation isRequired: number; // u8 // threshold for confidence ratio (conf / price < threshold) confThreshBps: number; // u16 // how much internal price derivations can differ (≥ 1.0) volatilityThreshBps: number; // u16 // max slippage allowed for min liquidity maxSlippageBps: number; // u16 // minimum liquidity (in quote tokens) for valid price minLiquidity: BN; //u64 // how old a price can be before it's invalid stalenessThresh: BN; // u64 // rate (bps) that inflates confidence with time staleness stalenessConfRateBps: number; // u16 tokenDecimals: number; // u8 // Special parameters specific to Raydium twapSecondsAgo: BN; // u64, primary twap window in seconds twapSecondarySecondsAgo: BN; // u64, secondary twap window - to check volatility quote: Quote; // u8, 1 = USDC, 2 = WSOL side: Side; // 0 = base, 1 = quote } export const OracleSettingsLayout = struct([ u8('oracleType'), u8('numRequiredAccounts'), u16('weight'), u8('isRequired'), u16('confThreshBps'), u16('volatilityThreshBps'), u16('maxSlippageBps'), u64('minLiquidity'), u64('stalenessThresh'), u16('stalenessConfRateBps'), u8('tokenDecimals'), u64('twapSecondsAgo'), u64('twapSecondarySecondsAgo'), u8('quote'), u8('side'), ]); export interface OracleData { oracleSettings: OracleSettings; accountsToLoadLutIds: number[]; // length MAX_ACCOUNTS_PER_ORACLE accountsToLoadLutIndices: number[]; // length MAX_ACCOUNTS_PER_ORACLE }; export const OracleDataLayout = struct([ OracleSettingsLayout.replicate('oracleSettings'), array(u8(), MAX_ACCOUNTS_PER_ORACLE, 'accountsToLoadLutIds'), array(u8(), MAX_ACCOUNTS_PER_ORACLE, 'accountsToLoadLutIndices'), ]); export interface OracleAggregator { numOracles: number; // u8 // minimum number of oracle results required for valid price minOraclesThresh: number; // u8 oracles: OracleData[]; // [OracleData; MAX_ORACLES_PER_TOKEN] // minimum confidence ratio allowed (conf / price) minConfBps: number; // u16 // maximum confidence ratio allowed (conf / price) confThreshBps: number; // u16 // factor to expand confidence bands (≥ 1.0) confMultiplier: Fraction; } export const OracleAggregatorLayout = struct([ u8('numOracles'), u8('minOraclesThresh'), array(OracleDataLayout, MAX_ORACLES_PER_TOKEN, 'oracles'), u16('minConfBps'), u16('confThreshBps'), FractionLayout.replicate('confMultiplier') ]); export const DEFAULT_ORACLE_SETTINGS: OracleSettings = { oracleType: OracleType.Pyth, numRequiredAccounts: 1, weight: HUNDRED_PERCENT_BPS, isRequired: 1, confThreshBps: 100, // TODO: 1% ?? volatilityThreshBps: 50000, maxSlippageBps: 1000, minLiquidity: new BN(0), stalenessThresh: new BN(120), stalenessConfRateBps: 50, tokenDecimals: WSOL_DECIMALS, twapSecondsAgo:new BN(0), twapSecondarySecondsAgo: new BN(0), quote: Quote.Wsol, side: Side.Base, }; export const PYTH_WSOL_ORACLE_SETTINGS: OracleSettings = { ...DEFAULT_ORACLE_SETTINGS, tokenDecimals: WSOL_DECIMALS, } export const PYTH_USDC_ORACLE_SETTINGS: OracleSettings = { ...DEFAULT_ORACLE_SETTINGS, tokenDecimals: USDC_DECIMALS, }