From 9bf508f54cf089b51591f3241dff747f3291a264 Mon Sep 17 00:00:00 2001 From: Chris Heaney Date: Tue, 26 Nov 2024 15:40:05 -0600 Subject: [PATCH] start adding ts changes --- .../pollingDriftClientAccountSubscriber.ts | 80 +++++++++++-------- sdk/src/accounts/types.ts | 8 +- sdk/src/accounts/utils.ts | 12 ++- .../webSocketDriftClientAccountSubscriber.ts | 4 +- sdk/src/config.ts | 18 +++-- sdk/src/driftClient.ts | 7 +- sdk/src/oracles/oracleId.ts | 27 +++++++ 7 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 sdk/src/oracles/oracleId.ts diff --git a/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts b/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts index de96b3927..5167024ac 100644 --- a/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/pollingDriftClientAccountSubscriber.ts @@ -15,6 +15,7 @@ import { SpotMarketAccount, StateAccount, UserAccount, + OracleSource, } from '../types'; import { getDriftStateAccountPublicKey, @@ -28,8 +29,12 @@ import { OracleInfo, OraclePriceData } from '../oracles/types'; import { OracleClientCache } from '../oracles/oracleClientCache'; import { QUOTE_ORACLE_PRICE_DATA } from '../oracles/quoteAssetOracleClient'; import { findAllMarketAndOracles } from '../config'; +import { getOracleId } from '../oracles/oracleId'; -const ORACLE_DEFAULT_KEY = PublicKey.default.toBase58(); +const ORACLE_DEFAULT_ID = getOracleId( + PublicKey.default, + OracleSource.QUOTE_ASSET +); export class PollingDriftClientAccountSubscriber implements DriftClientAccountSubscriber @@ -217,10 +222,13 @@ export class PollingDriftClientAccountSubscriber } addOracleToPoll(oracleInfo: OracleInfo): boolean { - this.oraclesToPoll.set(oracleInfo.publicKey.toString(), { - publicKey: oracleInfo.publicKey, - source: oracleInfo.source, - }); + this.oraclesToPoll.set( + getOracleId(oracleInfo.publicKey, oracleInfo.source), + { + publicKey: oracleInfo.publicKey, + source: oracleInfo.source, + } + ); return true; } @@ -279,6 +287,8 @@ export class PollingDriftClientAccountSubscriber this.program ); + const oracleId = getOracleId(oracleToPoll.publicKey, oracleToPoll.source); + oracleToPoll.callbackId = await this.accountLoader.addAccount( oracleToPoll.publicKey, (buffer: Buffer, slot: number) => { @@ -291,11 +301,12 @@ export class PollingDriftClientAccountSubscriber slot, }; - this.oracles.set(oracleToPoll.publicKey.toString(), dataAndSlot); + this.oracles.set(oracleId, dataAndSlot); this.eventEmitter.emit( 'oraclePriceUpdate', oracleToPoll.publicKey, + oracleToPoll.source, oraclePriceData ); this.eventEmitter.emit('update'); @@ -353,9 +364,11 @@ export class PollingDriftClientAccountSubscriber ); const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(buffer); - this.oracles.set(oracleToPoll.publicKey.toString(), { - data: oraclePriceData, - slot, + this.oracles.set( + getOracleId(oracleToPoll.publicKey, oracleToPoll.source), + { + data: oraclePriceData, + slot, }); } } @@ -427,23 +440,23 @@ export class PollingDriftClientAccountSubscriber } async addOracle(oracleInfo: OracleInfo): Promise { + const oracleId = getOracleId(oracleInfo.publicKey, oracleInfo.source); if ( oracleInfo.publicKey.equals(PublicKey.default) || - this.oracles.has(oracleInfo.publicKey.toBase58()) + this.oracles.has(oracleId) ) { return true; } - const oracleString = oracleInfo.publicKey.toBase58(); // this func can be called multiple times before the first pauseForOracleToBeAdded finishes // avoid adding to oraclesToPoll multiple time - if (!this.oraclesToPoll.has(oracleString)) { + if (!this.oraclesToPoll.has(oracleId)) { this.addOracleToPoll(oracleInfo); - const oracleToPoll = this.oraclesToPoll.get(oracleString); + const oracleToPoll = this.oraclesToPoll.get(oracleId); await this.addOracleToAccountLoader(oracleToPoll); } - await this.pauseForOracleToBeAdded(3, oracleString); + await this.pauseForOracleToBeAdded(3, oracleInfo.publicKey.toBase58()); return true; } @@ -464,6 +477,7 @@ export class PollingDriftClientAccountSubscriber } console.log(`Pausing to find oracle ${oracle} failed`); } + async setPerpOracleMap() { const perpMarkets = this.getMarketAccountsAndSlots(); const oraclePromises = []; @@ -471,7 +485,8 @@ export class PollingDriftClientAccountSubscriber const perpMarketAccount = perpMarket.data; const perpMarketIndex = perpMarketAccount.marketIndex; const oracle = perpMarketAccount.amm.oracle; - if (!this.oracles.has(oracle.toBase58())) { + const oracleId = getOracleId(oracle, perpMarketAccount.amm.oracleSource); + if (!this.oracles.has(oracleId)) { oraclePromises.push( this.addOracle({ publicKey: oracle, @@ -480,7 +495,10 @@ export class PollingDriftClientAccountSubscriber ); } this.perpOracleMap.set(perpMarketIndex, oracle); - this.perpOracleStringMap.set(perpMarketIndex, oracle.toBase58()); + this.perpOracleStringMap.set( + perpMarketIndex, + oracleId + ); } await Promise.all(oraclePromises); } @@ -492,7 +510,8 @@ export class PollingDriftClientAccountSubscriber const spotMarketAccount = spotMarket.data; const spotMarketIndex = spotMarketAccount.marketIndex; const oracle = spotMarketAccount.oracle; - if (!this.oracles.has(oracle.toBase58())) { + const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource); + if (!this.oracles.has(oracleId)) { oraclePromises.push( this.addOracle({ publicKey: oracle, @@ -501,7 +520,7 @@ export class PollingDriftClientAccountSubscriber ); } this.spotOracleMap.set(spotMarketIndex, oracle); - this.spotOracleStringMap.set(spotMarketIndex, oracle.toBase58()); + this.spotOracleStringMap.set(spotMarketIndex, oracleId); } await Promise.all(oraclePromises); } @@ -528,10 +547,11 @@ export class PollingDriftClientAccountSubscriber } for (const oracle of oracles) { - const callbackId = this.oraclesToPoll.get(oracle.toBase58()).callbackId; - this.accountLoader.removeAccount(oracle, callbackId); + const oracleId = getOracleId(oracle.publicKey, oracle.source); + const callbackId = this.oraclesToPoll.get(oracleId).callbackId; + this.accountLoader.removeAccount(oracle.publicKey, callbackId); if (this.delistedMarketSetting === DelistedMarketSetting.Discard) { - this.oracles.delete(oracle.toBase58()); + this.oracles.delete(oracleId); } } } @@ -570,21 +590,17 @@ export class PollingDriftClientAccountSubscriber } public getOraclePriceDataAndSlot( - oraclePublicKey: PublicKey | string + oracleId: string ): DataAndSlot | undefined { this.assertIsSubscribed(); - const oracleString = - typeof oraclePublicKey === 'string' - ? oraclePublicKey - : oraclePublicKey.toBase58(); - if (oracleString === ORACLE_DEFAULT_KEY) { + if (oracleId === ORACLE_DEFAULT_ID) { return { data: QUOTE_ORACLE_PRICE_DATA, slot: 0, }; } - return this.oracles.get(oracleString); + return this.oracles.get(oracleId); } public getOraclePriceDataAndSlotForPerpMarket( @@ -592,7 +608,7 @@ export class PollingDriftClientAccountSubscriber ): DataAndSlot | undefined { const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex); const oracle = this.perpOracleMap.get(marketIndex); - const oracleString = this.perpOracleStringMap.get(marketIndex); + const oracleId = this.perpOracleStringMap.get(marketIndex); if (!perpMarketAccount || !oracle) { return undefined; @@ -603,7 +619,7 @@ export class PollingDriftClientAccountSubscriber this.setPerpOracleMap(); } - return this.getOraclePriceDataAndSlot(oracleString); + return this.getOraclePriceDataAndSlot(oracleId); } public getOraclePriceDataAndSlotForSpotMarket( @@ -611,7 +627,7 @@ export class PollingDriftClientAccountSubscriber ): DataAndSlot | undefined { const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex); const oracle = this.spotOracleMap.get(marketIndex); - const oracleString = this.spotOracleStringMap.get(marketIndex); + const oracleId = this.spotOracleStringMap.get(marketIndex); if (!spotMarketAccount || !oracle) { return undefined; } @@ -621,7 +637,7 @@ export class PollingDriftClientAccountSubscriber this.setSpotOracleMap(); } - return this.getOraclePriceDataAndSlot(oracleString); + return this.getOraclePriceDataAndSlot(oracleId); } public updateAccountLoaderPollingFrequency(pollingFrequency: number): void { diff --git a/sdk/src/accounts/types.ts b/sdk/src/accounts/types.ts index 8eb55ba58..73dfb438c 100644 --- a/sdk/src/accounts/types.ts +++ b/sdk/src/accounts/types.ts @@ -43,7 +43,11 @@ export interface DriftClientAccountEvents { stateAccountUpdate: (payload: StateAccount) => void; perpMarketAccountUpdate: (payload: PerpMarketAccount) => void; spotMarketAccountUpdate: (payload: SpotMarketAccount) => void; - oraclePriceUpdate: (publicKey: PublicKey, data: OraclePriceData) => void; + oraclePriceUpdate: ( + publicKey: PublicKey, + oracleSource: OracleSource, + data: OraclePriceData + ) => void; userAccountUpdate: (payload: UserAccount) => void; update: void; error: (e: Error) => void; @@ -73,7 +77,7 @@ export interface DriftClientAccountSubscriber { ): DataAndSlot | undefined; getSpotMarketAccountsAndSlots(): DataAndSlot[]; getOraclePriceDataAndSlot( - oraclePublicKey: PublicKey | string + oracleId: string ): DataAndSlot | undefined; getOraclePriceDataAndSlotForPerpMarket( marketIndex: number diff --git a/sdk/src/accounts/utils.ts b/sdk/src/accounts/utils.ts index b0c57bb94..08bd12ec7 100644 --- a/sdk/src/accounts/utils.ts +++ b/sdk/src/accounts/utils.ts @@ -1,6 +1,7 @@ import { PublicKey } from '@solana/web3.js'; import { DataAndSlot } from './types'; import { isVariant, PerpMarketAccount, SpotMarketAccount } from '../types'; +import { OracleInfo } from '../oracles/types'; export function capitalize(value: string): string { return value[0].toUpperCase() + value.slice(1); @@ -9,9 +10,9 @@ export function capitalize(value: string): string { export function findDelistedPerpMarketsAndOracles( perpMarkets: DataAndSlot[], spotMarkets: DataAndSlot[] -): { perpMarketIndexes: number[]; oracles: PublicKey[] } { +): { perpMarketIndexes: number[]; oracles: OracleInfo[] } { const delistedPerpMarketIndexes = []; - const delistedOracles = []; + const delistedOracles: OracleInfo[] = []; for (const perpMarket of perpMarkets) { if (!perpMarket.data) { continue; @@ -19,7 +20,10 @@ export function findDelistedPerpMarketsAndOracles( if (isVariant(perpMarket.data.status, 'delisted')) { delistedPerpMarketIndexes.push(perpMarket.data.marketIndex); - delistedOracles.push(perpMarket.data.amm.oracle); + delistedOracles.push({ + publicKey: perpMarket.data.amm.oracle, + source: perpMarket.data.amm.oracleSource, + }); } } @@ -31,7 +35,7 @@ export function findDelistedPerpMarketsAndOracles( continue; } - if (spotMarket.data.oracle.equals(delistedOracle)) { + if (spotMarket.data.oracle.equals(delistedOracle.publicKey)) { break; } } diff --git a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts index 14c93e4ca..35dcecb52 100644 --- a/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts +++ b/sdk/src/accounts/webSocketDriftClientAccountSubscriber.ts @@ -508,9 +508,9 @@ export class WebSocketDriftClientAccountSubscriber } for (const oracle of oracles) { - await this.oracleSubscribers.get(oracle.toBase58()).unsubscribe(); + await this.oracleSubscribers.get(oracle.publicKey.toBase58()).unsubscribe(); if (this.delistedMarketSetting === DelistedMarketSetting.Discard) { - this.oracleSubscribers.delete(oracle.toBase58()); + this.oracleSubscribers.delete(oracle.publicKey.toBase58()); } } } diff --git a/sdk/src/config.ts b/sdk/src/config.ts index 33e6be442..32adc95b1 100644 --- a/sdk/src/config.ts +++ b/sdk/src/config.ts @@ -18,6 +18,7 @@ import { ON_DEMAND_DEVNET_PID, ON_DEMAND_MAINNET_PID, } from '@switchboard-xyz/on-demand'; +import { getOracleId } from './oracles/oracleId'; type DriftConfig = { ENV: DriftEnv; @@ -134,7 +135,7 @@ export function getMarketsAndOraclesForSubscription( for (const market of perpMarketsToUse) { perpMarketIndexes.push(market.marketIndex); - oracleInfos.set(market.oracle.toString(), { + oracleInfos.set(getOracleId(market.oracle, market.oracleSource), { publicKey: market.oracle, source: market.oracleSource, }); @@ -142,7 +143,7 @@ export function getMarketsAndOraclesForSubscription( for (const spotMarket of spotMarketsToUse) { spotMarketIndexes.push(spotMarket.marketIndex); - oracleInfos.set(spotMarket.oracle.toString(), { + oracleInfos.set(getOracleId(spotMarket.oracle, spotMarket.oracleSource), { publicKey: spotMarket.oracle, source: spotMarket.oracleSource, }); @@ -174,16 +175,19 @@ export async function findAllMarketAndOracles(program: Program): Promise<{ for (const perpMarketProgramAccount of perpMarketProgramAccounts) { const perpMarket = perpMarketProgramAccount.account as PerpMarketAccount; perpMarketIndexes.push(perpMarket.marketIndex); - oracleInfos.set(perpMarket.amm.oracle.toString(), { - publicKey: perpMarket.amm.oracle, - source: perpMarket.amm.oracleSource, - }); + oracleInfos.set( + getOracleId(perpMarket.amm.oracle, perpMarket.amm.oracleSource), + { + publicKey: perpMarket.amm.oracle, + source: perpMarket.amm.oracleSource, + } + ); } for (const spotMarketProgramAccount of spotMarketProgramAccounts) { const spotMarket = spotMarketProgramAccount.account as SpotMarketAccount; spotMarketIndexes.push(spotMarket.marketIndex); - oracleInfos.set(spotMarket.oracle.toString(), { + oracleInfos.set(getOracleId(spotMarket.oracle, spotMarket.oracleSource), { publicKey: spotMarket.oracle, source: spotMarket.oracleSource, }); diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 48f6bd86c..a96c005cd 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -31,6 +31,7 @@ import { ModifyOrderPolicy, OpenbookV2FulfillmentConfigAccount, OptionalOrderParams, + OracleSource, Order, OrderParams, OrderTriggerCondition, @@ -175,6 +176,7 @@ import { gprcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAcco import nacl from 'tweetnacl'; import { digest } from './util/digest'; import { Slothash } from './slot/SlothashSubscriber'; +import { getOracleId } from './oracles/oracleId'; type RemainingAccountParams = { userAccounts: UserAccount[]; @@ -600,10 +602,11 @@ export class DriftClient { } public getOraclePriceDataAndSlot( - oraclePublicKey: PublicKey + oraclePublicKey: PublicKey, + oracleSource: OracleSource, ): DataAndSlot | undefined { return this.accountSubscriber.getOraclePriceDataAndSlot( - oraclePublicKey.toBase58() + getOracleId(oraclePublicKey, oracleSource) ); } diff --git a/sdk/src/oracles/oracleId.ts b/sdk/src/oracles/oracleId.ts new file mode 100644 index 000000000..396c85660 --- /dev/null +++ b/sdk/src/oracles/oracleId.ts @@ -0,0 +1,27 @@ +import { PublicKey } from '@solana/web3.js'; +import { OracleSource, OracleSourceNum } from '../types'; + +export function getOracleSourceNum(source: OracleSource): number { + if ('pyth' in source) return OracleSourceNum.PYTH; + if ('pyth1K' in source) return OracleSourceNum.PYTH_1K; + if ('pyth1M' in source) return OracleSourceNum.PYTH_1M; + if ('pythPull' in source) return OracleSourceNum.PYTH_PULL; + if ('pyth1KPull' in source) return OracleSourceNum.PYTH_1K_PULL; + if ('pyth1MPull' in source) return OracleSourceNum.PYTH_1M_PULL; + if ('switchboard' in source) return OracleSourceNum.SWITCHBOARD; + if ('quoteAsset' in source) return OracleSourceNum.QUOTE_ASSET; + if ('pythStableCoin' in source) return OracleSourceNum.PYTH_STABLE_COIN; + if ('pythStableCoinPull' in source) + return OracleSourceNum.PYTH_STABLE_COIN_PULL; + if ('prelaunch' in source) return OracleSourceNum.PRELAUNCH; + if ('switchboardOnDemand' in source) + return OracleSourceNum.SWITCHBOARD_ON_DEMAND; + throw new Error('Invalid oracle source'); +} + +export function getOracleId( + publicKey: PublicKey, + source: OracleSource +): string { + return `${publicKey.toBase58()}-${getOracleSourceNum(source)}`; +}