@haven-fi/solauto-sdk 1.0.661 → 1.0.663

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 (36) hide show
  1. package/dist/services/flashLoans/flProviderAggregator.d.ts.map +1 -1
  2. package/dist/services/flashLoans/marginfiFlProvider.d.ts +2 -0
  3. package/dist/services/flashLoans/marginfiFlProvider.d.ts.map +1 -1
  4. package/dist/services/flashLoans/marginfiFlProvider.js +14 -31
  5. package/dist/services/rebalance/rebalanceTxBuilder.d.ts +0 -1
  6. package/dist/services/rebalance/rebalanceTxBuilder.d.ts.map +1 -1
  7. package/dist/services/rebalance/rebalanceTxBuilder.js +1 -6
  8. package/dist/services/solauto/solautoClient.d.ts.map +1 -1
  9. package/dist/services/solauto/solautoClient.js +1 -4
  10. package/dist/services/solauto/solautoMarginfiClient.d.ts +2 -1
  11. package/dist/services/solauto/solautoMarginfiClient.d.ts.map +1 -1
  12. package/dist/services/solauto/solautoMarginfiClient.js +20 -7
  13. package/dist/solautoPosition/solautoPositionEx.d.ts +9 -1
  14. package/dist/solautoPosition/solautoPositionEx.d.ts.map +1 -1
  15. package/dist/solautoPosition/solautoPositionEx.js +56 -29
  16. package/dist/utils/marginfiUtils.d.ts +3 -2
  17. package/dist/utils/marginfiUtils.d.ts.map +1 -1
  18. package/dist/utils/marginfiUtils.js +14 -0
  19. package/dist/utils/numberUtils.d.ts +2 -0
  20. package/dist/utils/numberUtils.d.ts.map +1 -1
  21. package/dist/utils/numberUtils.js +7 -0
  22. package/dist/utils/solanaUtils.d.ts +2 -1
  23. package/dist/utils/solanaUtils.d.ts.map +1 -1
  24. package/dist/utils/solanaUtils.js +4 -0
  25. package/local/logPositions.ts +0 -2
  26. package/local/txSandbox.ts +13 -7
  27. package/package.json +1 -1
  28. package/src/services/flashLoans/flProviderAggregator.ts +3 -1
  29. package/src/services/flashLoans/marginfiFlProvider.ts +28 -38
  30. package/src/services/rebalance/rebalanceTxBuilder.ts +6 -19
  31. package/src/services/solauto/solautoClient.ts +1 -4
  32. package/src/services/solauto/solautoMarginfiClient.ts +50 -9
  33. package/src/solautoPosition/solautoPositionEx.ts +104 -46
  34. package/src/utils/marginfiUtils.ts +19 -1
  35. package/src/utils/numberUtils.ts +13 -1
  36. package/src/utils/solanaUtils.ts +10 -0
@@ -2,6 +2,7 @@ import { Keypair, PublicKey } from "@solana/web3.js";
2
2
  import { createSignerFromKeypair } from "@metaplex-foundation/umi";
3
3
  import { fromWeb3JsKeypair } from "@metaplex-foundation/umi-web3js-adapters";
4
4
  import {
5
+ borrow,
5
6
  consoleLog,
6
7
  getClient,
7
8
  getSolanaRpcConnection,
@@ -12,7 +13,10 @@ import {
12
13
  rebalance,
13
14
  SOLAUTO_PROD_PROGRAM,
14
15
  SOLAUTO_TEST_PROGRAM,
16
+ toBaseUnit,
17
+ tokenInfo,
15
18
  TransactionsManager,
19
+ USDC,
16
20
  } from "../src";
17
21
  import { getSecretKey } from "./shared";
18
22
 
@@ -29,7 +33,7 @@ export async function main() {
29
33
 
30
34
  const signer = createSignerFromKeypair(
31
35
  umi,
32
- fromWeb3JsKeypair(Keypair.fromSecretKey(getSecretKey("solauto-manager")))
36
+ fromWeb3JsKeypair(Keypair.fromSecretKey(getSecretKey()))
33
37
  );
34
38
 
35
39
  const client = getClient(LendingPlatform.Marginfi, {
@@ -41,14 +45,16 @@ export async function main() {
41
45
  });
42
46
 
43
47
  await client.initialize({
44
- positionId: 1,
45
- authority: new PublicKey("5UqsR2PGzbP8pGPbXEeXx86Gjz2N2UFBAuFZUSVydAEe"),
46
- // lpUserAccount: new PublicKey(
47
- // "EyATEtRH6FQRCJCn4nVzEnKXnzyGaUV1nt4sHqBrQ2fZ"
48
- // ),
48
+ positionId: 0,
49
+ // authority: new PublicKey("5UqsR2PGzbP8pGPbXEeXx86Gjz2N2UFBAuFZUSVydAEe"),
50
+ lpUserAccount: new PublicKey(
51
+ "GEokw9jqbh6d1xUNA3qaeYFFetbSR5Y1nt7C3chwwgSz"
52
+ ),
49
53
  });
50
54
 
51
- const transactionItems = [rebalance(client)];
55
+ const transactionItems = [
56
+ rebalance(client, 300),
57
+ ];
52
58
 
53
59
  const txManager = new TransactionsManager(
54
60
  client,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haven-fi/solauto-sdk",
3
- "version": "1.0.661",
3
+ "version": "1.0.663",
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",
@@ -76,6 +76,8 @@ export class FlProviderAggregator extends FlProviderBase {
76
76
  }
77
77
 
78
78
  flashRepay(flashLoan: FlashLoanDetails): TransactionBuilder {
79
- return this.flProvider(flashLoan.liquiditySource).flashRepay(flashLoan);
79
+ return this.flProvider(flashLoan.liquiditySource).flashRepay(
80
+ flashLoan
81
+ );
80
82
  }
81
83
  }
@@ -30,6 +30,7 @@ import {
30
30
  getBankLiquidityAvailableBaseUnit,
31
31
  getEmptyMarginfiAccountsByAuthority,
32
32
  getMarginfiPriceOracle,
33
+ getRemainingAccountsForMarginfiHealthCheck,
33
34
  getTokenAccount,
34
35
  rpcAccountCreated,
35
36
  safeGetPrice,
@@ -49,8 +50,11 @@ export class MarginfiFlProvider extends FlProviderBase {
49
50
  private existingMarginfiAccounts!: MarginfiAccount[];
50
51
  private supplyBankLiquiditySource!: Bank;
51
52
  private debtBankLiquiditySource!: Bank;
53
+
52
54
  private supplyImfiAccount!: IMFIAccount;
53
55
  private debtImfiAccount!: IMFIAccount;
56
+ private supplyRemainingAccounts!: AccountMeta[];
57
+ private debtRemainingAccounts!: AccountMeta[];
54
58
 
55
59
  async initialize() {
56
60
  await this.setAvailableBanks();
@@ -62,10 +66,10 @@ export class MarginfiFlProvider extends FlProviderBase {
62
66
  this.liquidityBank(TokenType.Supply).group.toString() !==
63
67
  this.liquidityBank(TokenType.Debt).group.toString()
64
68
  ) {
65
- this.setIntermediaryAccount([TokenType.Supply]);
66
- this.setIntermediaryAccount([TokenType.Debt]);
69
+ await this.setIntermediaryAccount([TokenType.Supply]);
70
+ await this.setIntermediaryAccount([TokenType.Debt]);
67
71
  } else {
68
- this.setIntermediaryAccount([TokenType.Supply, TokenType.Debt]);
72
+ await this.setIntermediaryAccount([TokenType.Supply, TokenType.Debt]);
69
73
  }
70
74
  }
71
75
 
@@ -114,7 +118,7 @@ export class MarginfiFlProvider extends FlProviderBase {
114
118
  this.debtBankLiquiditySource = debtBanks[0][1];
115
119
  }
116
120
 
117
- private setIntermediaryAccount(sources: TokenType[]) {
121
+ private async setIntermediaryAccount(sources: TokenType[]) {
118
122
  const compatibleMarginfiAccounts = this.existingMarginfiAccounts.filter(
119
123
  (x) => x.group.toString() == this.liquidityBank(sources[0]).group
120
124
  );
@@ -136,6 +140,16 @@ export class MarginfiFlProvider extends FlProviderBase {
136
140
  this.flSigners.push(signer);
137
141
  }
138
142
 
143
+ const remainingAccounts = accountData
144
+ ? (
145
+ await Promise.all(
146
+ accountData.lendingAccount.balances.map((balance) =>
147
+ getRemainingAccountsForMarginfiHealthCheck(this.umi, balance)
148
+ )
149
+ )
150
+ ).flat()
151
+ : [];
152
+
139
153
  for (const s of sources) {
140
154
  const data: IMFIAccount = {
141
155
  signer,
@@ -146,8 +160,10 @@ export class MarginfiFlProvider extends FlProviderBase {
146
160
  const supply = s === TokenType.Supply;
147
161
  if (supply) {
148
162
  this.supplyImfiAccount = data;
163
+ this.supplyRemainingAccounts = remainingAccounts;
149
164
  } else {
150
165
  this.debtImfiAccount = data;
166
+ this.debtRemainingAccounts = remainingAccounts;
151
167
  }
152
168
  consoleLog(
153
169
  `${supply ? "Supply" : "Debt"} iMfi account:`,
@@ -292,45 +308,19 @@ export class MarginfiFlProvider extends FlProviderBase {
292
308
  );
293
309
  const iMfiAccount = this.iMfiAccount(flashLoan.liquiditySource)!;
294
310
 
295
- const remainingAccounts: AccountMeta[] = [];
296
- let flBankHadPrevBalance = false;
297
-
298
- if (iMfiAccount?.accountData) {
299
- iMfiAccount.accountData.lendingAccount.balances.forEach(async (x) => {
300
- if (x.active) {
301
- if (x.bankPk.toString() === bank.publicKey.toString()) {
302
- flBankHadPrevBalance = true;
303
- }
304
-
305
- const priceOracle = publicKey(
306
- await getMarginfiPriceOracle(this.umi, {
307
- pk: toWeb3JsPublicKey(x.bankPk),
308
- })
309
- );
310
-
311
- remainingAccounts.push(
312
- ...[
313
- {
314
- pubkey: x.bankPk,
315
- isSigner: false,
316
- isWritable: false,
317
- },
318
- {
319
- pubkey: priceOracle,
320
- isSigner: false,
321
- isWritable: false,
322
- },
323
- ]
324
- );
325
- }
326
- });
327
- }
311
+ const remainingAccounts: AccountMeta[] =
312
+ flashLoan.liquiditySource === TokenType.Supply
313
+ ? this.supplyRemainingAccounts
314
+ : this.debtRemainingAccounts;
315
+ let iMfiAccountHadPrevFlBalance = remainingAccounts.find(
316
+ (x) => x.pubkey.toString() === bank.publicKey.toString()
317
+ );
328
318
 
329
319
  return transactionBuilder()
330
320
  .add(
331
321
  lendingAccountRepay(this.umi, {
332
322
  amount: flashLoan.baseUnitAmount,
333
- repayAll: !flBankHadPrevBalance,
323
+ repayAll: !iMfiAccountHadPrevFlBalance,
334
324
  bank: bank.publicKey,
335
325
  bankLiquidityVault: publicKey(associatedBankAccs.liquidityVault),
336
326
  marginfiAccount: publicKey(iMfiAccount.accountPk),
@@ -15,6 +15,7 @@ import {
15
15
  getTokenAccount,
16
16
  hasFirstRebalance,
17
17
  hasLastRebalance,
18
+ realtimeUsdToEmaUsd,
18
19
  safeGetPrice,
19
20
  tokenInfo,
20
21
  } from "../../utils";
@@ -202,31 +203,17 @@ export class RebalanceTxBuilder {
202
203
  }
203
204
  }
204
205
 
205
- private realtimeUsdToEmaUsd(realtimeAmountUsd: number, mint: PublicKey) {
206
- return (
207
- (realtimeAmountUsd / safeGetPrice(mint, PriceType.Realtime)!) *
208
- safeGetPrice(mint, PriceType.Ema)!
209
- );
210
- }
211
-
212
206
  private getInitialRebalanceValues() {
213
207
  let rebalanceValues = this.getRebalanceValues();
214
208
  if (!rebalanceValues) {
215
209
  return undefined;
216
210
  }
217
211
 
218
- const postRebalanceEmaUtilRateBps = getLiqUtilzationRateBps(
219
- this.realtimeUsdToEmaUsd(
220
- rebalanceValues.endResult.supplyUsd,
221
- this.client.pos.supplyMint
222
- ),
223
- this.realtimeUsdToEmaUsd(
224
- rebalanceValues.endResult.debtUsd,
225
- this.client.pos.debtMint
226
- ),
227
- this.client.pos.state.liqThresholdBps
228
- );
229
- if (postRebalanceEmaUtilRateBps > this.client.pos.maxBoostToBps) {
212
+ if (
213
+ !this.client.pos.rebalanceHelper.validRealtimePricesBoost(
214
+ rebalanceValues.debtAdjustmentUsd
215
+ )
216
+ ) {
230
217
  this.priceType = PriceType.Ema;
231
218
  rebalanceValues = this.getRebalanceValues();
232
219
  if (!rebalanceValues) {
@@ -78,9 +78,6 @@ export abstract class SolautoClient extends ReferralStateManager {
78
78
  await super.initialize(args);
79
79
 
80
80
  const positionId = args.positionId ?? 0;
81
- if (positionId === 0 && !args.lpUserAccount) {
82
- throw new Error("Self managed position is missing arguments");
83
- }
84
81
  this.pos = await getOrCreatePositionEx(
85
82
  this.umi,
86
83
  this.authority,
@@ -96,7 +93,7 @@ export abstract class SolautoClient extends ReferralStateManager {
96
93
  },
97
94
  this.contextUpdates
98
95
  );
99
- if (this.pos.selfManaged && (!args.supplyMint || !args.debtMint)) {
96
+ if (this.pos.selfManaged) {
100
97
  await this.pos.refreshPositionState();
101
98
  }
102
99
 
@@ -5,8 +5,12 @@ import {
5
5
  publicKey,
6
6
  PublicKey as UmiPublicKey,
7
7
  createSignerFromKeypair,
8
+ AccountMeta,
8
9
  } from "@metaplex-foundation/umi";
9
- import { toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
10
+ import {
11
+ fromWeb3JsPublicKey,
12
+ toWeb3JsPublicKey,
13
+ } from "@metaplex-foundation/umi-web3js-adapters";
10
14
  import { MarginfiAssetAccounts, RebalanceDetails } from "../../types";
11
15
  import { getMarginfiAccounts, MarginfiProgramAccounts } from "../../constants";
12
16
  import {
@@ -31,18 +35,26 @@ import {
31
35
  marginfiAccountEmpty,
32
36
  getTokenAccount,
33
37
  hasFirstRebalance,
38
+ getRemainingAccountsForMarginfiHealthCheck,
39
+ getAccountMeta,
34
40
  } from "../../utils";
35
41
  import {
36
42
  Bank,
43
+ fetchMarginfiAccount,
37
44
  lendingAccountBorrow,
38
45
  lendingAccountDeposit,
39
46
  lendingAccountRepay,
40
47
  lendingAccountWithdraw,
41
48
  marginfiAccountInitialize,
42
49
  safeFetchAllMarginfiAccount,
50
+ safeFetchMarginfiAccount,
43
51
  } from "../../marginfi-sdk";
44
52
  import { SolautoClient, SolautoClientArgs } from "./solautoClient";
45
53
 
54
+ function isSigner(account: PublicKey | Signer): account is Signer {
55
+ return "publicKey" in account;
56
+ }
57
+
46
58
  export class SolautoMarginfiClient extends SolautoClient {
47
59
  public lendingPlatform = LendingPlatform.Marginfi;
48
60
 
@@ -50,6 +62,7 @@ export class SolautoMarginfiClient extends SolautoClient {
50
62
 
51
63
  public marginfiAccount!: PublicKey | Signer;
52
64
  public marginfiAccountPk!: PublicKey;
65
+ public healthCheckRemainingAccounts?: AccountMeta[];
53
66
  public marginfiGroup!: PublicKey;
54
67
 
55
68
  public marginfiSupplyAccounts!: MarginfiAssetAccounts;
@@ -97,13 +110,25 @@ export class SolautoMarginfiClient extends SolautoClient {
97
110
  );
98
111
  }
99
112
  }
100
- this.marginfiAccountPk =
101
- "publicKey" in this.marginfiAccount
102
- ? toWeb3JsPublicKey(this.marginfiAccount.publicKey)
103
- : this.marginfiAccount;
104
113
 
105
- if ("publicKey" in this.marginfiAccount) {
114
+ this.marginfiAccountPk = isSigner(this.marginfiAccount)
115
+ ? toWeb3JsPublicKey(this.marginfiAccount.publicKey)
116
+ : this.marginfiAccount;
117
+
118
+ if (isSigner(this.marginfiAccount)) {
106
119
  this.otherSigners.push(this.marginfiAccount);
120
+ } else if (this.pos.selfManaged) {
121
+ const accountData = await fetchMarginfiAccount(
122
+ this.umi,
123
+ fromWeb3JsPublicKey(this.marginfiAccount as PublicKey)
124
+ );
125
+ this.healthCheckRemainingAccounts = (
126
+ await Promise.all(
127
+ accountData.lendingAccount.balances.map((balance) =>
128
+ getRemainingAccountsForMarginfiHealthCheck(this.umi, balance)
129
+ )
130
+ )
131
+ ).flat();
107
132
  }
108
133
 
109
134
  this.marginfiSupplyAccounts =
@@ -247,6 +272,20 @@ export class SolautoMarginfiClient extends SolautoClient {
247
272
  });
248
273
  }
249
274
  case "Borrow": {
275
+ const remainingAccounts = this.healthCheckRemainingAccounts ?? [];
276
+ if (
277
+ !remainingAccounts.find(
278
+ (x) =>
279
+ x.pubkey.toString() === this.marginfiDebtAccounts.bank.toString()
280
+ )
281
+ ) {
282
+ remainingAccounts.push(
283
+ ...[
284
+ getAccountMeta(new PublicKey(this.marginfiDebtAccounts.bank)),
285
+ getAccountMeta(this.debtPriceOracle),
286
+ ]
287
+ );
288
+ }
250
289
  return lendingAccountBorrow(this.umi, {
251
290
  amount: args.fields[0],
252
291
  signer: this.signer,
@@ -260,7 +299,7 @@ export class SolautoMarginfiClient extends SolautoClient {
260
299
  bankLiquidityVaultAuthority: publicKey(
261
300
  this.marginfiDebtAccounts.vaultAuthority
262
301
  ),
263
- });
302
+ }).addRemainingAccounts(remainingAccounts);
264
303
  }
265
304
  case "Repay": {
266
305
  return lendingAccountRepay(this.umi, {
@@ -293,7 +332,7 @@ export class SolautoMarginfiClient extends SolautoClient {
293
332
  bankLiquidityVaultAuthority: publicKey(
294
333
  this.marginfiSupplyAccounts.vaultAuthority
295
334
  ),
296
- });
335
+ }).addRemainingAccounts(this.healthCheckRemainingAccounts ?? []);
297
336
  }
298
337
  }
299
338
  }
@@ -408,7 +447,9 @@ export class SolautoMarginfiClient extends SolautoClient {
408
447
  ? publicKey(getTokenAccount(this.authority, this.pos.supplyMint))
409
448
  : undefined,
410
449
  vaultSupplyTa: publicKey(this.marginfiSupplyAccounts.liquidityVault),
411
- supplyVaultAuthority: publicKey(this.marginfiSupplyAccounts.vaultAuthority),
450
+ supplyVaultAuthority: publicKey(
451
+ this.marginfiSupplyAccounts.vaultAuthority
452
+ ),
412
453
  debtBank: publicKey(this.marginfiDebtAccounts.bank),
413
454
  debtPriceOracle: publicKey(this.debtPriceOracle),
414
455
  positionDebtTa: publicKey(this.positionDebtTa),
@@ -30,6 +30,7 @@ import {
30
30
  maxRepayFromBps,
31
31
  maxRepayToBps,
32
32
  positionStateWithLatestPrices,
33
+ realtimeUsdToEmaUsd,
33
34
  safeGetPrice,
34
35
  solautoStrategyName,
35
36
  supplyLiquidityDepositable,
@@ -87,6 +88,8 @@ export abstract class SolautoPositionEx {
87
88
  protected _supplyPrice?: number;
88
89
  protected _debtPrice?: number;
89
90
 
91
+ public rebalanceHelper!: PositionRebalanceHelper;
92
+
90
93
  constructor(args: PositionExArgs) {
91
94
  this.umi = args.umi;
92
95
  this.contextUpdates = args.contextUpdates;
@@ -111,6 +114,8 @@ export abstract class SolautoPositionEx {
111
114
 
112
115
  this._data = args.data;
113
116
  this.firstState = { ...args.data.state };
117
+
118
+ this.rebalanceHelper = new PositionRebalanceHelper(this);
114
119
  }
115
120
 
116
121
  abstract lendingPool(): Promise<PublicKey>;
@@ -277,53 +282,12 @@ export abstract class SolautoPositionEx {
277
282
  return tokenInfo(this.supplyMint).isMeme || tokenInfo(this.debtMint).isMeme;
278
283
  }
279
284
 
280
- private sufficientLiquidityToBoost() {
281
- const limitsUpToDate =
282
- this.debtLiquidityUsdAvailable !== 0 ||
283
- this.supplyLiquidityUsdDepositable !== 0;
284
-
285
- if (limitsUpToDate) {
286
- const { debtAdjustmentUsd } = getDebtAdjustment(
287
- this.state.liqThresholdBps,
288
- { supplyUsd: this.supplyUsd(), debtUsd: this.debtUsd() },
289
- this.boostToBps,
290
- { solauto: 50, lpBorrow: 50, flashLoan: 50 } // TODO: get true data here instead of magic numbers
291
- );
292
-
293
- const sufficientLiquidity =
294
- this.debtLiquidityUsdAvailable * 0.95 > debtAdjustmentUsd &&
295
- this.supplyLiquidityUsdDepositable * 0.95 > debtAdjustmentUsd;
296
-
297
- if (!sufficientLiquidity) {
298
- consoleLog("Insufficient liquidity to further boost");
299
- }
300
- return sufficientLiquidity;
301
- }
302
-
303
- return true;
304
- }
305
-
306
- eligibleForRebalance(bpsDistanceThreshold = 0): RebalanceAction | undefined {
307
- if (!this.settings || !this.supplyUsd()) {
308
- return undefined;
309
- }
310
-
311
- const realtimeLiqUtilRateBps = this.liqUtilizationRateBps(
312
- PriceType.Realtime
285
+ eligibleForRebalance(
286
+ bpsDistanceThreshold: number = 0
287
+ ): RebalanceAction | undefined {
288
+ return this.rebalanceHelper.eligibleForRebalance(
289
+ bpsDistanceThreshold
313
290
  );
314
- const emaLiqUtilRateBps = this.liqUtilizationRateBps(PriceType.Ema);
315
-
316
- if (this.repayFromBps - realtimeLiqUtilRateBps <= bpsDistanceThreshold) {
317
- return "repay";
318
- } else if (
319
- realtimeLiqUtilRateBps - this.boostFromBps <= bpsDistanceThreshold ||
320
- emaLiqUtilRateBps - this.boostFromBps <= bpsDistanceThreshold
321
- ) {
322
- const sufficientLiquidity = this.sufficientLiquidityToBoost();
323
- return sufficientLiquidity ? "boost" : undefined;
324
- }
325
-
326
- return undefined;
327
291
  }
328
292
 
329
293
  eligibleForRefresh(): boolean {
@@ -438,3 +402,97 @@ export abstract class SolautoPositionEx {
438
402
  );
439
403
  }
440
404
  }
405
+
406
+ class PositionRebalanceHelper {
407
+ constructor(private pos: SolautoPositionEx) {}
408
+
409
+ private sufficientLiquidityToBoost() {
410
+ const limitsUpToDate =
411
+ this.pos.debtLiquidityUsdAvailable !== 0 ||
412
+ this.pos.supplyLiquidityUsdDepositable !== 0;
413
+
414
+ if (limitsUpToDate) {
415
+ const { debtAdjustmentUsd } = getDebtAdjustment(
416
+ this.pos.state.liqThresholdBps,
417
+ { supplyUsd: this.pos.supplyUsd(), debtUsd: this.pos.debtUsd() },
418
+ this.pos.boostToBps,
419
+ { solauto: 50, lpBorrow: 50, flashLoan: 50 } // Overshoot fees
420
+ );
421
+
422
+ const sufficientLiquidity =
423
+ this.pos.debtLiquidityUsdAvailable * 0.95 > debtAdjustmentUsd &&
424
+ this.pos.supplyLiquidityUsdDepositable * 0.95 > debtAdjustmentUsd;
425
+
426
+ if (!sufficientLiquidity) {
427
+ consoleLog("Insufficient liquidity to further boost");
428
+ }
429
+ return sufficientLiquidity;
430
+ }
431
+
432
+ return true;
433
+ }
434
+
435
+ validRealtimePricesBoost(debtAdjustmentUsd: number) {
436
+ if (this.pos.lendingPlatform !== LendingPlatform.Marginfi) {
437
+ // TODO: LP
438
+ return true;
439
+ }
440
+
441
+ const postRebalanceLiqUtilRate = getLiqUtilzationRateBps(
442
+ realtimeUsdToEmaUsd(
443
+ this.pos.supplyUsd() + debtAdjustmentUsd,
444
+ this.pos.supplyMint
445
+ ),
446
+ realtimeUsdToEmaUsd(
447
+ this.pos.debtUsd() + debtAdjustmentUsd,
448
+ this.pos.debtMint
449
+ ),
450
+ this.pos.state.liqThresholdBps
451
+ );
452
+
453
+ return postRebalanceLiqUtilRate <= this.pos.maxBoostToBps;
454
+ }
455
+
456
+ private validBoostFromHere() {
457
+ const { debtAdjustmentUsd } = getDebtAdjustment(
458
+ this.pos.state.liqThresholdBps,
459
+ {
460
+ supplyUsd: this.pos.supplyUsd(PriceType.Realtime),
461
+ debtUsd: this.pos.debtUsd(PriceType.Realtime),
462
+ },
463
+ this.pos.boostToBps,
464
+ { solauto: 25, lpBorrow: 0, flashLoan: 0 } // Undershoot fees
465
+ );
466
+
467
+ return this.validRealtimePricesBoost(debtAdjustmentUsd);
468
+ }
469
+
470
+ eligibleForRebalance(
471
+ bpsDistanceThreshold: number
472
+ ): RebalanceAction | undefined {
473
+ if (!this.pos.settings || !this.pos.supplyUsd()) {
474
+ return undefined;
475
+ }
476
+
477
+ const realtimeLiqUtilRateBps = this.pos.liqUtilizationRateBps(
478
+ PriceType.Realtime
479
+ );
480
+ const emaLiqUtilRateBps = this.pos.liqUtilizationRateBps(PriceType.Ema);
481
+
482
+ if (
483
+ this.pos.repayFromBps - realtimeLiqUtilRateBps <=
484
+ bpsDistanceThreshold
485
+ ) {
486
+ return "repay";
487
+ } else if (
488
+ (realtimeLiqUtilRateBps - this.pos.boostFromBps <= bpsDistanceThreshold ||
489
+ emaLiqUtilRateBps - this.pos.boostFromBps <= bpsDistanceThreshold) &&
490
+ this.validBoostFromHere()
491
+ ) {
492
+ const sufficientLiquidity = this.sufficientLiquidityToBoost();
493
+ return sufficientLiquidity ? "boost" : undefined;
494
+ }
495
+
496
+ return undefined;
497
+ }
498
+ }
@@ -1,5 +1,5 @@
1
1
  import { PublicKey } from "@solana/web3.js";
2
- import { Program, publicKey, Umi } from "@metaplex-foundation/umi";
2
+ import { AccountMeta, Program, publicKey, Umi } from "@metaplex-foundation/umi";
3
3
  import {
4
4
  fromWeb3JsPublicKey,
5
5
  toWeb3JsPublicKey,
@@ -16,6 +16,7 @@ import {
16
16
  USD_DECIMALS,
17
17
  } from "../constants";
18
18
  import {
19
+ Balance,
19
20
  Bank,
20
21
  deserializeMarginfiAccount,
21
22
  fetchBank,
@@ -44,6 +45,7 @@ import {
44
45
  getMostUpToDatePythOracle,
45
46
  getPythPushOracleAddress,
46
47
  } from "./pythUtils";
48
+ import { getAccountMeta } from "./solanaUtils";
47
49
 
48
50
  export function createDynamicMarginfiProgram(env?: ProgramEnv): Program {
49
51
  return {
@@ -179,6 +181,22 @@ export function findMarginfiAccounts(
179
181
  throw new Error(`Marginfi accounts not found by the bank: ${bank}`);
180
182
  }
181
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
+
182
200
  export function calcMarginfiMaxLtvAndLiqThresholdBps(
183
201
  supplyBank: Bank,
184
202
  debtBank: Bank,
@@ -1,11 +1,13 @@
1
+ import { PublicKey } from "@solana/web3.js";
1
2
  import {
2
3
  BASIS_POINTS,
3
4
  MIN_REPAY_GAP_BPS,
4
5
  OFFSET_FROM_MAX_LTV,
5
6
  USD_DECIMALS,
6
7
  } from "../constants";
7
- import { PositionState } from "../generated";
8
+ import { PositionState, PriceType } from "../generated";
8
9
  import { RoundAction } from "../types";
10
+ import { safeGetPrice } from "./priceUtils";
9
11
 
10
12
  export function calcNetWorthUsd(state?: PositionState) {
11
13
  return fromRoundedUsdValue(state?.netWorth.baseAmountUsdValue ?? BigInt(0));
@@ -203,3 +205,13 @@ export function maxBoostToBps(maxLtvBps: number, liqThresholdBps: number) {
203
205
  getMaxLiqUtilizationRateBps(maxLtvBps, liqThresholdBps, OFFSET_FROM_MAX_LTV)
204
206
  );
205
207
  }
208
+
209
+ export function realtimeUsdToEmaUsd(
210
+ realtimeAmountUsd: number,
211
+ mint: PublicKey
212
+ ) {
213
+ return (
214
+ (realtimeAmountUsd / safeGetPrice(mint, PriceType.Realtime)!) *
215
+ safeGetPrice(mint, PriceType.Ema)!
216
+ );
217
+ }
@@ -18,6 +18,7 @@ import {
18
18
  createTransferInstruction,
19
19
  } from "@solana/spl-token";
20
20
  import {
21
+ AccountMeta,
21
22
  AddressLookupTableInput,
22
23
  Signer,
23
24
  TransactionBuilder,
@@ -28,6 +29,7 @@ import {
28
29
  } from "@metaplex-foundation/umi";
29
30
  import {
30
31
  fromWeb3JsInstruction,
32
+ fromWeb3JsPublicKey,
31
33
  toWeb3JsPublicKey,
32
34
  toWeb3JsTransaction,
33
35
  } from "@metaplex-foundation/umi-web3js-adapters";
@@ -154,6 +156,14 @@ export function splTokenTransferUmiIx(
154
156
  );
155
157
  }
156
158
 
159
+ export function getAccountMeta(
160
+ pubkey: PublicKey,
161
+ isSigner: boolean = false,
162
+ isWritable: boolean = false
163
+ ): AccountMeta {
164
+ return { pubkey: fromWeb3JsPublicKey(pubkey), isSigner, isWritable };
165
+ }
166
+
157
167
  export async function getWalletSplBalances(
158
168
  conn: Connection,
159
169
  wallet: PublicKey,