@haven-fi/solauto-sdk 1.0.668 → 1.0.669

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 (35) hide show
  1. package/dist/services/rebalance/rebalanceTxBuilder.js +1 -1
  2. package/dist/solautoPosition/positionUtils.d.ts.map +1 -1
  3. package/dist/solautoPosition/solautoPositionEx.d.ts +3 -3
  4. package/dist/solautoPosition/solautoPositionEx.d.ts.map +1 -1
  5. package/dist/solautoPosition/solautoPositionEx.js +14 -8
  6. package/dist/utils/index.d.ts +1 -1
  7. package/dist/utils/index.d.ts.map +1 -1
  8. package/dist/utils/index.js +1 -1
  9. package/dist/utils/marginfi/data.d.ts +41 -0
  10. package/dist/utils/marginfi/data.d.ts.map +1 -0
  11. package/dist/utils/{marginfiUtils.js → marginfi/data.js} +8 -145
  12. package/dist/utils/marginfi/general.d.ts +27 -0
  13. package/dist/utils/marginfi/general.d.ts.map +1 -0
  14. package/dist/utils/marginfi/general.js +146 -0
  15. package/dist/utils/marginfi/index.d.ts +3 -0
  16. package/dist/utils/marginfi/index.d.ts.map +1 -0
  17. package/dist/utils/marginfi/index.js +18 -0
  18. package/dist/utils/solanaUtils.js +3 -3
  19. package/dist/utils/solautoUtils.d.ts.map +1 -1
  20. package/dist/utils/solautoUtils.js +4 -4
  21. package/local/txSandbox.ts +3 -3
  22. package/package.json +1 -1
  23. package/src/services/rebalance/rebalanceTxBuilder.ts +1 -1
  24. package/src/solautoPosition/positionUtils.ts +0 -1
  25. package/src/solautoPosition/solautoPositionEx.ts +24 -9
  26. package/src/utils/index.ts +1 -1
  27. package/src/utils/{marginfiUtils.ts → marginfi/data.ts} +10 -220
  28. package/src/utils/marginfi/general.ts +226 -0
  29. package/src/utils/marginfi/index.ts +2 -0
  30. package/src/utils/solanaUtils.ts +2 -2
  31. package/src/utils/solautoUtils.ts +1 -4
  32. package/tests/transactions/shared.ts +3 -1
  33. package/tests/transactions/solautoMarginfi.ts +3 -1
  34. package/dist/utils/marginfiUtils.d.ts +0 -62
  35. package/dist/utils/marginfiUtils.d.ts.map +0 -1
@@ -23,8 +23,8 @@ const solautoPosition_1 = require("../solautoPosition");
23
23
  const accountUtils_1 = require("./accountUtils");
24
24
  const numberUtils_1 = require("./numberUtils");
25
25
  const priceUtils_1 = require("./priceUtils");
26
- const marginfiUtils_1 = require("./marginfiUtils");
27
26
  const generalUtils_1 = require("./generalUtils");
27
+ const marginfi_1 = require("./marginfi");
28
28
  function createDynamicSolautoProgram(programId) {
29
29
  return {
30
30
  name: "solauto",
@@ -120,8 +120,8 @@ async function getSolautoManagedPositions(umi, authority, positionTypeFilter) {
120
120
  let tokens;
121
121
  if (position.position.lendingPlatform === generated_1.LendingPlatform.Marginfi) {
122
122
  tokens = [
123
- (0, marginfiUtils_1.findMarginfiAccounts)((0, umi_web3js_adapters_1.toWeb3JsPublicKey)(position.position.lpSupplyAccount)).mint,
124
- (0, marginfiUtils_1.findMarginfiAccounts)((0, umi_web3js_adapters_1.toWeb3JsPublicKey)(position.position.lpDebtAccount))
123
+ (0, marginfi_1.findMarginfiAccounts)((0, umi_web3js_adapters_1.toWeb3JsPublicKey)(position.position.lpSupplyAccount)).mint,
124
+ (0, marginfi_1.findMarginfiAccounts)((0, umi_web3js_adapters_1.toWeb3JsPublicKey)(position.position.lpDebtAccount))
125
125
  .mint,
126
126
  ];
127
127
  }
@@ -193,7 +193,7 @@ async function getAllPositionsByAuthority(umi, user, positionTypeFilter) {
193
193
  if (positionTypeFilter === generated_1.PositionType.SafeLoan) {
194
194
  return [];
195
195
  }
196
- let marginfiPositions = await (0, marginfiUtils_1.getAllMarginfiAccountsByAuthority)(umi, user, undefined, true);
196
+ let marginfiPositions = await (0, marginfi_1.getAllMarginfiAccountsByAuthority)(umi, user, undefined, true);
197
197
  marginfiPositions = marginfiPositions.filter((x) => (0, generalUtils_1.validPubkey)(x.supplyMint) && (0, generalUtils_1.validPubkey)(x.debtMint));
198
198
  return marginfiPositions.map((x) => ({
199
199
  publicKey: x.marginfiAccount,
@@ -17,7 +17,7 @@ import {
17
17
  import { getSecretKey } from "./shared";
18
18
 
19
19
  const payForTransaction = false;
20
- const testProgram = true;
20
+ const testProgram = false;
21
21
  const lpEnv: ProgramEnv = "Prod";
22
22
 
23
23
  export async function main() {
@@ -29,7 +29,7 @@ export async function main() {
29
29
 
30
30
  const signer = createSignerFromKeypair(
31
31
  umi,
32
- fromWeb3JsKeypair(Keypair.fromSecretKey(getSecretKey("solauto-manager")))
32
+ fromWeb3JsKeypair(Keypair.fromSecretKey(getSecretKey()))
33
33
  );
34
34
 
35
35
  const client = getClient(LendingPlatform.Marginfi, {
@@ -54,7 +54,7 @@ export async function main() {
54
54
  client,
55
55
  undefined,
56
56
  payForTransaction ? "normal" : "only-simulate",
57
- PriorityFeeSetting.Min,
57
+ PriorityFeeSetting.High,
58
58
  true,
59
59
  undefined,
60
60
  { totalRetries: 5 }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haven-fi/solauto-sdk",
3
- "version": "1.0.668",
3
+ "version": "1.0.669",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "description": "Typescript SDK for the Solauto program on the Solana blockchain",
@@ -210,7 +210,7 @@ export class RebalanceTxBuilder {
210
210
  }
211
211
 
212
212
  if (
213
- !this.client.pos.rebalanceHelper.validRealtimePricesBoost(
213
+ !this.client.pos.rebalance.validRealtimePricesBoost(
214
214
  rebalanceValues.debtAdjustmentUsd
215
215
  )
216
216
  ) {
@@ -29,7 +29,6 @@ import {
29
29
  SolautoPositionEx,
30
30
  } from "./solautoPositionEx";
31
31
  import { MarginfiSolautoPositionEx } from "./marginfiSolautoPositionEx";
32
- import { assert } from "console";
33
32
 
34
33
  export function createSolautoSettings(
35
34
  settings: SolautoSettingsParametersInpArgs
@@ -91,7 +91,7 @@ export abstract class SolautoPositionEx {
91
91
  private _supplyPrice?: number;
92
92
  private _debtPrice?: number;
93
93
 
94
- public rebalanceHelper!: PositionRebalanceHelper;
94
+ public rebalance!: PositionRebalanceHelper;
95
95
 
96
96
  constructor(args: PositionExArgs) {
97
97
  this.umi = args.umi;
@@ -120,7 +120,7 @@ export abstract class SolautoPositionEx {
120
120
  this._data = args.data;
121
121
  this.firstState = { ...args.data.state };
122
122
 
123
- this.rebalanceHelper = new PositionRebalanceHelper(this);
123
+ this.rebalance = new PositionRebalanceHelper(this);
124
124
  }
125
125
 
126
126
  abstract lendingPool(): Promise<PublicKey>;
@@ -288,9 +288,13 @@ export abstract class SolautoPositionEx {
288
288
  }
289
289
 
290
290
  eligibleForRebalance(
291
- bpsDistanceThreshold: number = 0
291
+ bpsDistanceThreshold: number = 0,
292
+ skipExtraChecks?: boolean
292
293
  ): RebalanceAction | undefined {
293
- return this.rebalanceHelper.eligibleForRebalance(bpsDistanceThreshold);
294
+ return this.rebalance.eligibleForRebalance(
295
+ bpsDistanceThreshold,
296
+ skipExtraChecks
297
+ );
294
298
  }
295
299
 
296
300
  eligibleForRefresh(): boolean {
@@ -457,11 +461,21 @@ class PositionRebalanceHelper {
457
461
  }
458
462
 
459
463
  private validBoostFromHere() {
464
+ const realtimeSupplyUsd = this.pos.supplyUsd(PriceType.Realtime);
465
+ const realtimeDebtUsd = this.pos.debtUsd(PriceType.Realtime);
466
+
467
+ if (
468
+ realtimeSupplyUsd === this.pos.supplyUsd(PriceType.Ema) &&
469
+ realtimeDebtUsd === this.pos.debtUsd(PriceType.Ema)
470
+ ) {
471
+ return true;
472
+ }
473
+
460
474
  const { debtAdjustmentUsd } = getDebtAdjustment(
461
475
  this.pos.state.liqThresholdBps,
462
476
  {
463
- supplyUsd: this.pos.supplyUsd(PriceType.Realtime),
464
- debtUsd: this.pos.debtUsd(PriceType.Realtime),
477
+ supplyUsd: realtimeSupplyUsd,
478
+ debtUsd: realtimeDebtUsd,
465
479
  },
466
480
  this.pos.boostToBps,
467
481
  { solauto: 25, lpBorrow: 0, flashLoan: 0 } // Undershoot fees
@@ -471,7 +485,8 @@ class PositionRebalanceHelper {
471
485
  }
472
486
 
473
487
  eligibleForRebalance(
474
- bpsDistanceThreshold: number
488
+ bpsDistanceThreshold: number,
489
+ skipExtraChecks?: boolean
475
490
  ): RebalanceAction | undefined {
476
491
  if (!this.pos.settings || !this.pos.supplyUsd()) {
477
492
  return undefined;
@@ -488,8 +503,8 @@ class PositionRebalanceHelper {
488
503
  return "repay";
489
504
  } else if (
490
505
  realtimeLiqUtilRateBps - this.pos.boostFromBps <= bpsDistanceThreshold &&
491
- this.validBoostFromHere() &&
492
- this.sufficientLiquidityToBoost()
506
+ (!skipExtraChecks ||
507
+ (this.validBoostFromHere() && this.sufficientLiquidityToBoost()))
493
508
  ) {
494
509
  return "boost";
495
510
  }
@@ -3,7 +3,7 @@ export * from "./generalUtils";
3
3
  export * from "./instructionUtils";
4
4
  export * from "./jitoUtils";
5
5
  export * from "./jupiterUtils";
6
- export * from "./marginfiUtils";
6
+ export * from "./marginfi";
7
7
  export * from "./numberUtils";
8
8
  export * from "./solautoUtils";
9
9
  export * from "./solanaUtils";
@@ -1,37 +1,28 @@
1
1
  import { PublicKey } from "@solana/web3.js";
2
- import { AccountMeta, Program, publicKey, Umi } from "@metaplex-foundation/umi";
2
+ import { publicKey, Umi } from "@metaplex-foundation/umi";
3
3
  import {
4
- fromWeb3JsPublicKey,
5
4
  toWeb3JsPublicKey,
6
5
  } from "@metaplex-foundation/umi-web3js-adapters";
7
- import { ProgramEnv, MarginfiAssetAccounts } from "../types";
8
- import { PositionState, PositionTokenState, PriceType } from "../generated";
6
+ import { ProgramEnv } from "../../types";
7
+ import { PositionState, PositionTokenState, PriceType } from "../../generated";
9
8
  import {
10
9
  ALL_SUPPORTED_TOKENS,
11
10
  getMarginfiAccounts,
12
- MARGINFI_SPONSORED_SHARD_ID,
13
- MarginfiBankAccountsMap,
14
- PYTH_SPONSORED_SHARD_ID,
15
11
  TOKEN_INFO,
16
12
  USD_DECIMALS,
17
- } from "../constants";
13
+ } from "../../constants";
18
14
  import {
19
- Balance,
20
15
  Bank,
21
16
  deserializeMarginfiAccount,
22
- fetchBank,
23
17
  getMarginfiAccountSize,
24
- getMarginfiErrorFromCode,
25
- getMarginfiErrorFromName,
26
18
  MarginfiAccount,
27
19
  OracleSetup,
28
- safeFetchAllBank,
29
20
  safeFetchBank,
30
21
  safeFetchMarginfiAccount,
31
- } from "../marginfi-sdk";
32
- import { ContextUpdates } from "./solautoUtils";
33
- import { fetchTokenPrices, safeGetPrice } from "./priceUtils";
34
- import { currentUnixSeconds, validPubkey } from "./generalUtils";
22
+ } from "../../marginfi-sdk";
23
+ import { ContextUpdates } from "../solautoUtils";
24
+ import { fetchTokenPrices, safeGetPrice } from "../priceUtils";
25
+ import { currentUnixSeconds, validPubkey } from "../generalUtils";
35
26
  import {
36
27
  bytesToI80F48,
37
28
  calcNetWorthUsd,
@@ -39,198 +30,8 @@ import {
39
30
  getLiqUtilzationRateBps,
40
31
  toBaseUnit,
41
32
  toBps,
42
- } from "./numberUtils";
43
- import { getTokenAccountData } from "./accountUtils";
44
- import {
45
- getMostUpToDatePythOracle,
46
- getPythPushOracleAddress,
47
- } from "./pythUtils";
48
- import { getAccountMeta } from "./solanaUtils";
49
-
50
- export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
51
- return {
52
- name: "marginfi",
53
- publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program),
54
- getErrorFromCode(code: number, cause?: Error) {
55
- return getMarginfiErrorFromCode(code, this, cause);
56
- },
57
- getErrorFromName(name: string, cause?: Error) {
58
- return getMarginfiErrorFromName(name, this, cause);
59
- },
60
- isOnCluster() {
61
- return true;
62
- },
63
- };
64
- }
65
-
66
- export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) {
67
- return umi.use({
68
- install(umi) {
69
- umi.programs.add(
70
- createDynamicMarginfiProgram(marginfiEnv ?? "Prod"),
71
- false
72
- );
73
- },
74
- });
75
- }
76
-
77
- export async function getAllBankRelatedAccounts(
78
- umi: Umi,
79
- bankAccountsMap: MarginfiBankAccountsMap
80
- ): Promise<PublicKey[]> {
81
- const banks = Object.values(bankAccountsMap).flatMap((group) =>
82
- Object.values(group).map((accounts) => accounts.bank)
83
- );
84
- const banksData = await safeFetchAllBank(
85
- umi,
86
- banks.map((x) => publicKey(x))
87
- );
88
-
89
- const oracles = banksData
90
- .map((bank) => {
91
- const oracleKey = toWeb3JsPublicKey(bank.config.oracleKeys[0]);
92
- return bank.config.oracleSetup === OracleSetup.PythPushOracle
93
- ? [
94
- getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
95
- getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
96
- ]
97
- : [oracleKey];
98
- })
99
- .flat()
100
- .map((x) => x.toString());
101
-
102
- const otherAccounts = Object.entries(bankAccountsMap).flatMap(
103
- ([groupName, tokenMap]) =>
104
- Object.values(tokenMap).flatMap((accounts) => [
105
- groupName,
106
- accounts.liquidityVault,
107
- accounts.vaultAuthority,
108
- ])
109
- );
110
-
111
- return Array.from(new Set([...banks, ...oracles, ...otherAccounts]))
112
- .filter((x) => x !== PublicKey.default.toString())
113
- .map((x) => new PublicKey(x));
114
- }
115
-
116
- export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) {
117
- const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk));
118
- const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault);
119
- const vaultAuthority = (await getTokenAccountData(umi, liquidityVault))
120
- ?.owner;
121
- const priceOracle = await getMarginfiPriceOracle(umi, { data: bank });
122
-
123
- return {
124
- bank: bankPk,
125
- liquidityVault,
126
- vaultAuthority,
127
- priceOracle,
128
- };
129
- }
130
-
131
- export async function getMarginfiPriceOracle(
132
- umi: Umi,
133
- bank: { pk?: PublicKey; data?: Bank }
134
- ) {
135
- if (!bank.data) {
136
- bank.data = await fetchBank(umi, fromWeb3JsPublicKey(bank.pk!));
137
- }
138
-
139
- const oracleKey = toWeb3JsPublicKey(bank.data.config.oracleKeys[0]);
140
- const priceOracle =
141
- bank.data.config.oracleSetup === OracleSetup.PythPushOracle
142
- ? await getMostUpToDatePythOracle(umi, [
143
- getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
144
- getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
145
- ])
146
- : oracleKey;
147
-
148
- return priceOracle;
149
- }
150
-
151
- interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
152
- mint: PublicKey;
153
- }
154
-
155
- export function findMarginfiAccounts(
156
- bank: PublicKey
157
- ): AllMarginfiAssetAccounts {
158
- const search = (bankAccounts: MarginfiBankAccountsMap) => {
159
- for (const group in bankAccounts) {
160
- for (const key in bankAccounts[group]) {
161
- const account = bankAccounts[group][key];
162
- if (
163
- account.bank.toString().toLowerCase() ===
164
- bank.toString().toLowerCase()
165
- ) {
166
- return { ...account, mint: new PublicKey(key) };
167
- }
168
- }
169
- }
170
- };
171
-
172
- let res = search(getMarginfiAccounts("Prod").bankAccounts);
173
- if (res) {
174
- return res;
175
- }
176
- res = search(getMarginfiAccounts("Staging").bankAccounts);
177
- if (res) {
178
- return res;
179
- }
180
-
181
- throw new Error(`Marginfi accounts not found by the bank: ${bank}`);
182
- }
183
-
184
- export async function getRemainingAccountsForMarginfiHealthCheck(
185
- umi: Umi,
186
- balance: Balance
187
- ): Promise<AccountMeta[]> {
188
- if (!balance.active) {
189
- return [];
190
- }
191
- const priceOracle = await getMarginfiPriceOracle(umi, {
192
- pk: toWeb3JsPublicKey(balance.bankPk),
193
- });
194
- return [
195
- getAccountMeta(toWeb3JsPublicKey(balance.bankPk)),
196
- getAccountMeta(priceOracle),
197
- ];
198
- }
199
-
200
- export function calcMarginfiMaxLtvAndLiqThresholdBps(
201
- supplyBank: Bank,
202
- debtBank: Bank,
203
- supplyPrice: number
204
- ): [number, number] {
205
- let maxLtv =
206
- bytesToI80F48(supplyBank.config.assetWeightInit.value) /
207
- bytesToI80F48(debtBank.config.liabilityWeightInit.value);
208
- const liqThreshold =
209
- bytesToI80F48(supplyBank.config.assetWeightMaint.value) /
210
- bytesToI80F48(debtBank.config.liabilityWeightMaint.value);
211
-
212
- const totalDepositedUsdValue =
213
- fromBaseUnit(
214
- BigInt(
215
- Math.round(
216
- bytesToI80F48(supplyBank.totalAssetShares.value) *
217
- bytesToI80F48(supplyBank.assetShareValue.value)
218
- )
219
- ),
220
- supplyBank.mintDecimals
221
- ) * supplyPrice!;
222
- if (
223
- supplyBank.config.totalAssetValueInitLimit !== BigInt(0) &&
224
- totalDepositedUsdValue > supplyBank.config.totalAssetValueInitLimit
225
- ) {
226
- const discount =
227
- Number(supplyBank.config.totalAssetValueInitLimit) /
228
- totalDepositedUsdValue;
229
- maxLtv = maxLtv * Number(discount);
230
- }
231
-
232
- return [toBps(maxLtv, "Floor"), toBps(liqThreshold, "Floor")];
233
- }
33
+ } from "../numberUtils";
34
+ import { calcMarginfiMaxLtvAndLiqThresholdBps, marginfiAccountEmpty } from "./general";
234
35
 
235
36
  export async function getMarginfiMaxLtvAndLiqThresholdBps(
236
37
  umi: Umi,
@@ -777,14 +578,3 @@ export function getUpToDateShareValues(bank: Bank): [number, number] {
777
578
  ),
778
579
  ];
779
580
  }
780
-
781
- export function marginfiAccountEmpty(marginfiAccount: MarginfiAccount) {
782
- return (
783
- marginfiAccount.lendingAccount.balances.find(
784
- (x) =>
785
- x.bankPk.toString() !== PublicKey.default.toString() &&
786
- (bytesToI80F48(x.assetShares.value) > 0.000001 ||
787
- bytesToI80F48(x.liabilityShares.value) > 0.000001)
788
- ) === undefined
789
- );
790
- }
@@ -0,0 +1,226 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ import { AccountMeta, Program, publicKey, Umi } from "@metaplex-foundation/umi";
3
+ import {
4
+ fromWeb3JsPublicKey,
5
+ toWeb3JsPublicKey,
6
+ } from "@metaplex-foundation/umi-web3js-adapters";
7
+ import { ProgramEnv, MarginfiAssetAccounts } from "../../types";
8
+ import {
9
+ getMarginfiAccounts,
10
+ MARGINFI_SPONSORED_SHARD_ID,
11
+ MarginfiBankAccountsMap,
12
+ PYTH_SPONSORED_SHARD_ID,
13
+ } from "../../constants";
14
+ import {
15
+ Balance,
16
+ Bank,
17
+ fetchBank,
18
+ getMarginfiErrorFromCode,
19
+ getMarginfiErrorFromName,
20
+ MarginfiAccount,
21
+ OracleSetup,
22
+ safeFetchAllBank,
23
+ } from "../../marginfi-sdk";
24
+ import { bytesToI80F48, fromBaseUnit, toBps } from "../numberUtils";
25
+ import { getTokenAccountData } from "../accountUtils";
26
+ import {
27
+ getMostUpToDatePythOracle,
28
+ getPythPushOracleAddress,
29
+ } from "../pythUtils";
30
+ import { getAccountMeta } from "../solanaUtils";
31
+
32
+ export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
33
+ return {
34
+ name: "marginfi",
35
+ publicKey: publicKey(getMarginfiAccounts(env ?? "Prod").program),
36
+ getErrorFromCode(code: number, cause?: Error) {
37
+ return getMarginfiErrorFromCode(code, this, cause);
38
+ },
39
+ getErrorFromName(name: string, cause?: Error) {
40
+ return getMarginfiErrorFromName(name, this, cause);
41
+ },
42
+ isOnCluster() {
43
+ return true;
44
+ },
45
+ };
46
+ }
47
+
48
+ export function umiWithMarginfiProgram(umi: Umi, marginfiEnv?: ProgramEnv) {
49
+ return umi.use({
50
+ install(umi) {
51
+ umi.programs.add(
52
+ createDynamicMarginfiProgram(marginfiEnv ?? "Prod"),
53
+ false
54
+ );
55
+ },
56
+ });
57
+ }
58
+
59
+ export async function getAllBankRelatedAccounts(
60
+ umi: Umi,
61
+ bankAccountsMap: MarginfiBankAccountsMap
62
+ ): Promise<PublicKey[]> {
63
+ const banks = Object.values(bankAccountsMap).flatMap((group) =>
64
+ Object.values(group).map((accounts) => accounts.bank)
65
+ );
66
+ const banksData = await safeFetchAllBank(
67
+ umi,
68
+ banks.map((x) => publicKey(x))
69
+ );
70
+
71
+ const oracles = banksData
72
+ .map((bank) => {
73
+ const oracleKey = toWeb3JsPublicKey(bank.config.oracleKeys[0]);
74
+ return bank.config.oracleSetup === OracleSetup.PythPushOracle
75
+ ? [
76
+ getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
77
+ getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
78
+ ]
79
+ : [oracleKey];
80
+ })
81
+ .flat()
82
+ .map((x) => x.toString());
83
+
84
+ const otherAccounts = Object.entries(bankAccountsMap).flatMap(
85
+ ([groupName, tokenMap]) =>
86
+ Object.values(tokenMap).flatMap((accounts) => [
87
+ groupName,
88
+ accounts.liquidityVault,
89
+ accounts.vaultAuthority,
90
+ ])
91
+ );
92
+
93
+ return Array.from(new Set([...banks, ...oracles, ...otherAccounts]))
94
+ .filter((x) => x !== PublicKey.default.toString())
95
+ .map((x) => new PublicKey(x));
96
+ }
97
+
98
+ export async function fetchBankAddresses(umi: Umi, bankPk: PublicKey) {
99
+ const bank = await fetchBank(umi, fromWeb3JsPublicKey(bankPk));
100
+ const liquidityVault = toWeb3JsPublicKey(bank!.liquidityVault);
101
+ const vaultAuthority = (await getTokenAccountData(umi, liquidityVault))
102
+ ?.owner;
103
+ const priceOracle = await getMarginfiPriceOracle(umi, { data: bank });
104
+
105
+ return {
106
+ bank: bankPk,
107
+ liquidityVault,
108
+ vaultAuthority,
109
+ priceOracle,
110
+ };
111
+ }
112
+
113
+ export async function getMarginfiPriceOracle(
114
+ umi: Umi,
115
+ bank: { pk?: PublicKey; data?: Bank }
116
+ ) {
117
+ if (!bank.data) {
118
+ bank.data = await fetchBank(umi, fromWeb3JsPublicKey(bank.pk!));
119
+ }
120
+
121
+ const oracleKey = toWeb3JsPublicKey(bank.data.config.oracleKeys[0]);
122
+ const priceOracle =
123
+ bank.data.config.oracleSetup === OracleSetup.PythPushOracle
124
+ ? await getMostUpToDatePythOracle(umi, [
125
+ getPythPushOracleAddress(oracleKey, PYTH_SPONSORED_SHARD_ID),
126
+ getPythPushOracleAddress(oracleKey, MARGINFI_SPONSORED_SHARD_ID),
127
+ ])
128
+ : oracleKey;
129
+
130
+ return priceOracle;
131
+ }
132
+
133
+ interface AllMarginfiAssetAccounts extends MarginfiAssetAccounts {
134
+ mint: PublicKey;
135
+ }
136
+
137
+ export function findMarginfiAccounts(
138
+ bank: PublicKey
139
+ ): AllMarginfiAssetAccounts {
140
+ const search = (bankAccounts: MarginfiBankAccountsMap) => {
141
+ for (const group in bankAccounts) {
142
+ for (const key in bankAccounts[group]) {
143
+ const account = bankAccounts[group][key];
144
+ if (
145
+ account.bank.toString().toLowerCase() ===
146
+ bank.toString().toLowerCase()
147
+ ) {
148
+ return { ...account, mint: new PublicKey(key) };
149
+ }
150
+ }
151
+ }
152
+ };
153
+
154
+ let res = search(getMarginfiAccounts("Prod").bankAccounts);
155
+ if (res) {
156
+ return res;
157
+ }
158
+ res = search(getMarginfiAccounts("Staging").bankAccounts);
159
+ if (res) {
160
+ return res;
161
+ }
162
+
163
+ throw new Error(`Marginfi accounts not found by the bank: ${bank}`);
164
+ }
165
+
166
+ export async function getRemainingAccountsForMarginfiHealthCheck(
167
+ umi: Umi,
168
+ balance: Balance
169
+ ): Promise<AccountMeta[]> {
170
+ if (!balance.active) {
171
+ return [];
172
+ }
173
+ const priceOracle = await getMarginfiPriceOracle(umi, {
174
+ pk: toWeb3JsPublicKey(balance.bankPk),
175
+ });
176
+ return [
177
+ getAccountMeta(toWeb3JsPublicKey(balance.bankPk)),
178
+ getAccountMeta(priceOracle),
179
+ ];
180
+ }
181
+
182
+ export function calcMarginfiMaxLtvAndLiqThresholdBps(
183
+ supplyBank: Bank,
184
+ debtBank: Bank,
185
+ supplyPrice: number
186
+ ): [number, number] {
187
+ let maxLtv =
188
+ bytesToI80F48(supplyBank.config.assetWeightInit.value) /
189
+ bytesToI80F48(debtBank.config.liabilityWeightInit.value);
190
+ const liqThreshold =
191
+ bytesToI80F48(supplyBank.config.assetWeightMaint.value) /
192
+ bytesToI80F48(debtBank.config.liabilityWeightMaint.value);
193
+
194
+ const totalDepositedUsdValue =
195
+ fromBaseUnit(
196
+ BigInt(
197
+ Math.round(
198
+ bytesToI80F48(supplyBank.totalAssetShares.value) *
199
+ bytesToI80F48(supplyBank.assetShareValue.value)
200
+ )
201
+ ),
202
+ supplyBank.mintDecimals
203
+ ) * supplyPrice!;
204
+ if (
205
+ supplyBank.config.totalAssetValueInitLimit !== BigInt(0) &&
206
+ totalDepositedUsdValue > supplyBank.config.totalAssetValueInitLimit
207
+ ) {
208
+ const discount =
209
+ Number(supplyBank.config.totalAssetValueInitLimit) /
210
+ totalDepositedUsdValue;
211
+ maxLtv = maxLtv * Number(discount);
212
+ }
213
+
214
+ return [toBps(maxLtv, "Floor"), toBps(liqThreshold, "Floor")];
215
+ }
216
+
217
+ export function marginfiAccountEmpty(marginfiAccount: MarginfiAccount) {
218
+ return (
219
+ marginfiAccount.lendingAccount.balances.find(
220
+ (x) =>
221
+ x.bankPk.toString() !== PublicKey.default.toString() &&
222
+ (bytesToI80F48(x.assetShares.value) > 0.000001 ||
223
+ bytesToI80F48(x.liabilityShares.value) > 0.000001)
224
+ ) === undefined
225
+ );
226
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./data";
2
+ export * from "./general";
@@ -47,7 +47,7 @@ import {
47
47
  retryWithExponentialBackoff,
48
48
  } from "./generalUtils";
49
49
  import { createDynamicSolautoProgram } from "./solautoUtils";
50
- import { createDynamicMarginfiProgram } from "./marginfiUtils";
50
+ import { createDynamicMarginfiProgram } from "./marginfi";
51
51
 
52
52
  export function getSolanaRpcConnection(
53
53
  rpcUrl: string,
@@ -471,7 +471,7 @@ export async function sendSingleOptimizedTransaction(
471
471
  let cuPrice: number | undefined;
472
472
  if (prioritySetting !== PriorityFeeSetting.None) {
473
473
  cuPrice = await getComputeUnitPriceEstimate(umi, tx, prioritySetting);
474
- cuPrice = Math.min(cuPrice ?? 0, 100 * 1_000_000);
474
+ cuPrice = Math.min(cuPrice ?? 0, 100_000_000);
475
475
  consoleLog("Compute unit price: ", cuPrice);
476
476
  }
477
477
 
@@ -37,11 +37,8 @@ import {
37
37
  toRoundedUsdValue,
38
38
  } from "./numberUtils";
39
39
  import { fetchTokenPrices } from "./priceUtils";
40
- import {
41
- findMarginfiAccounts,
42
- getAllMarginfiAccountsByAuthority,
43
- } from "./marginfiUtils";
44
40
  import { validPubkey } from "./generalUtils";
41
+ import { findMarginfiAccounts, getAllMarginfiAccountsByAuthority } from "./marginfi";
45
42
 
46
43
  export function createDynamicSolautoProgram(programId?: PublicKey): Program {
47
44
  return {