@haven-fi/solauto-sdk 1.0.625 → 1.0.627

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 (58) hide show
  1. package/README.md +115 -0
  2. package/dist/constants/marginfiAccounts.d.ts +10 -6
  3. package/dist/constants/marginfiAccounts.d.ts.map +1 -1
  4. package/dist/constants/marginfiAccounts.js +61 -37
  5. package/dist/constants/pythConstants.d.ts +4 -0
  6. package/dist/constants/pythConstants.d.ts.map +1 -1
  7. package/dist/constants/pythConstants.js +5 -1
  8. package/dist/services/flashLoans/marginfiFlProvider.d.ts.map +1 -1
  9. package/dist/services/flashLoans/marginfiFlProvider.js +9 -9
  10. package/dist/services/solauto/solautoClient.d.ts.map +1 -1
  11. package/dist/services/solauto/solautoClient.js +6 -6
  12. package/dist/services/solauto/solautoMarginfiClient.d.ts +2 -1
  13. package/dist/services/solauto/solautoMarginfiClient.d.ts.map +1 -1
  14. package/dist/services/solauto/solautoMarginfiClient.js +15 -17
  15. package/dist/services/transactions/transactionUtils.d.ts.map +1 -1
  16. package/dist/services/transactions/transactionUtils.js +3 -2
  17. package/dist/solautoPosition/marginfiSolautoPositionEx.d.ts +5 -0
  18. package/dist/solautoPosition/marginfiSolautoPositionEx.d.ts.map +1 -1
  19. package/dist/solautoPosition/marginfiSolautoPositionEx.js +20 -6
  20. package/dist/solautoPosition/solautoPositionEx.d.ts +5 -1
  21. package/dist/solautoPosition/solautoPositionEx.d.ts.map +1 -1
  22. package/dist/solautoPosition/solautoPositionEx.js +11 -4
  23. package/dist/solautoPosition/utils.js +1 -1
  24. package/dist/types/accounts.d.ts +0 -1
  25. package/dist/types/accounts.d.ts.map +1 -1
  26. package/dist/utils/generalUtils.d.ts +1 -0
  27. package/dist/utils/generalUtils.d.ts.map +1 -1
  28. package/dist/utils/generalUtils.js +10 -0
  29. package/dist/utils/index.d.ts +1 -0
  30. package/dist/utils/index.d.ts.map +1 -1
  31. package/dist/utils/index.js +1 -0
  32. package/dist/utils/marginfiUtils.d.ts +13 -3
  33. package/dist/utils/marginfiUtils.d.ts.map +1 -1
  34. package/dist/utils/marginfiUtils.js +78 -20
  35. package/dist/utils/pythUtils.d.ts +21 -0
  36. package/dist/utils/pythUtils.d.ts.map +1 -0
  37. package/dist/utils/pythUtils.js +67 -0
  38. package/dist/utils/solautoUtils.js +1 -1
  39. package/local/txSandbox.ts +3 -3
  40. package/local/updateMarginfiLUT.ts +17 -26
  41. package/package.json +1 -1
  42. package/src/constants/marginfiAccounts.ts +80 -37
  43. package/src/constants/pythConstants.ts +8 -0
  44. package/src/services/flashLoans/marginfiFlProvider.ts +13 -11
  45. package/src/services/solauto/solautoClient.ts +7 -6
  46. package/src/services/solauto/solautoMarginfiClient.ts +18 -26
  47. package/src/services/transactions/transactionUtils.ts +1 -1
  48. package/src/solautoPosition/marginfiSolautoPositionEx.ts +28 -9
  49. package/src/solautoPosition/solautoPositionEx.ts +18 -5
  50. package/src/solautoPosition/utils.ts +1 -1
  51. package/src/types/accounts.ts +0 -1
  52. package/src/utils/generalUtils.ts +12 -0
  53. package/src/utils/index.ts +1 -0
  54. package/src/utils/marginfiUtils.ts +123 -30
  55. package/src/utils/pythUtils.ts +84 -0
  56. package/src/utils/solautoUtils.ts +1 -1
  57. package/tests/unit/accounts.ts +7 -13
  58. package/tests/unit/lookupTables.ts +27 -48
@@ -13,11 +13,15 @@ import {
13
13
  fromBaseUnit,
14
14
  getBankLiquidityAvailableBaseUnit,
15
15
  getMarginfiAccountPositionState,
16
+ getMarginfiPriceOracle,
16
17
  } from "../utils";
17
- import { DEFAULT_MARGINFI_GROUP, MARGINFI_ACCOUNTS } from "../constants";
18
+ import { getMarginfiAccounts } from "../constants";
18
19
  import { SolautoPositionEx } from "./solautoPositionEx";
20
+ import { LendingPlatform } from "../generated";
19
21
 
20
22
  export class MarginfiSolautoPositionEx extends SolautoPositionEx {
23
+ lendingPlatform = LendingPlatform.Marginfi;
24
+
21
25
  private marginfiAccountData: MarginfiAccount | null = null;
22
26
  private supplyBank: Bank | null = null;
23
27
  private debtBank: Bank | null = null;
@@ -41,19 +45,18 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
41
45
  }
42
46
 
43
47
  if (!this.lp) {
44
- this.lp = new PublicKey(DEFAULT_MARGINFI_GROUP);
48
+ this.lp = getMarginfiAccounts(this.lpEnv).defaultGroup;
45
49
  }
46
50
 
47
51
  return this.lp;
48
52
  }
49
53
 
50
- async maxLtvAndLiqThresholdBps(): Promise<[number, number]> {
54
+ async getBanks(): Promise<Bank[]> {
51
55
  if (!this.supplyBank || !this.debtBank) {
52
56
  const group = (await this.lendingPool()).toString();
53
- const supplyBank =
54
- MARGINFI_ACCOUNTS[group][this.supplyMint().toString()].bank;
55
- const debtBank =
56
- MARGINFI_ACCOUNTS[group][this.debtMint().toString()].bank;
57
+ const bankAccounts = getMarginfiAccounts(this.lpEnv).bankAccounts;
58
+ const supplyBank = bankAccounts[group][this.supplyMint().toString()].bank;
59
+ const debtBank = bankAccounts[group][this.debtMint().toString()].bank;
57
60
 
58
61
  [this.supplyBank, this.debtBank] = await safeFetchAllBank(this.umi, [
59
62
  publicKey(supplyBank),
@@ -61,10 +64,25 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
61
64
  ]);
62
65
  }
63
66
 
67
+ return [this.supplyBank!, this.debtBank!];
68
+ }
69
+
70
+ async priceOracles(): Promise<PublicKey[]> {
71
+ const [supplyBank, debtBank] = await this.getBanks();
72
+
73
+ return await Promise.all([
74
+ getMarginfiPriceOracle(this.umi, { data: supplyBank }),
75
+ getMarginfiPriceOracle(this.umi, { data: debtBank }),
76
+ ]);
77
+ }
78
+
79
+ async maxLtvAndLiqThresholdBps(): Promise<[number, number]> {
80
+ const [supplyBank, debtBank] = await this.getBanks();
81
+
64
82
  const [supplyPrice] = await fetchTokenPrices([this.supplyMint()]);
65
83
  const [maxLtvBps, liqThresholdBps] = calcMarginfiMaxLtvAndLiqThresholdBps(
66
- this.supplyBank,
67
- this.debtBank,
84
+ supplyBank,
85
+ debtBank,
68
86
  supplyPrice
69
87
  );
70
88
 
@@ -94,6 +112,7 @@ export class MarginfiSolautoPositionEx extends SolautoPositionEx {
94
112
  useDesignatedMint
95
113
  ? { mint: toWeb3JsPublicKey(this.state().debt.mint) }
96
114
  : undefined,
115
+ this.lpEnv,
97
116
  this.contextUpdates
98
117
  );
99
118
 
@@ -37,7 +37,7 @@ import {
37
37
  tokenInfo,
38
38
  toRoundedUsdValue,
39
39
  } from "../utils";
40
- import { RebalanceAction } from "../types";
40
+ import { ProgramEnv, RebalanceAction } from "../types";
41
41
  import {
42
42
  getDebtAdjustment,
43
43
  getRebalanceValues,
@@ -51,6 +51,7 @@ export interface PositionCustomArgs {
51
51
  debtMint?: PublicKey;
52
52
  lendingPool?: PublicKey;
53
53
  lpUserAccount?: PublicKey;
54
+ lpEnv?: ProgramEnv;
54
55
  }
55
56
 
56
57
  interface SolautoPositionExData extends Partial<SolautoPosition> {
@@ -66,10 +67,12 @@ interface PositionExArgs {
66
67
  }
67
68
 
68
69
  export abstract class SolautoPositionEx {
70
+ public lendingPlatform!: LendingPlatform;
69
71
  public umi!: Umi;
70
72
  public publicKey!: PublicKey;
71
73
  protected _data!: SolautoPositionExData;
72
74
  protected lp?: PublicKey = undefined;
75
+ protected lpEnv!: ProgramEnv;
73
76
  public lpUserAccount?: PublicKey = undefined;
74
77
  protected contextUpdates?: ContextUpdates;
75
78
 
@@ -86,6 +89,7 @@ export abstract class SolautoPositionEx {
86
89
  (args.data.position
87
90
  ? toWeb3JsPublicKey(args.data.position!.protocolUserAccount)
88
91
  : undefined);
92
+ this.lpEnv = args.customArgs?.lpEnv ?? "Prod";
89
93
 
90
94
  this._data = args.data;
91
95
  this.firstState = { ...args.data.state };
@@ -183,7 +187,10 @@ export abstract class SolautoPositionEx {
183
187
  }
184
188
 
185
189
  supplyUsd() {
186
- return calcSupplyUsd(this.state());
190
+ const supplyPrice = safeGetPrice(this.supplyMint());
191
+ return supplyPrice
192
+ ? calcTotalSupply(this.state()) * supplyPrice
193
+ : calcSupplyUsd(this.state());
187
194
  }
188
195
 
189
196
  totalDebt() {
@@ -191,7 +198,10 @@ export abstract class SolautoPositionEx {
191
198
  }
192
199
 
193
200
  debtUsd() {
194
- return calcDebtUsd(this.state());
201
+ const debtPrice = safeGetPrice(this.debtMint());
202
+ return debtPrice
203
+ ? calcTotalDebt(this.state()) * debtPrice
204
+ : calcDebtUsd(this.state());
195
205
  }
196
206
 
197
207
  supplyLiquidityDepositable() {
@@ -203,7 +213,9 @@ export abstract class SolautoPositionEx {
203
213
  }
204
214
 
205
215
  supplyLiquidityUsdAvailable() {
206
- return this.supplyLiquidityAvailable() * (safeGetPrice(this.supplyMint()) ?? 0);
216
+ return (
217
+ this.supplyLiquidityAvailable() * (safeGetPrice(this.supplyMint()) ?? 0)
218
+ );
207
219
  }
208
220
 
209
221
  debtLiquidityAvailable() {
@@ -215,6 +227,7 @@ export abstract class SolautoPositionEx {
215
227
  }
216
228
 
217
229
  abstract maxLtvAndLiqThresholdBps(): Promise<[number, number]>;
230
+ abstract priceOracles(): Promise<PublicKey[]>;
218
231
  abstract supplyLiquidityAvailable(): number;
219
232
 
220
233
  sufficientLiquidityToBoost() {
@@ -244,7 +257,7 @@ export abstract class SolautoPositionEx {
244
257
  }
245
258
 
246
259
  eligibleForRebalance(bpsDistanceThreshold = 0): RebalanceAction | undefined {
247
- if (!this.settings() || !calcSupplyUsd(this.state())) {
260
+ if (!this.settings() || !this.supplyUsd()) {
248
261
  return undefined;
249
262
  }
250
263
 
@@ -41,7 +41,7 @@ export async function getPositionExBulk(
41
41
  umi: Umi,
42
42
  publicKeys: PublicKey[]
43
43
  ): Promise<SolautoPositionEx[]> {
44
- const batches = getBatches(publicKeys, 30);
44
+ const batches = getBatches(publicKeys, 50);
45
45
 
46
46
  const data = (
47
47
  await Promise.all(
@@ -2,5 +2,4 @@ export interface MarginfiAssetAccounts {
2
2
  bank: string;
3
3
  liquidityVault: string;
4
4
  vaultAuthority: string;
5
- priceOracle: string;
6
5
  }
@@ -184,3 +184,15 @@ export async function customRpcCall(umi: Umi, method: string, params?: any) {
184
184
  return data;
185
185
  }
186
186
  }
187
+
188
+ export function u16ToArrayBufferLE(value: number): Uint8Array {
189
+ // Create a buffer of 2 bytes
190
+ const buffer = new ArrayBuffer(2);
191
+ const dataView = new DataView(buffer);
192
+
193
+ // Set the Uint16 value in little-endian order
194
+ dataView.setUint16(0, value, true);
195
+
196
+ // Return the buffer
197
+ return new Uint8Array(buffer);
198
+ }
@@ -8,4 +8,5 @@ export * from "./solautoUtils";
8
8
  export * from "./solanaUtils";
9
9
  export * from "./stringUtils";
10
10
  export * from "./priceUtils";
11
+ export * from "./pythUtils";
11
12
  export * from "./switchboardUtils";
@@ -1,25 +1,30 @@
1
1
  import { PublicKey } from "@solana/web3.js";
2
2
  import { Program, publicKey, Umi } from "@metaplex-foundation/umi";
3
- import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
3
+ import {
4
+ fromWeb3JsPublicKey,
5
+ toWeb3JsPublicKey,
6
+ } from "@metaplex-foundation/umi-web3js-adapters";
4
7
  import { ProgramEnv, MarginfiAssetAccounts } from "../types";
5
8
  import { PositionState, PositionTokenState } from "../generated";
6
9
  import {
7
- DEFAULT_MARGINFI_GROUP,
8
- MARGINFI_ACCOUNTS,
9
10
  ALL_SUPPORTED_TOKENS,
10
- MARGINFI_PROD_PROGRAM,
11
- MARGINFI_STAGING_PROGRAM,
11
+ getMarginfiAccounts,
12
+ MARGINFI_SPONSORED_SHARD_ID,
13
+ MarginfiBankAccountsMap,
14
+ PYTH_SPONSORED_SHARD_ID,
12
15
  TOKEN_INFO,
13
16
  USD_DECIMALS,
14
17
  } from "../constants";
15
18
  import {
16
19
  Bank,
17
20
  deserializeMarginfiAccount,
21
+ fetchBank,
18
22
  getMarginfiAccountSize,
19
23
  getMarginfiErrorFromCode,
20
24
  getMarginfiErrorFromName,
21
25
  MarginfiAccount,
22
26
  OracleSetup,
27
+ safeFetchAllBank,
23
28
  safeFetchBank,
24
29
  safeFetchMarginfiAccount,
25
30
  } from "../marginfi-sdk";
@@ -34,22 +39,16 @@ import {
34
39
  toBaseUnit,
35
40
  toBps,
36
41
  } from "./numberUtils";
37
-
38
- export function getMarginfiProgram(env: ProgramEnv) {
39
- return env === "Prod" ? MARGINFI_PROD_PROGRAM : MARGINFI_STAGING_PROGRAM;
40
- }
41
-
42
- export function isMarginfiProgram(programId: PublicKey) {
43
- return (
44
- programId.equals(MARGINFI_PROD_PROGRAM) ||
45
- programId.equals(MARGINFI_STAGING_PROGRAM)
46
- );
47
- }
42
+ import { getTokenAccountData } from "./accountUtils";
43
+ import {
44
+ getMostUpToDatePythOracle,
45
+ getPythPushOracleAddress,
46
+ } from "./pythUtils";
48
47
 
49
48
  export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
50
49
  return {
51
50
  name: "marginfi",
52
- publicKey: publicKey(getMarginfiProgram(env ?? "Prod")),
51
+ publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program),
53
52
  getErrorFromCode(code: number, cause?: Error) {
54
53
  return getMarginfiErrorFromCode(code, this, cause);
55
54
  },
@@ -73,6 +72,81 @@ export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) {
73
72
  });
74
73
  }
75
74
 
75
+ export async function getAllBankRelatedAccounts(
76
+ umi: Umi,
77
+ bankAccountsMap: MarginfiBankAccountsMap
78
+ ): Promise<PublicKey[]> {
79
+ const banks = Object.values(bankAccountsMap).flatMap((group) =>
80
+ Object.values(group).map((accounts) => accounts.bank)
81
+ );
82
+ const banksData = await safeFetchAllBank(
83
+ umi,
84
+ banks.map((x) => publicKey(x))
85
+ );
86
+
87
+ const oracles = banksData
88
+ .map((bank) => {
89
+ const oracleKey = toWeb3JsPublicKey(bank.config.oracleKeys[0]);
90
+ return bank.config.oracleSetup === OracleSetup.PythPushOracle
91
+ ? [
92
+ getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
93
+ getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
94
+ ]
95
+ : [oracleKey];
96
+
97
+ })
98
+ .flat()
99
+ .map((x) => x.toString());
100
+
101
+ const otherAccounts = Object.entries(bankAccountsMap).flatMap(
102
+ ([groupName, tokenMap]) =>
103
+ Object.values(tokenMap).flatMap((accounts) => [
104
+ groupName,
105
+ accounts.liquidityVault,
106
+ accounts.vaultAuthority,
107
+ ])
108
+ );
109
+
110
+ return Array.from(new Set([...banks, ...oracles, ...otherAccounts]))
111
+ .filter((x) => x !== PublicKey.default.toString())
112
+ .map((x) => new PublicKey(x));
113
+ }
114
+
115
+ export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) {
116
+ const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk));
117
+ const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault);
118
+ const vaultAuthority = (await getTokenAccountData(umi, liquidityVault))
119
+ ?.owner;
120
+ const priceOracle = await getMarginfiPriceOracle(umi, { data: bank });
121
+
122
+ return {
123
+ bank: bankPk,
124
+ liquidityVault,
125
+ vaultAuthority,
126
+ priceOracle,
127
+ };
128
+ }
129
+
130
+ export async function getMarginfiPriceOracle(
131
+ umi: Umi,
132
+ bank: { pk?: PublicKey; data?: Bank }
133
+ ) {
134
+ if (!bank.data) {
135
+ bank.data = await fetchBank(umi, fromWeb3JsPublicKey(bank.pk!));
136
+ }
137
+
138
+ const oracleKey = toWeb3JsPublicKey(bank.data.config.oracleKeys[0]);
139
+ const priceOracle =
140
+ bank.data.config.oracleSetup === OracleSetup.PythPushOracle
141
+ ? await getMostUpToDatePythOracle(umi, [
142
+ getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
143
+ getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
144
+ ])
145
+ : oracleKey;
146
+
147
+ return priceOracle;
148
+ }
149
+
76
150
  interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
77
151
  mint: PublicKey;
78
152
  }
@@ -80,16 +154,29 @@ interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
80
154
  export function findMarginfiAccounts(
81
155
  bank: PublicKey
82
156
  ): AllMarginfiAssetAccounts {
83
- for (const group in MARGINFI_ACCOUNTS) {
84
- for (const key in MARGINFI_ACCOUNTS[group]) {
85
- const account = MARGINFI_ACCOUNTS[group][key];
86
- if (
87
- account.bank.toString().toLowerCase() === bank.toString().toLowerCase()
88
- ) {
89
- return { ...account, mint: new PublicKey(key) };
157
+ const search = (bankAccounts: MarginfiBankAccountsMap) => {
158
+ for (const group in bankAccounts) {
159
+ for (const key in bankAccounts[group]) {
160
+ const account = bankAccounts[group][key];
161
+ if (
162
+ account.bank.toString().toLowerCase() ===
163
+ bank.toString().toLowerCase()
164
+ ) {
165
+ return { ...account, mint: new PublicKey(key) };
166
+ }
90
167
  }
91
168
  }
169
+ };
170
+
171
+ let res = search(getMarginfiAccounts("Prod").bankAccounts);
172
+ if (res) {
173
+ return res;
174
+ }
175
+ res = search(getMarginfiAccounts("Staging").bankAccounts);
176
+ if (res) {
177
+ return res;
92
178
  }
179
+
93
180
  throw new Error(`Marginfi accounts not found by the bank: ${bank}`);
94
181
  }
95
182
 
@@ -145,11 +232,16 @@ export async function getMarginfiMaxLtvAndLiqThresholdBps(
145
232
  return [0, 0];
146
233
  }
147
234
 
235
+ const bankAccounts = getMarginfiAccounts(
236
+ undefined,
237
+ marginfiGroup
238
+ ).bankAccounts;
239
+
148
240
  if (!supply.bank || supply.bank === null) {
149
241
  supply.bank = await safeFetchBank(
150
242
  umi,
151
243
  publicKey(
152
- MARGINFI_ACCOUNTS[marginfiGroup.toString()][supply.mint.toString()].bank
244
+ bankAccounts[marginfiGroup.toString()][supply.mint.toString()].bank
153
245
  ),
154
246
  { commitment: "confirmed" }
155
247
  );
@@ -162,7 +254,7 @@ export async function getMarginfiMaxLtvAndLiqThresholdBps(
162
254
  debt.bank = await safeFetchBank(
163
255
  umi,
164
256
  publicKey(
165
- MARGINFI_ACCOUNTS[marginfiGroup.toString()][debt.mint.toString()].bank
257
+ bankAccounts[marginfiGroup.toString()][debt.mint.toString()].bank
166
258
  ),
167
259
  { commitment: "confirmed" }
168
260
  );
@@ -381,9 +473,9 @@ async function getBank(
381
473
  ? await safeFetchBank(
382
474
  umi,
383
475
  publicKey(
384
- MARGINFI_ACCOUNTS[marginfiGroup?.toString() ?? ""][
385
- data?.mint.toString()
386
- ].bank
476
+ getMarginfiAccounts(undefined, marginfiGroup).bankAccounts[
477
+ marginfiGroup?.toString() ?? ""
478
+ ][data?.mint.toString()].bank
387
479
  ),
388
480
  { commitment: "confirmed" }
389
481
  )
@@ -396,6 +488,7 @@ export async function getMarginfiAccountPositionState(
396
488
  marginfiGroup?: PublicKey,
397
489
  supply?: BankSelection,
398
490
  debt?: BankSelection,
491
+ programEnv?: ProgramEnv,
399
492
  contextUpdates?: ContextUpdates
400
493
  ): Promise<
401
494
  | { supplyBank: Bank | null; debtBank: Bank | null; state: PositionState }
@@ -523,7 +616,7 @@ export async function getMarginfiAccountPositionState(
523
616
  const supplyPrice = safeGetPrice(supply.mint!)!;
524
617
  let [maxLtvBps, liqThresholdBps] = await getMarginfiMaxLtvAndLiqThresholdBps(
525
618
  umi,
526
- marginfiGroup ?? new PublicKey(DEFAULT_MARGINFI_GROUP),
619
+ marginfiGroup ?? getMarginfiAccounts(programEnv).defaultGroup,
527
620
  {
528
621
  mint: toWeb3JsPublicKey(supplyBank.mint),
529
622
  bank: supplyBank,
@@ -0,0 +1,84 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import { PYTH_PUSH_PROGRAM } from "../constants";
3
+ import { u16ToArrayBufferLE, zip } from "./generalUtils";
4
+ import * as borsh from "borsh";
5
+ import { Umi } from "@metaplex-foundation/umi";
6
+ import { fromWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
7
+
8
+ type PriceUpdateV2 = {
9
+ writeAuthority: Buffer;
10
+ verificationLevel: number;
11
+ priceMessage: {
12
+ feedId: Buffer;
13
+ price: bigint;
14
+ conf: bigint;
15
+ exponent: number;
16
+ publishTime: bigint;
17
+ prevPublishTime: bigint;
18
+ emaPrice: bigint;
19
+ emaConf: bigint;
20
+ };
21
+ };
22
+
23
+ const priceUpdateV2Schema = {
24
+ struct: {
25
+ writeAuthority: {
26
+ array: { type: "u8", len: 32 },
27
+ },
28
+ verificationLevel: "u8",
29
+ priceMessage: {
30
+ struct: {
31
+ feedId: { array: { type: "u8", len: 32 } },
32
+ price: "i64",
33
+ conf: "u64",
34
+ exponent: "i32",
35
+ publishTime: "i64",
36
+ prevPublishTime: "i64",
37
+ emaPrice: "i64",
38
+ emaConf: "u64",
39
+ },
40
+ },
41
+ postedSlot: "u64",
42
+ },
43
+ };
44
+
45
+ export function parsePriceInfo(data: Uint8Array): PriceUpdateV2 {
46
+ let decoded: PriceUpdateV2 = borsh.deserialize(
47
+ priceUpdateV2Schema,
48
+ data
49
+ ) as any;
50
+ return decoded;
51
+ }
52
+
53
+ export async function getMostUpToDatePythOracle(
54
+ umi: Umi,
55
+ oracleKeys: PublicKey[]
56
+ ) {
57
+ const oracles = zip(
58
+ oracleKeys,
59
+ (
60
+ await umi.rpc.getAccounts(
61
+ oracleKeys.map((x) => fromWeb3JsPublicKey(x)),
62
+ { commitment: "confirmed" }
63
+ )
64
+ ).map((x) => (x.exists ? parsePriceInfo(x!.data.slice(8)) : undefined))
65
+ ).sort(
66
+ (a, b) =>
67
+ Number(b[1]?.priceMessage.publishTime ?? 0) -
68
+ Number(a[1]?.priceMessage.publishTime ?? 0)
69
+ );
70
+
71
+ return oracles[0][0];
72
+ }
73
+
74
+ export function getPythPushOracleAddress(
75
+ feedId: PublicKey,
76
+ shardId: number,
77
+ programId: PublicKey = PYTH_PUSH_PROGRAM
78
+ ): PublicKey {
79
+ const shardBytes = u16ToArrayBufferLE(shardId);
80
+ return PublicKey.findProgramAddressSync(
81
+ [shardBytes, feedId.toBuffer()],
82
+ programId
83
+ )[0];
84
+ }
@@ -453,7 +453,7 @@ export function getClient(
453
453
  export function isMarginfiClient(
454
454
  client: SolautoClient
455
455
  ): client is SolautoMarginfiClient {
456
- return client.lendingPlatform == LendingPlatform.Marginfi;
456
+ return client.lendingPlatform === LendingPlatform.Marginfi;
457
457
  }
458
458
  // TODO: PF
459
459
 
@@ -6,21 +6,17 @@ import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
6
6
  import {
7
7
  ALL_SUPPORTED_TOKENS,
8
8
  TOKEN_INFO,
9
- MARGINFI_ACCOUNTS,
10
9
  SOLAUTO_FEES_WALLET,
11
10
  SOLAUTO_MANAGER,
12
11
  LOCAL_IRONFORGE_API_URL,
13
- } from "../../src/constants";
14
- import {
12
+ getMarginfiAccounts,
15
13
  getSolanaRpcConnection,
16
14
  getEmptyMarginfiAccountsByAuthority,
17
15
  getTokenAccount,
18
- } from "../../src/utils";
16
+ } from "../../src";
19
17
 
20
18
  async function hasTokenAccounts(wallet: PublicKey) {
21
- let [_, umi] = getSolanaRpcConnection(
22
- LOCAL_IRONFORGE_API_URL
23
- );
19
+ let [_, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL);
24
20
 
25
21
  const tokenAccounts = await umi.rpc.getAccounts(
26
22
  ALL_SUPPORTED_TOKENS.map((x) =>
@@ -45,17 +41,15 @@ describe("Assert Solauto fee token accounts are created", async () => {
45
41
  });
46
42
 
47
43
  it("ISM accounts for every supported Marginfi group", async () => {
48
- let [_, umi] = getSolanaRpcConnection(
49
- LOCAL_IRONFORGE_API_URL
50
- );
44
+ let [_, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL);
51
45
 
52
46
  const ismAccounts = await getEmptyMarginfiAccountsByAuthority(
53
47
  umi,
54
48
  SOLAUTO_MANAGER
55
49
  );
56
- const supportedMarginfiGroups = Object.keys(MARGINFI_ACCOUNTS).map(
57
- (x) => new PublicKey(x)
58
- );
50
+ const supportedMarginfiGroups = Object.keys(
51
+ getMarginfiAccounts("Prod").bankAccounts
52
+ ).map((x) => new PublicKey(x));
59
53
  const missingIsmAccounts = supportedMarginfiGroups.filter(
60
54
  (group) =>
61
55
  !ismAccounts.find((x) => group.equals(toWeb3JsPublicKey(x.group)))
@@ -1,64 +1,43 @@
1
1
  import { describe, it } from "mocha";
2
- import { PublicKey } from "@solana/web3.js";
3
2
  import {
3
+ getMarginfiAccounts,
4
4
  LOCAL_IRONFORGE_API_URL,
5
- MARGINFI_ACCOUNTS,
6
- MARGINFI_ACCOUNTS_LOOKUP_TABLE,
7
5
  SOLAUTO_MANAGER,
8
- } from "../../src/constants";
9
- import {
6
+ getAllBankRelatedAccounts,
10
7
  getEmptyMarginfiAccountsByAuthority,
11
8
  getSolanaRpcConnection,
12
- } from "../../src/utils";
9
+ ProgramEnv,
10
+ } from "../../src";
13
11
 
14
- const [conn, umi] = getSolanaRpcConnection(
15
- LOCAL_IRONFORGE_API_URL
16
- );
12
+ const [conn, umi] = getSolanaRpcConnection(LOCAL_IRONFORGE_API_URL);
17
13
 
18
- describe("Assert lookup tables up-to-date", async () => {
19
- it("marginfi accounts LUT should have everything", async () => {
20
- const lookupTable = await conn.getAddressLookupTable(
21
- new PublicKey(MARGINFI_ACCOUNTS_LOOKUP_TABLE)
22
- );
23
- if (lookupTable === null) {
24
- throw new Error("Lookup table not found");
25
- }
14
+ async function checkLookupTableAccounts(programEnv: ProgramEnv) {
15
+ const data = getMarginfiAccounts(programEnv);
16
+ const lookupTable = await conn.getAddressLookupTable(data.lookupTable);
17
+ if (lookupTable === null) {
18
+ throw new Error("Lookup table not found");
19
+ }
26
20
 
27
- const ismAccounts = await getEmptyMarginfiAccountsByAuthority(
28
- umi,
29
- SOLAUTO_MANAGER
30
- );
21
+ const ismAccounts = (
22
+ await getEmptyMarginfiAccountsByAuthority(umi, SOLAUTO_MANAGER)
23
+ ).map((x) => x.publicKey.toString());
31
24
 
32
- const existingAccounts =
33
- lookupTable.value?.state.addresses.map((x) => x.toString()) ?? [];
25
+ const bankAccounts = (
26
+ await getAllBankRelatedAccounts(umi, data.bankAccounts)
27
+ ).map((x) => x.toString());
34
28
 
35
- for (const group in MARGINFI_ACCOUNTS) {
36
- for (const key in MARGINFI_ACCOUNTS[group]) {
37
- if (key === PublicKey.default.toString()) {
38
- continue;
39
- }
29
+ const accountsRequired = [...ismAccounts, ...bankAccounts];
40
30
 
41
- const groupIsmAccounts = ismAccounts
42
- .filter((x) => x.group.toString() === group)
43
- .map((x) => x.publicKey.toString());
44
- if (groupIsmAccounts.length === 0) {
45
- throw new Error(`Missing ISM account for group: ${group}`);
46
- }
31
+ const existingAccounts =
32
+ lookupTable.value?.state.addresses.map((x) => x.toString()) ?? [];
47
33
 
48
- const accounts = MARGINFI_ACCOUNTS[group][key];
49
- const addresses = [
50
- group,
51
- accounts.bank,
52
- accounts.liquidityVault,
53
- accounts.vaultAuthority,
54
- accounts.priceOracle,
55
- ...groupIsmAccounts,
56
- ];
34
+ if (accountsRequired.find((x) => !existingAccounts.includes(x.toString()))) {
35
+ throw new Error("Marginfi accounts lookup table missing an account");
36
+ }
37
+ }
57
38
 
58
- if (addresses.find((x) => !existingAccounts.includes(x.toString()))) {
59
- throw new Error("Marginfi accounts lookup table missing an account");
60
- }
61
- }
62
- }
39
+ describe("Assert lookup tables up-to-date", async () => {
40
+ it("marginfi accounts LUT should have everything", async () => {
41
+ await checkLookupTableAccounts("Prod");
63
42
  });
64
43
  });