From d7a7492614d72e591b5ce7a78a3c0ee0a76651ed Mon Sep 17 00:00:00 2001 From: Lou-Kamades Date: Mon, 4 Dec 2023 18:32:02 -0600 Subject: [PATCH] enable use of fallback oracles in TS client --- lib/client/src/context.rs | 4 ++-- ts/client/src/accounts/bank.ts | 11 +++++++++ ts/client/src/client.ts | 41 ++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/client/src/context.rs b/lib/client/src/context.rs index 38698d04a..aecf29f8e 100644 --- a/lib/client/src/context.rs +++ b/lib/client/src/context.rs @@ -527,12 +527,12 @@ impl MangoGroupContext { for acc in oracle_accounts { let bank = banks_by_oracle.get(acc.key()).unwrap(); let state = oracle_state_unchecked(&acc, bank.mint_decimals)?; - if state + if (state .check_confidence(bank.name(), &bank.oracle_config) .is_err() || state .check_staleness(bank.name(), &bank.oracle_config, now_slot) - .is_err() + .is_err()) && bank.fallback_oracle != Pubkey::default() { fallbacks.push(bank.fallback_oracle); } diff --git a/ts/client/src/accounts/bank.ts b/ts/client/src/accounts/bank.ts index 6b491a773..1225bf60b 100644 --- a/ts/client/src/accounts/bank.ts +++ b/ts/client/src/accounts/bank.ts @@ -79,6 +79,7 @@ export class Bank implements BankForHealth { public maintWeightShiftDurationInv: I80F48; public maintWeightShiftAssetTarget: I80F48; public maintWeightShiftLiabTarget: I80F48; + public fallbackOracle: PublicKey; static from( publicKey: PublicKey, @@ -138,6 +139,7 @@ export class Bank implements BankForHealth { maintWeightShiftDurationInv: I80F48Dto; maintWeightShiftAssetTarget: I80F48Dto; maintWeightShiftLiabTarget: I80F48Dto; + fallbackOracle: PublicKey; }, ): Bank { return new Bank( @@ -197,6 +199,7 @@ export class Bank implements BankForHealth { obj.maintWeightShiftDurationInv, obj.maintWeightShiftAssetTarget, obj.maintWeightShiftLiabTarget, + obj.fallbackOracle ); } @@ -257,6 +260,7 @@ export class Bank implements BankForHealth { maintWeightShiftDurationInv: I80F48Dto, maintWeightShiftAssetTarget: I80F48Dto, maintWeightShiftLiabTarget: I80F48Dto, + public fallbaclOracle: PublicKey, ) { this.name = utf8.decode(new Uint8Array(name)).split('\x00')[0]; this.oracleConfig = { @@ -307,6 +311,8 @@ export class Bank implements BankForHealth { this.mintDecimals + '\n oracle - ' + this.oracle.toBase58() + + '\n fallback oracle - ' + + this.fallbackOracle.toBase58() + '\n price - ' + this._price?.toString() + '\n uiPrice - ' + @@ -622,6 +628,7 @@ export class MintInfo { banks: PublicKey[]; vaults: PublicKey[]; oracle: PublicKey; + fallbackOracle: PublicKey; registrationTime: BN; groupInsuranceFund: number; }, @@ -634,6 +641,7 @@ export class MintInfo { obj.banks, obj.vaults, obj.oracle, + obj.fallbackOracle, obj.registrationTime, obj.groupInsuranceFund == 1, ); @@ -647,6 +655,7 @@ export class MintInfo { public banks: PublicKey[], public vaults: PublicKey[], public oracle: PublicKey, + public fallbackOracle: PublicKey, public registrationTime: BN, public groupInsuranceFund: boolean, ) {} @@ -664,6 +673,8 @@ export class MintInfo { this.mint.toBase58() + '\n oracle ' + this.oracle.toBase58() + + '\n fallback oracle - ' + + this.fallbackOracle.toBase58() + '\n banks ' + this.banks .filter((pk) => pk.toBase58() !== PublicKey.default.toBase58()) diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 4775cf932..f700bb6dc 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -104,6 +104,17 @@ export enum AccountRetriever { Fixed, } +export enum FallbackOracleMode { + None, + Fixed, + AllAvailable, +} + +export type FallbackOracleOptions = { + mode: FallbackOracleMode, + fixedOracles: PublicKey[] +} + export type IdsSource = 'api' | 'static' | 'get-program-accounts'; export type MangoClientOptions = { @@ -114,6 +125,7 @@ export type MangoClientOptions = { txConfirmationCommitment?: Commitment; openbookFeesToDao?: boolean; prependedGlobalAdditionalInstructions?: TransactionInstruction[]; + fallbackOracleOptions?: FallbackOracleOptions; }; export class MangoClient { @@ -124,6 +136,10 @@ export class MangoClient { private txConfirmationCommitment: Commitment; private openbookFeesToDao: boolean; private prependedGlobalAdditionalInstructions: TransactionInstruction[] = []; + private fallbackOracleOptions: FallbackOracleOptions = { + mode: FallbackOracleMode.None, + fixedOracles: [] + }; constructor( public program: Program, @@ -4498,6 +4514,16 @@ export class MangoClient { .map((serumPosition) => serumPosition.openOrders), ); + switch (this.fallbackOracleOptions.mode) { + case FallbackOracleMode.None: + break; + case FallbackOracleMode.Fixed: + healthRemainingAccounts.push(...this.fallbackOracleOptions.fixedOracles) + case FallbackOracleMode.AllAvailable: + healthRemainingAccounts.push( + ...mintInfos.filter((mintInfo) => mintInfo.fallbackOracle.toBase58() != PublicKey.default.toBase58()).map(m => m.fallbackOracle)) + } + return healthRemainingAccounts; } @@ -4634,4 +4660,19 @@ export class MangoClient { return Math.max(1, Math.ceil(medianFee)); } + + /** + * Checks all oracles and returns the PublicKeys of any configured fallback oracles if the primary oracle is stale or outside the confidence interval. + * This function assumes that the group has Banks. + * + */ + public async fetchFallbacksForStaleOracles(group: Group): Promise { + await group.reloadBankOraclePrices(this) + const banks: Bank[][] = Array.from( + group.banksMapByMint, + ([, value]) => value, + ); + const staleBanks = banks.filter((b) => b[0].isOracleStaleOrUnconfident).map((s) => s[0].fallbackOracle); + return staleBanks + } }