@haven-fi/solauto-sdk 1.0.835 → 1.0.837

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/constants/switchboardConstants.d.ts.map +1 -1
  2. package/dist/constants/switchboardConstants.js +2 -25
  3. package/dist/externalSdks/marginfi/types/oracleSetup.d.ts +22 -1
  4. package/dist/externalSdks/marginfi/types/oracleSetup.d.ts.map +1 -1
  5. package/dist/externalSdks/marginfi/types/oracleSetup.js +21 -0
  6. package/dist/services/flashLoans/marginfiFlProvider.d.ts.map +1 -1
  7. package/dist/services/flashLoans/marginfiFlProvider.js +5 -8
  8. package/dist/services/solauto/solautoMarginfiClient.d.ts +3 -0
  9. package/dist/services/solauto/solautoMarginfiClient.d.ts.map +1 -1
  10. package/dist/services/solauto/solautoMarginfiClient.js +22 -12
  11. package/dist/services/transactions/manager/transactionsManager.d.ts.map +1 -1
  12. package/dist/services/transactions/manager/transactionsManager.js +2 -1
  13. package/dist/solautoPosition/marginfiSolautoPositionEx.d.ts.map +1 -1
  14. package/dist/solautoPosition/marginfiSolautoPositionEx.js +4 -2
  15. package/dist/utils/marginfi/data.d.ts.map +1 -1
  16. package/dist/utils/marginfi/data.js +9 -15
  17. package/dist/utils/marginfi/general.d.ts +5 -0
  18. package/dist/utils/marginfi/general.d.ts.map +1 -1
  19. package/dist/utils/marginfi/general.js +36 -8
  20. package/dist/utils/marginfi/index.d.ts +1 -0
  21. package/dist/utils/marginfi/index.d.ts.map +1 -1
  22. package/dist/utils/marginfi/index.js +1 -0
  23. package/dist/utils/marginfi/prices.d.ts +14 -0
  24. package/dist/utils/marginfi/prices.d.ts.map +1 -0
  25. package/dist/utils/marginfi/prices.js +119 -0
  26. package/dist/utils/switchboardUtils.d.ts +5 -3
  27. package/dist/utils/switchboardUtils.d.ts.map +1 -1
  28. package/dist/utils/switchboardUtils.js +35 -12
  29. package/local/txSandbox.ts +3 -3
  30. package/package.json +1 -1
  31. package/src/constants/switchboardConstants.ts +2 -27
  32. package/src/externalSdks/marginfi/types/oracleSetup.ts +21 -0
  33. package/src/services/flashLoans/marginfiFlProvider.ts +10 -11
  34. package/src/services/solauto/solautoMarginfiClient.ts +34 -12
  35. package/src/services/transactions/manager/transactionsManager.ts +4 -1
  36. package/src/solautoPosition/marginfiSolautoPositionEx.ts +14 -3
  37. package/src/utils/marginfi/data.ts +16 -16
  38. package/src/utils/marginfi/general.ts +46 -12
  39. package/src/utils/marginfi/index.ts +2 -1
  40. package/src/utils/marginfi/prices.ts +165 -0
  41. package/src/utils/switchboardUtils.ts +55 -17
  42. package/tests/fixtures/README.md +17 -0
  43. package/tests/fixtures/marginfiOracles.json +82 -0
  44. package/tests/unit/marginfiOracles.ts +333 -0
  45. package/tests/unit/transactionsManager.ts +44 -1
@@ -18,7 +18,6 @@ import {
18
18
  safeFetchMarginfiAccount,
19
19
  } from "../../externalSdks/marginfi";
20
20
  import { ContextUpdates } from "../solautoUtils";
21
- import { fetchTokenPrices, safeGetPrice } from "../priceUtils";
22
21
  import { currentUnixSeconds, validPubkey } from "../generalUtils";
23
22
  import {
24
23
  bytesToI80F48,
@@ -32,6 +31,7 @@ import {
32
31
  calcMarginfiMaxLtvAndLiqThresholdBps,
33
32
  marginfiAccountEmpty,
34
33
  } from "./general";
34
+ import { getMarginfiBankPrice } from "./prices";
35
35
 
36
36
  export async function getMarginfiMaxLtvAndLiqThresholdBps(
37
37
  umi: Umi,
@@ -76,10 +76,7 @@ export async function getMarginfiMaxLtvAndLiqThresholdBps(
76
76
  }
77
77
 
78
78
  if (!supplyPrice) {
79
- const [price] = await fetchTokenPrices([
80
- toWeb3JsPublicKey(supply.bank!.mint),
81
- ]);
82
- supplyPrice = price;
79
+ supplyPrice = await getMarginfiBankPrice(umi, supply.bank!);
83
80
  }
84
81
 
85
82
  if (!debt.bank || debt.bank === null) {
@@ -239,6 +236,7 @@ export function getBankLiquidityUsedBaseUnit(
239
236
  }
240
237
 
241
238
  async function getTokenUsage(
239
+ umi: Umi,
242
240
  bank: Bank | null,
243
241
  isAsset: boolean,
244
242
  shares: number,
@@ -251,10 +249,7 @@ async function getTokenUsage(
251
249
  let originationFee = 0;
252
250
 
253
251
  if (bank !== null) {
254
- [marketPrice] = await fetchTokenPrices(
255
- [toWeb3JsPublicKey(bank.mint)],
256
- priceType
257
- );
252
+ marketPrice = await getMarginfiBankPrice(umi, bank, priceType);
258
253
  const [assetShareValue, liabilityShareValue] = getUpToDateShareValues(bank);
259
254
  const shareValue = isAsset ? assetShareValue : liabilityShareValue;
260
255
  amountUsed = shares * shareValue + Number(amountUsedAdjustment ?? 0);
@@ -365,10 +360,6 @@ export async function getMarginfiAccountPositionState(
365
360
  let supplyUsage: PositionTokenState | undefined = undefined;
366
361
  let debtUsage: PositionTokenState | undefined = undefined;
367
362
 
368
- if (supply.mint && debt.mint) {
369
- await fetchTokenPrices([supply.mint, debt.mint]);
370
- }
371
-
372
363
  if (
373
364
  marginfiAccount !== null &&
374
365
  marginfiAccount.lendingAccount.balances.filter((x) => x.active).length > 0
@@ -397,6 +388,7 @@ export async function getMarginfiAccountPositionState(
397
388
  supply.mint = toWeb3JsPublicKey(supplyBank!.mint);
398
389
  }
399
390
  supplyUsage = await getTokenUsage(
391
+ umi,
400
392
  supplyBank!,
401
393
  true,
402
394
  bytesToI80F48(supplyBalances[0].assetShares.value),
@@ -415,6 +407,7 @@ export async function getMarginfiAccountPositionState(
415
407
  debt.mint = toWeb3JsPublicKey(debtBank!.mint);
416
408
  }
417
409
  debtUsage = await getTokenUsage(
410
+ umi,
418
411
  debtBank!,
419
412
  false,
420
413
  bytesToI80F48(debtBalances[0].liabilityShares.value),
@@ -430,10 +423,12 @@ export async function getMarginfiAccountPositionState(
430
423
 
431
424
  if (!supplyUsage) {
432
425
  supplyUsage = await getTokenUsage(
426
+ umi,
433
427
  supplyBank,
434
428
  true,
435
429
  0,
436
- contextUpdates?.supplyAdjustment
430
+ contextUpdates?.supplyAdjustment,
431
+ priceType
437
432
  );
438
433
  }
439
434
 
@@ -458,17 +453,22 @@ export async function getMarginfiAccountPositionState(
458
453
 
459
454
  if (!debtUsage) {
460
455
  debtUsage = await getTokenUsage(
456
+ umi,
461
457
  debtBank,
462
458
  false,
463
459
  0,
464
- contextUpdates?.debtAdjustment
460
+ contextUpdates?.debtAdjustment,
461
+ priceType
465
462
  );
466
463
  }
467
464
 
468
465
  if (!marginfiGroup) {
469
466
  marginfiGroup = toWeb3JsPublicKey(supplyBank.group);
470
467
  }
471
- const supplyPrice = safeGetPrice(toWeb3JsPublicKey(supplyBank.mint))!;
468
+ const supplyPrice = fromBaseUnit(
469
+ supplyUsage.baseAmountMarketPriceUsd,
470
+ USD_DECIMALS
471
+ );
472
472
  let [maxLtvBps, liqThresholdBps] = await getMarginfiMaxLtvAndLiqThresholdBps(
473
473
  umi,
474
474
  marginfiGroup,
@@ -125,6 +125,40 @@ export async function getMarginfiPriceOracle(
125
125
  return priceOracle;
126
126
  }
127
127
 
128
+ // Keep each bank's variable-length oracle group intact when sorting health accounts.
129
+ export type MarginfiHealthAccountMeta = AccountMeta & {
130
+ oracleAccountCount?: number;
131
+ };
132
+
133
+ export function getMarginfiAuxiliaryOracleAccounts(bank: Bank): PublicKey[] {
134
+ if (bank.config.oracleSetup !== OracleSetup.PythLST) {
135
+ return [];
136
+ }
137
+ const pool = toWeb3JsPublicKey(bank.config.oracleKeys[1]);
138
+ if (!validPubkey(pool)) {
139
+ throw new Error(`Missing PythLST stake pool for bank ${bank.publicKey}`);
140
+ }
141
+ return [pool];
142
+ }
143
+
144
+ export async function getMarginfiBankHealthAccounts(
145
+ umi: Umi,
146
+ bank: Bank,
147
+ priceOracle?: PublicKey
148
+ ): Promise<MarginfiHealthAccountMeta[]> {
149
+ const oracles = [
150
+ priceOracle ?? (await getMarginfiPriceOracle(umi, { data: bank })),
151
+ ...getMarginfiAuxiliaryOracleAccounts(bank),
152
+ ];
153
+ return [
154
+ {
155
+ ...getAccountMeta(toWeb3JsPublicKey(bank.publicKey)),
156
+ oracleAccountCount: oracles.length,
157
+ },
158
+ ...oracles.map((oracle) => getAccountMeta(oracle)),
159
+ ];
160
+ }
161
+
128
162
  interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
129
163
  mint: PublicKey;
130
164
  }
@@ -165,13 +199,8 @@ export async function getRemainingAccountsForMarginfiHealthCheck(
165
199
  if (!balance.active) {
166
200
  return [];
167
201
  }
168
- const priceOracle = await getMarginfiPriceOracle(umi, {
169
- pk: toWeb3JsPublicKey(balance.bankPk),
170
- });
171
- return [
172
- getAccountMeta(toWeb3JsPublicKey(balance.bankPk)),
173
- getAccountMeta(priceOracle),
174
- ];
202
+ const bank = await fetchBank(umi, balance.bankPk);
203
+ return getMarginfiBankHealthAccounts(umi, bank);
175
204
  }
176
205
 
177
206
  export function calcMarginfiMaxLtvAndLiqThresholdBps(
@@ -221,11 +250,16 @@ export function marginfiAccountEmpty(marginfiAccount: MarginfiAccount) {
221
250
  }
222
251
 
223
252
  export function composeRemainingAccounts(accs: AccountMeta[]): AccountMeta[] {
224
- const banksAndOracles: [AccountMeta, AccountMeta][] = accs.reduce(
225
- (acc: [AccountMeta, AccountMeta][], _, i) =>
226
- i % 2 === 0 ? [...acc, [accs[i], accs[i + 1]]] : acc,
227
- []
228
- );
253
+ const banksAndOracles: AccountMeta[][] = [];
254
+ for (let i = 0; i < accs.length; ) {
255
+ const count =
256
+ 1 + ((accs[i] as MarginfiHealthAccountMeta).oracleAccountCount ?? 1);
257
+ if (!Number.isInteger(count) || count < 2 || i + count > accs.length) {
258
+ throw new Error("Incomplete Marginfi bank oracle accounts");
259
+ }
260
+ banksAndOracles.push(accs.slice(i, i + count));
261
+ i += count;
262
+ }
229
263
  return banksAndOracles
230
264
  .sort((a, b) => {
231
265
  const aBytes = toWeb3JsPublicKey(a[0].pubkey).toBuffer();
@@ -1,2 +1,3 @@
1
1
  export * from "./data";
2
- export * from "./general";
2
+ export * from "./general";
3
+ export * from "./prices";
@@ -0,0 +1,165 @@
1
+ import { PublicKey, SYSVAR_CLOCK_PUBKEY } from "@solana/web3.js";
2
+ import {
3
+ publicKey,
4
+ RpcAccount,
5
+ Umi,
6
+ assertAccountExists,
7
+ } from "@metaplex-foundation/umi";
8
+ import { Bank, OracleSetup, PriceBias } from "../../externalSdks/marginfi";
9
+ import { getPriceMessageSerializer } from "../../externalSdks/pyth";
10
+ import { PriceType } from "../../generated";
11
+ import { fetchTokenPrices } from "../priceUtils";
12
+ import { bytesToI80F48 } from "../numberUtils";
13
+
14
+ const STAKE_POOL_PROGRAMS = new Set([
15
+ "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy",
16
+ "SP12tWFxD9oJsVWNavTTBZvMbA6gkAmxtVgxdqvyvhY",
17
+ "SPMBzsVUuoHA4Jm6KunbsotaahvVikZs1JyTW6iJvbn",
18
+ ]);
19
+ const PYTH_RECEIVER = "rec2HHDDnjLfj4kE7VyEtFA1HPGQLK33259532cRyHp";
20
+
21
+ interface BankPrice {
22
+ realtimePrice: number;
23
+ emaPrice: number;
24
+ confInterval: number;
25
+ emaConfInterval: number;
26
+ }
27
+
28
+ // Bank prices include protocol-specific multipliers and must not be overwritten
29
+ // by the generic mint/USD market-price cache.
30
+ const bankPrices = new Map<string, BankPrice>();
31
+
32
+ function bankPriceKey(bank: Bank): string {
33
+ return [
34
+ bank.publicKey,
35
+ bank.config.oracleSetup,
36
+ ...bank.config.oracleKeys,
37
+ ...bank.config.padding.slice(6, 22),
38
+ ].join(":");
39
+ }
40
+
41
+ export function getPythLstPriceData(
42
+ bank: Bank,
43
+ oracle: RpcAccount,
44
+ stakePool: RpcAccount,
45
+ clock: RpcAccount
46
+ ): BankPrice {
47
+ const pool = Buffer.from(stakePool.data);
48
+ if (
49
+ stakePool.publicKey !== bank.config.oracleKeys[1] ||
50
+ !STAKE_POOL_PROGRAMS.has(stakePool.owner) ||
51
+ pool.length < 282 ||
52
+ pool[0] !== 1 ||
53
+ new PublicKey(pool.subarray(162, 194)).toString() !== bank.mint
54
+ ) {
55
+ throw new Error(`Invalid PythLST stake pool for bank ${bank.publicKey}`);
56
+ }
57
+ const clockData = Buffer.from(clock.data);
58
+ if (
59
+ clock.publicKey !== SYSVAR_CLOCK_PUBKEY.toString() ||
60
+ clockData.length < 40
61
+ ) {
62
+ throw new Error("Invalid clock account");
63
+ }
64
+ const epoch = clockData.readBigUInt64LE(16);
65
+ const now = clockData.readBigInt64LE(32);
66
+ const supply = pool.readBigUInt64LE(266);
67
+ if (supply === 0n || epoch > pool.readBigUInt64LE(274) + 1n) {
68
+ throw new Error("PythLST stake pool has zero supply or is stale");
69
+ }
70
+ const rate = (pool.readBigUInt64LE(258) << 48n) / supply;
71
+ if (rate <= 0n || rate >= 3n << 48n) {
72
+ throw new Error("Invalid PythLST exchange rate");
73
+ }
74
+ const data = Buffer.from(oracle.data);
75
+ if (
76
+ oracle.publicKey !== bank.config.oracleKeys[0] ||
77
+ oracle.owner !== PYTH_RECEIVER ||
78
+ data.length < 133 ||
79
+ data.subarray(0, 8).toString("hex") !== "22f123639d7ef4cd" ||
80
+ data[40] !== 1 // VerificationLevel::Full
81
+ ) {
82
+ throw new Error("Invalid PythLST base oracle");
83
+ }
84
+ const [price] = getPriceMessageSerializer().deserialize(data, 41);
85
+ if (now > price.publishTime + BigInt(bank.config.oracleMaxAge)) {
86
+ throw new Error("PythLST base oracle is stale");
87
+ }
88
+ if (price.price <= 0n || price.emaPrice <= 0n) {
89
+ throw new Error("Invalid PythLST base price");
90
+ }
91
+ // Match Marginfi's I80F48 multiplier and integer rounding before applying expo.
92
+ const scaled = (value: bigint) =>
93
+ Number((value * rate) >> 48n) * 10 ** price.exponent;
94
+ return {
95
+ realtimePrice: scaled(price.price),
96
+ emaPrice: scaled(price.emaPrice),
97
+ confInterval: scaled(price.conf) * 2.12,
98
+ emaConfInterval: scaled(price.emaConf) * 2.12,
99
+ };
100
+ }
101
+
102
+ function selectPrice(
103
+ data: BankPrice,
104
+ type = PriceType.Realtime,
105
+ bias?: PriceBias
106
+ ): number {
107
+ const price = type === PriceType.Ema ? data.emaPrice : data.realtimePrice;
108
+ const conf = Math.min(
109
+ type === PriceType.Ema ? data.emaConfInterval : data.confInterval,
110
+ price * 0.05
111
+ );
112
+ return bias === undefined
113
+ ? price
114
+ : price + (bias === PriceBias.Low ? -conf : conf);
115
+ }
116
+
117
+ export function safeGetMarginfiBankPrice(
118
+ bank: Bank | null,
119
+ type?: PriceType,
120
+ bias?: PriceBias
121
+ ) {
122
+ const data = bank ? bankPrices.get(bankPriceKey(bank)) : undefined;
123
+ return data ? selectPrice(data, type, bias) : undefined;
124
+ }
125
+
126
+ export async function getMarginfiBankPrice(
127
+ umi: Umi,
128
+ bank: Bank,
129
+ type?: PriceType,
130
+ bias?: PriceBias
131
+ ): Promise<number> {
132
+ let data: BankPrice;
133
+ if (bank.config.oracleSetup === OracleSetup.PythLST) {
134
+ const accounts = await umi.rpc.getAccounts(
135
+ [
136
+ bank.config.oracleKeys[0],
137
+ bank.config.oracleKeys[1],
138
+ publicKey(SYSVAR_CLOCK_PUBKEY),
139
+ ],
140
+ { commitment: "confirmed" }
141
+ );
142
+ for (const account of accounts) assertAccountExists(account);
143
+ data = getPythLstPriceData(
144
+ bank,
145
+ accounts[0] as RpcAccount,
146
+ accounts[1] as RpcAccount,
147
+ accounts[2] as RpcAccount
148
+ );
149
+ } else if (bank.config.oracleSetup === OracleSetup.Fixed) {
150
+ // BankConfig.fixed_price occupies bytes 808..824 of the bank, formerly padding.
151
+ const price = bytesToI80F48(bank.config.padding.slice(6, 22));
152
+ if (!Number.isFinite(price) || price <= 0)
153
+ throw new Error("Invalid fixed bank price");
154
+ data = {
155
+ realtimePrice: price,
156
+ emaPrice: price,
157
+ confInterval: 0,
158
+ emaConfInterval: 0,
159
+ };
160
+ } else {
161
+ return (await fetchTokenPrices([new PublicKey(bank.mint)], type, bias))[0];
162
+ }
163
+ bankPrices.set(bankPriceKey(bank), data);
164
+ return selectPrice(data, type, bias);
165
+ }
@@ -4,9 +4,9 @@ import {
4
4
  Transaction,
5
5
  VersionedTransaction,
6
6
  } from "@solana/web3.js";
7
- import { Signer, transactionBuilder } from "@metaplex-foundation/umi";
7
+ import { publicKey, Signer, transactionBuilder } from "@metaplex-foundation/umi";
8
8
  import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
9
- import { AnchorProvider, Idl, Program } from "@coral-xyz/anchor";
9
+ import { AnchorProvider, Program } from "@coral-xyz/anchor";
10
10
  import * as OnDemand from "@switchboard-xyz/on-demand";
11
11
  import Big from "big.js";
12
12
  import {
@@ -24,12 +24,19 @@ import {
24
24
  import { getWrappedInstruction } from "./solanaUtils";
25
25
  import { CrossbarClient } from "@switchboard-xyz/common";
26
26
  import { SolautoClient, TransactionItem } from "../services";
27
+ import { Bank, OracleSetup, fetchAllBank } from "../externalSdks/marginfi";
28
+ import { LendingPlatform } from "../generated";
27
29
 
28
30
  export async function getPullFeed(
29
31
  conn: Connection,
30
32
  mint: PublicKey,
31
- wallet?: PublicKey
33
+ wallet?: PublicKey,
34
+ feedId?: string
32
35
  ) {
36
+ const address = feedId ?? SWITCHBOARD_PRICE_FEED_IDS[mint.toString()]?.feedId;
37
+ if (!address) {
38
+ throw new Error(`No Switchboard feed configured for ${mint}`);
39
+ }
33
40
  const dummyWallet = {
34
41
  publicKey: wallet ?? new PublicKey("11111111111111111111111111111111"),
35
42
  signTransaction: async <T extends Transaction | VersionedTransaction>(
@@ -52,25 +59,24 @@ export async function getPullFeed(
52
59
  const crossbar = new CrossbarClient(CROSSBAR_URL);
53
60
 
54
61
  consoleLog("Pulled SWB program!");
55
- consoleLog("Feed id:", SWITCHBOARD_PRICE_FEED_IDS[mint.toString()].feedId);
62
+ consoleLog("Feed id:", address);
56
63
  return {
57
64
  crossbar,
58
- feed: new PullFeed(
59
- sbProgram,
60
- new PublicKey(SWITCHBOARD_PRICE_FEED_IDS[mint.toString()].feedId)
61
- ),
65
+ feed: new PullFeed(sbProgram, new PublicKey(address)),
62
66
  };
63
67
  }
64
68
 
65
69
  export async function buildSwbSubmitResponseTx(
66
70
  conn: Connection,
67
71
  signer: Signer,
68
- mint: PublicKey
72
+ mint: PublicKey,
73
+ feedId?: string
69
74
  ): Promise<TransactionItemInputs | undefined> {
70
75
  const { feed, crossbar } = await getPullFeed(
71
76
  conn,
72
77
  mint,
73
- toWeb3JsPublicKey(signer.publicKey)
78
+ toWeb3JsPublicKey(signer.publicKey),
79
+ feedId
74
80
  );
75
81
 
76
82
  consoleLog("Fetching crank IX...");
@@ -143,7 +149,8 @@ export async function buildSwbSubmitResponseTx(
143
149
 
144
150
  export async function getSwitchboardFeedData(
145
151
  conn: Connection,
146
- mints: PublicKey[]
152
+ mints: PublicKey[],
153
+ feedIds: Record<string, string> = {}
147
154
  ): Promise<{ mint: PublicKey; price: number; stale: boolean }[]> {
148
155
  if (mints.length === 0) {
149
156
  return [];
@@ -156,7 +163,9 @@ export async function getSwitchboardFeedData(
156
163
 
157
164
  const results = await Promise.all(
158
165
  mints.map(async (mint) => {
159
- const { feed } = await getPullFeed(conn, mint);
166
+ const { feed } = await getPullFeed(
167
+ conn, mint, undefined, feedIds[mint.toString()]
168
+ );
160
169
  const result = await feed.loadData();
161
170
  const price = Number(result.result.value) / Math.pow(10, 18);
162
171
  const stale =
@@ -173,22 +182,50 @@ export function isSwitchboardMint(mint: PublicKey | string) {
173
182
  return Object.keys(SWITCHBOARD_PRICE_FEED_IDS).includes(mint.toString());
174
183
  }
175
184
 
185
+ export function getMarginfiSwitchboardFeeds(
186
+ banks: Bank[]
187
+ ): Record<string, string> {
188
+ return Object.fromEntries(
189
+ banks
190
+ .filter((bank) => bank.config.oracleSetup === OracleSetup.SwitchboardPull)
191
+ .map((bank) => [
192
+ bank.mint.toString(), bank.config.oracleKeys[0].toString(),
193
+ ])
194
+ );
195
+ }
196
+
176
197
  export async function addSwbOraclePullTxs(
177
198
  client: SolautoClient,
178
199
  txs: TransactionItem[]
179
200
  ) {
201
+ if (!txs.some((tx) => tx.oracleInteractor)) {
202
+ return;
203
+ }
204
+ const feedIds =
205
+ client.lendingPlatform === LendingPlatform.Marginfi
206
+ ? getMarginfiSwitchboardFeeds(
207
+ await fetchAllBank(client.umi, [
208
+ publicKey(client.pos.lpSupplyAccount),
209
+ publicKey(client.pos.lpDebtAccount),
210
+ ])
211
+ )
212
+ : Object.fromEntries(
213
+ Object.entries(SWITCHBOARD_PRICE_FEED_IDS).map(
214
+ ([mint, feed]) => [mint, feed.feedId]
215
+ )
216
+ );
180
217
  const switchboardMints = [
181
- ...(isSwitchboardMint(client.pos.supplyMint)
218
+ ...(feedIds[client.pos.supplyMint.toString()]
182
219
  ? [client.pos.supplyMint]
183
220
  : []),
184
- ...(isSwitchboardMint(client.pos.debtMint) ? [client.pos.debtMint] : []),
221
+ ...(feedIds[client.pos.debtMint.toString()] ? [client.pos.debtMint] : []),
185
222
  ];
186
223
 
187
- if (txs.find((x) => x.oracleInteractor) && switchboardMints.length) {
224
+ if (switchboardMints.length) {
188
225
  consoleLog("Checking if oracle update(s) needed...");
189
226
  const staleOracles =
190
227
  (
191
- await getSwitchboardFeedData(client.connection, switchboardMints)
228
+ await getSwitchboardFeedData(client.connection, switchboardMints, feedIds)
192
229
  ).filter((x) => x.stale).length > 0;
193
230
 
194
231
  if (staleOracles) {
@@ -200,7 +237,8 @@ export async function addSwbOraclePullTxs(
200
237
  await buildSwbSubmitResponseTx(
201
238
  client.connection,
202
239
  client.signer,
203
- x
240
+ x,
241
+ feedIds[x.toString()]
204
242
  ),
205
243
  UPDATE_ORACLE_TX_NAME
206
244
  )
@@ -0,0 +1,17 @@
1
+ `marginfiOracles.json` contains public mainnet account snapshots: banks at slot
2
+ 446044167 and their Pyth/stake-pool/clock accounts at the `oracleSlot` recorded in
3
+ the JSON. They cover the six migrated production banks and staging USDC's fixed
4
+ oracle. Matching binary fixtures for Rust live in
5
+ `programs/solauto/tests/fixtures/marginfi-oracles`.
6
+
7
+ PythLST layout, validation, multiplier rounding, and oracle enum ordering follow
8
+ Marginfi commit `5c97c5efb68a24f68041d2bb7d90917b8dc989e2`:
9
+
10
+ - https://github.com/0dotxyz/marginfi-v2/blob/5c97c5efb68a24f68041d2bb7d90917b8dc989e2/programs/marginfi/src/state/lst_stake_price.rs
11
+ - https://github.com/0dotxyz/marginfi-v2/blob/5c97c5efb68a24f68041d2bb7d90917b8dc989e2/programs/marginfi/src/state/price.rs
12
+ - https://github.com/0dotxyz/marginfi-v2/blob/5c97c5efb68a24f68041d2bb7d90917b8dc989e2/type-crate/src/types/bank.rs
13
+
14
+ Regenerate the TypeScript and Rust oracle enums after changing the IDL with
15
+ `node scripts/generateMarginfiOracleSetup.cjs` from the repository root. This
16
+ preserves the local zero-copy Rust representation and leaves other generated
17
+ types intact.
@@ -0,0 +1,82 @@
1
+ {
2
+ "bankSlot": 446044167,
3
+ "oracleSlot": 446050655,
4
+ "banks": [
5
+ {
6
+ "ticker": "SOL",
7
+ "env": "Prod",
8
+ "publicKey": "CCKtUs6Cgwo4aaQUmBPmyoApH2gUDErxNZCAntD6LYGh",
9
+ "owner": "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA",
10
+ "data": "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"
11
+ },
12
+ {
13
+ "ticker": "JitoSOL",
14
+ "env": "Prod",
15
+ "publicKey": "Bohoc1ikHLD7xKJuzTyiTyCwzaL5N7ggJQu75A8mKYM8",
16
+ "owner": "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA",
17
+ "data": "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"
18
+ },
19
+ {
20
+ "ticker": "LST",
21
+ "env": "Prod",
22
+ "publicKey": "DMoqjmsuoru986HgfjqrKEvPv8YBufvBGADHUonkadC5",
23
+ "owner": "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA",
24
+ "data": "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"
25
+ },
26
+ {
27
+ "ticker": "hSOL",
28
+ "env": "Prod",
29
+ "publicKey": "GJCi1uj3kYPZ64puA5sLUiCQfFapxT2xnREzrbDzFkYY",
30
+ "owner": "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA",
31
+ "data": "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"
32
+ },
33
+ {
34
+ "ticker": "JupSOL",
35
+ "env": "Prod",
36
+ "publicKey": "8LaUZadNqtzuCG7iCvZd7d5cbquuYfv19KjAg6GPuuCb",
37
+ "owner": "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA",
38
+ "data": "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"
39
+ },
40
+ {
41
+ "ticker": "JTO",
42
+ "env": "Prod",
43
+ "publicKey": "EdB7YADw4XUt6wErT8kHGCUok4mnTpWGzPUU9rWDebzb",
44
+ "owner": "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA",
45
+ "data": "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"
46
+ },
47
+ {
48
+ "ticker": "USDC",
49
+ "env": "Staging",
50
+ "publicKey": "Ek5JSFJFD8QgXM6rPDCzf31XhDp1q3xezaWYSkJWqbqc",
51
+ "owner": "stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct",
52
+ "data": "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"
53
+ }
54
+ ],
55
+ "oracles": [
56
+ {
57
+ "publicKey": "7AviUf9nL62mcxNbQGKm4nKDQnPjswo6c5MX4D57HmyE",
58
+ "owner": "rec2HHDDnjLfj4kE7VyEtFA1HPGQLK33259532cRyHp",
59
+ "data": "IvEjY51+9M1bsRWwDdII6qyfKmvtWLaGyH3c9S5trfFYcCJDMhaezQHvDYtv2izrpB2hXUCV0do5Kg0vjtDGx7wPTPrIwoC1bcDABk4CAAAA4MgQAAAAAAD4////a3CjagAAAABqcKNqAAAAAKp7UU8CAAAAvjQSAAAAAABZMZYaAAAAAAA="
60
+ },
61
+ {
62
+ "publicKey": "DqhH94PjkZsjAqEze2BEkWhFQJ6EyU6MdtMphMgnXqeK",
63
+ "owner": "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy",
64
+ "data": "AeyQbgDMiaTMHdkWVVNLdGMVxLoFwrqhD9XjkhemN0Jq7JBuAMyJpMwd2RZVU0t0YxXEugXCuqEP1eOSF6Y3QmrqDe4Y8ubFZHboxqW5FNnQl3Kq6SYd/iUUo+aHmdwt5vtayAkn0fddk60vnf3OvAD5elAPGSXIVtRESx/G3HndK401jxVQVNnS45VEzpXzDK3ozdVsSjrNQcYBOgUnucLPBPrUIcjzdr78481pSM1jtr7FuKNvNk3rg6OT1yG31+8sd1hXU7BYzgFva118nVG70oz++wbPnrUKB1CrWVmF6wbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpb3cDYy5YAADYMjpINTkAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABC328Spcb8pCbTh1Z7T7NGrh+S/ukb06fWfmQ9dgYVFEBC328Spcb8pCbTh1Z7T7NGrh+S/ukb06fWfmQ9dgYVFEAAAAAAAAAAAAAAAAAAAAA6AMAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgDAAAAAAAAAQAAAAAAAAAAnNeRLDo5AAC31ydgMlgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
65
+ },
66
+ {
67
+ "publicKey": "3wK2g8ZdzAH8FJ7PKr2RcvGh7V9VYson5hrVsJM5Lmws",
68
+ "owner": "SP12tWFxD9oJsVWNavTTBZvMbA6gkAmxtVgxdqvyvhY",
69
+ "data": "AWx5DsQHemyL8o97W+i/lc2/qE7uZVlmLWzQRmmf8Uq5PRgH1PoaW6dH6RcZ08ZNaGOAAb+85vaQZ58y+nnoFQ3ggsINLCShcBz8KCWSf32p0btor05WI0yx978xxzyn5v4Gtp7vVH8vO306IX2rThUSuYvXyWyf40P5AWJp9WS9BA5Ag6PZH3S4lYTu8+zXnp3DrLvH0NcY+x5eXE+z18SBCmkVHaZQ17AWqh+o5ge5FZC1oVoIC8fiICDzu4Ls3cWjoubuCykyxsHy8XY9yowmeq9AcjM89g/9e7z5VcZCSAbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCp7Zd0mxBJAwDp38BWUsUCAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6AMAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADoAwAAAAAAAAEAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAA6AMAAAAAAAABAAAAAAAAAAAILxbincMCAAFxINTcRgMAEhZ5gZnDAQB7tA4MoekBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
70
+ },
71
+ {
72
+ "publicKey": "8VpRhuxa7sUUepdY3kQiTmX9rS5vx4WgaXiAnXq4KCtr",
73
+ "owner": "SPMBzsVUuoHA4Jm6KunbsotaahvVikZs1JyTW6iJvbn",
74
+ "data": "AWx5DsQHemyL8o97W+i/lc2/qE7uZVlmLWzQRmmf8Uq5PRgH1PoaW6dH6RcZ08ZNaGOAAb+85vaQZ58y+nnoFQ30eOslQRIaMKaoMtW2TlXWU9AjHE6T4rYFcG0gAEcDWv+NfiqSfMdnnCpSOMPeqZT7KzRiuJxVYIEJL6fNZPqt5NUt/JG1WJ29Ksc8kGcoLqsnn5xOHQ5vXEvwPxbznRS1Cv4dkWcUIsdlx6BqETn/YTnTgPy0Irp493i+1TxpfYHn0UFurkktcUEtumlKKGnEYaQkAutAgtRT50Mczm4nZQbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpci8QG1ViEgC+6o8kUDYPAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6AMAAAAAAAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADoAwAAAAAAAAEAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAAAAA6AMAAAAAAAABAAAAAAAAAADft7MExTUPAOtmkVuRYBIAifjvQXlcEABuGLhjVg4SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
75
+ },
76
+ {
77
+ "publicKey": "SysvarC1ock11111111111111111111111111111111",
78
+ "owner": "Sysvar1111111111111111111111111111111111111",
79
+ "data": "XzGWGgAAAABnWKJqAAAAAAgEAAAAAAAACQQAAAAAAABucKNqAAAAAA=="
80
+ }
81
+ ]
82
+ }