@haven-fi/solauto-sdk 1.0.58 → 1.0.60

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.
@@ -1,5 +1,5 @@
1
1
  import { PublicKey } from "@solana/web3.js";
2
- import { isOption, isSome, Umi } from "@metaplex-foundation/umi";
2
+ import { isOption, isSome, publicKey, Umi } from "@metaplex-foundation/umi";
3
3
  import {
4
4
  AutomationSettings,
5
5
  DCASettings,
@@ -53,9 +53,10 @@ export function nextAutomationPeriodTimestamp(
53
53
  }
54
54
 
55
55
  export function eligibleForNextAutomationPeriod(
56
- automation: AutomationSettings
56
+ automation: AutomationSettings,
57
+ currentUnixTime: number
57
58
  ): boolean {
58
- return currentUnixSeconds() >= nextAutomationPeriodTimestamp(automation);
59
+ return currentUnixTime >= nextAutomationPeriodTimestamp(automation);
59
60
  }
60
61
 
61
62
  export function getUpdatedValueFromAutomation(
@@ -78,16 +79,16 @@ export function getUpdatedValueFromAutomation(
78
79
 
79
80
  export function getAdjustedSettingsFromAutomation(
80
81
  settings: SolautoSettingsParameters,
81
- currentUnixSeconds: number
82
+ currentUnixTime: number
82
83
  ): SolautoSettingsParameters {
83
84
  const boostToBps =
84
85
  settings.automation.targetPeriods > 0 &&
85
- eligibleForNextAutomationPeriod(settings.automation)
86
+ eligibleForNextAutomationPeriod(settings.automation, currentUnixTime)
86
87
  ? getUpdatedValueFromAutomation(
87
88
  settings.boostToBps,
88
89
  settings.targetBoostToBps,
89
90
  settings.automation,
90
- currentUnixSeconds
91
+ currentUnixTime
91
92
  )
92
93
  : settings.boostToBps;
93
94
 
@@ -121,11 +122,12 @@ export function getSolautoFeesBps(
121
122
  export function eligibleForRebalance(
122
123
  positionState: PositionState,
123
124
  positionSettings: SolautoSettingsParameters,
124
- positionDca: DCASettings
125
+ positionDca: DCASettings,
126
+ currentUnixSecs: number
125
127
  ): RebalanceAction | undefined {
126
128
  if (
127
129
  positionDca.automation.targetPeriods > 0 &&
128
- eligibleForNextAutomationPeriod(positionDca.automation)
130
+ eligibleForNextAutomationPeriod(positionDca.automation, currentUnixSecs)
129
131
  ) {
130
132
  return "dca";
131
133
  }
@@ -135,13 +137,13 @@ export function eligibleForRebalance(
135
137
  }
136
138
 
137
139
  const boostToBps =
138
- eligibleForRefresh(positionState, positionSettings) &&
140
+ eligibleForRefresh(positionState, positionSettings, currentUnixSecs) &&
139
141
  positionSettings.automation.targetPeriods > 0
140
142
  ? getUpdatedValueFromAutomation(
141
143
  positionSettings.boostToBps,
142
144
  positionSettings.targetBoostToBps,
143
145
  positionSettings.automation,
144
- currentUnixSeconds()
146
+ currentUnixSecs
145
147
  )
146
148
  : positionSettings.boostToBps;
147
149
  const repayFrom = positionSettings.repayToBps + positionSettings.repayGap;
@@ -158,10 +160,14 @@ export function eligibleForRebalance(
158
160
 
159
161
  export function eligibleForRefresh(
160
162
  positionState: PositionState,
161
- positionSettings: SolautoSettingsParameters
163
+ positionSettings: SolautoSettingsParameters,
164
+ currentUnixTime: number
162
165
  ): boolean {
163
166
  if (positionSettings.automation.targetPeriods > 0) {
164
- return eligibleForNextAutomationPeriod(positionSettings.automation);
167
+ return eligibleForNextAutomationPeriod(
168
+ positionSettings.automation,
169
+ currentUnixTime
170
+ );
165
171
  } else {
166
172
  return (
167
173
  currentUnixSeconds() - Number(positionState.lastUpdated) >
@@ -319,15 +325,32 @@ export async function getAllPositionsByAuthority(
319
325
  return allPositions;
320
326
  }
321
327
 
322
- export async function positionStateWithPrices(
323
- umi: Umi,
324
- state: PositionState,
325
- protocolAccount: PublicKey,
326
- lendingPlatform: LendingPlatform,
327
- supplyPrice?: number,
328
- debtPrice?: number
329
- ): Promise<PositionState | undefined> {
328
+ interface GetLatestStateProps {
329
+ state: PositionState;
330
+ umi?: Umi;
331
+ protocolAccount?: PublicKey;
332
+ lendingPlatform?: LendingPlatform;
333
+ supplyPrice?: number;
334
+ debtPrice?: number;
335
+ }
336
+
337
+ export async function positionStateWithPrices({
338
+ state,
339
+ supplyPrice,
340
+ debtPrice,
341
+ umi,
342
+ protocolAccount,
343
+ lendingPlatform,
344
+ }: GetLatestStateProps): Promise<PositionState | undefined> {
330
345
  if (currentUnixSeconds() - Number(state.lastUpdated) > 60 * 60 * 24 * 7) {
346
+ if (
347
+ umi === undefined ||
348
+ protocolAccount === undefined ||
349
+ lendingPlatform === undefined
350
+ ) {
351
+ throw new Error("Missing required parameters");
352
+ }
353
+
331
354
  if (lendingPlatform === LendingPlatform.Marginfi) {
332
355
  return await getMarginfiAccountPositionState(
333
356
  umi,
@@ -361,7 +384,10 @@ export async function positionStateWithPrices(
361
384
  state.liqThresholdBps
362
385
  ),
363
386
  netWorth: {
364
- ...state.netWorth,
387
+ baseUnit: toBaseUnit(
388
+ (supplyUsd - debtUsd) / supplyPrice,
389
+ state.supply.decimals
390
+ ),
365
391
  baseAmountUsdValue: toBaseUnit(supplyUsd - debtUsd, USD_DECIMALS),
366
392
  },
367
393
  supply: {
@@ -381,6 +407,82 @@ export async function positionStateWithPrices(
381
407
  };
382
408
  }
383
409
 
410
+ interface AssetProps {
411
+ amountUsedBaseUnit: bigint;
412
+ decimals: number;
413
+ price: number;
414
+ mint: PublicKey;
415
+ }
416
+
417
+ export function createFakePositionState(
418
+ supply: AssetProps,
419
+ debt: AssetProps,
420
+ maxLtvBps: number,
421
+ liqThresholdBps: number
422
+ ): PositionState {
423
+ const supplyUsd =
424
+ fromBaseUnit(supply.amountUsedBaseUnit, supply.decimals) * supply.price;
425
+ const debtUsd =
426
+ fromBaseUnit(debt.amountUsedBaseUnit, debt.decimals) * debt.price;
427
+
428
+ return {
429
+ liqUtilizationRateBps: getLiqUtilzationRateBps(
430
+ supplyUsd,
431
+ debtUsd,
432
+ liqThresholdBps
433
+ ),
434
+ supply: {
435
+ amountUsed: {
436
+ baseUnit: supply.amountUsedBaseUnit,
437
+ baseAmountUsdValue: toBaseUnit(supplyUsd, USD_DECIMALS),
438
+ },
439
+ amountCanBeUsed: {
440
+ baseUnit: toBaseUnit(1000000, supply.decimals),
441
+ baseAmountUsdValue: BigInt(Math.round(1000000 * supply.price)),
442
+ },
443
+ baseAmountMarketPriceUsd: toBaseUnit(supply.price, USD_DECIMALS),
444
+ borrowFeeBps: 0,
445
+ decimals: supply.decimals,
446
+ flashLoanFeeBps: 0,
447
+ mint: publicKey(supply.mint),
448
+ padding1: [],
449
+ padding2: [],
450
+ padding: new Uint8Array([]),
451
+ },
452
+ debt: {
453
+ amountUsed: {
454
+ baseUnit: debt.amountUsedBaseUnit,
455
+ baseAmountUsdValue: toBaseUnit(debtUsd, USD_DECIMALS),
456
+ },
457
+ amountCanBeUsed: {
458
+ baseUnit: toBaseUnit(1000000, debt.decimals),
459
+ baseAmountUsdValue: BigInt(Math.round(1000000 * debt.price)),
460
+ },
461
+ baseAmountMarketPriceUsd: toBaseUnit(debt.price, USD_DECIMALS),
462
+ borrowFeeBps: 0,
463
+ decimals: debt.decimals,
464
+ flashLoanFeeBps: 0,
465
+ mint: publicKey(debt.mint),
466
+ padding1: [],
467
+ padding2: [],
468
+ padding: new Uint8Array([]),
469
+ },
470
+ netWorth: {
471
+ baseUnit: toBaseUnit(
472
+ (supplyUsd - debtUsd) / supply.price,
473
+ supply.decimals
474
+ ),
475
+ baseAmountUsdValue: toBaseUnit(supplyUsd - debtUsd, USD_DECIMALS),
476
+ },
477
+ maxLtvBps,
478
+ liqThresholdBps,
479
+ lastUpdated: BigInt(currentUnixSeconds()),
480
+ padding1: [],
481
+ padding2: [],
482
+ padding: [],
483
+ };
484
+ }
485
+
384
486
  type PositionAdjustment =
385
487
  | { type: "supply"; value: bigint }
386
488
  | { type: "debt"; value: bigint }
@@ -1,6 +1,12 @@
1
1
  import { PublicKey } from "@solana/web3.js";
2
2
  import { SolautoClient } from "../../clients/solautoClient";
3
- import { FeeType, PositionTokenUsage } from "../../generated";
3
+ import {
4
+ DCASettings,
5
+ FeeType,
6
+ PositionState,
7
+ PositionTokenUsage,
8
+ SolautoSettingsParameters,
9
+ } from "../../generated";
4
10
  import {
5
11
  eligibleForNextAutomationPeriod,
6
12
  getAdjustedSettingsFromAutomation,
@@ -26,15 +32,12 @@ import {
26
32
  PRICES,
27
33
  } from "../../constants/solautoConstants";
28
34
 
29
- function getAdditionalAmountToDcaIn(client: SolautoClient): number {
30
- const dca = client.solautoPositionActiveDca()!;
35
+ function getAdditionalAmountToDcaIn(dca: DCASettings): number {
31
36
  if (dca.debtToAddBaseUnit === BigInt(0)) {
32
37
  return 0;
33
38
  }
34
39
 
35
- const debtBalance =
36
- Number(client.solautoPositionData?.position.dca.debtToAddBaseUnit ?? 0) +
37
- Number(client.livePositionUpdates.debtTaBalanceAdjustment ?? 0);
40
+ const debtBalance = Number(dca.debtToAddBaseUnit);
38
41
  const updatedDebtBalance = getUpdatedValueFromAutomation(
39
42
  debtBalance,
40
43
  0,
@@ -45,44 +48,44 @@ function getAdditionalAmountToDcaIn(client: SolautoClient): number {
45
48
  return debtBalance - updatedDebtBalance;
46
49
  }
47
50
 
48
- function getStandardTargetLiqUtilizationRateBps(client: SolautoClient): number {
49
- if (!client.selfManaged) {
50
- const adjustedSettings = getAdjustedSettingsFromAutomation(
51
- client.solautoPositionSettings()!,
52
- currentUnixSeconds()
53
- );
51
+ function getStandardTargetLiqUtilizationRateBps(
52
+ state: PositionState,
53
+ settings: SolautoSettingsParameters
54
+ ): number {
55
+ const adjustedSettings = getAdjustedSettingsFromAutomation(
56
+ settings,
57
+ currentUnixSeconds()
58
+ );
59
+
60
+ const repayFrom = adjustedSettings.repayToBps - adjustedSettings.repayGap;
61
+ const boostFrom = adjustedSettings.boostToBps + adjustedSettings.boostGap;
54
62
 
55
- const repayFrom = adjustedSettings.repayToBps - adjustedSettings.repayGap;
56
- const boostFrom = adjustedSettings.boostToBps + adjustedSettings.boostGap;
57
-
58
- if (client.solautoPositionState!.liqUtilizationRateBps < boostFrom) {
59
- return adjustedSettings.boostToBps;
60
- } else if (
61
- client.solautoPositionState!.liqUtilizationRateBps > repayFrom ||
62
- repayFrom - client.solautoPositionState!.liqUtilizationRateBps <
63
- repayFrom * 0.015
64
- ) {
65
- return adjustedSettings.repayToBps;
66
- } else {
67
- throw new Error("Invalid rebalance condition");
68
- }
63
+ if (state.liqUtilizationRateBps < boostFrom) {
64
+ return adjustedSettings.boostToBps;
65
+ } else if (
66
+ state.liqUtilizationRateBps > repayFrom ||
67
+ repayFrom - state.liqUtilizationRateBps < repayFrom * 0.015
68
+ ) {
69
+ return adjustedSettings.repayToBps;
69
70
  } else {
70
- throw new Error(
71
- "This is a self-managed position, a targetLiqUtilizationRateBps must be provided initiate a rebalance"
72
- );
71
+ throw new Error("Invalid rebalance condition");
73
72
  }
74
73
  }
75
74
 
76
- function targetLiqUtilizationRateBpsFromDCA(client: SolautoClient) {
75
+ function targetLiqUtilizationRateBpsFromDCA(
76
+ state: PositionState,
77
+ settings: SolautoSettingsParameters,
78
+ dca: DCASettings
79
+ ) {
77
80
  const adjustedSettings = getAdjustedSettingsFromAutomation(
78
- client.solautoPositionSettings()!,
81
+ settings,
79
82
  currentUnixSeconds()
80
83
  );
81
84
 
82
85
  let targetRateBps = 0;
83
- if (client.solautoPositionActiveDca()!.debtToAddBaseUnit > BigInt(0)) {
86
+ if (dca.debtToAddBaseUnit > BigInt(0)) {
84
87
  targetRateBps = Math.max(
85
- client.solautoPositionState!.liqUtilizationRateBps,
88
+ state.liqUtilizationRateBps,
86
89
  adjustedSettings.boostToBps
87
90
  );
88
91
  } else {
@@ -91,32 +94,29 @@ function targetLiqUtilizationRateBpsFromDCA(client: SolautoClient) {
91
94
  return targetRateBps;
92
95
  }
93
96
 
94
- function isDcaRebalance(client: SolautoClient): boolean {
95
- if (client.solautoPositionActiveDca() === undefined || client.selfManaged) {
97
+ function isDcaRebalance(
98
+ state: PositionState,
99
+ settings: SolautoSettingsParameters,
100
+ dca: DCASettings | undefined,
101
+ currentUnixTime: number
102
+ ): boolean {
103
+ if (dca === undefined || dca.automation.targetPeriods === 0) {
96
104
  return false;
97
105
  }
98
106
 
99
107
  const adjustedSettings = getAdjustedSettingsFromAutomation(
100
- client.solautoPositionSettings()!,
108
+ settings,
101
109
  currentUnixSeconds()
102
110
  );
103
111
 
104
112
  if (
105
- client.solautoPositionState!.liqUtilizationRateBps >
113
+ state.liqUtilizationRateBps >
106
114
  adjustedSettings.repayToBps + adjustedSettings.repayGap
107
115
  ) {
108
116
  return false;
109
117
  }
110
118
 
111
- if (client.solautoPositionActiveDca()!.automation.targetPeriods === 0) {
112
- return false;
113
- }
114
-
115
- if (
116
- !eligibleForNextAutomationPeriod(
117
- client.solautoPositionActiveDca()!.automation
118
- )
119
- ) {
119
+ if (!eligibleForNextAutomationPeriod(dca.automation, currentUnixTime)) {
120
120
  return false;
121
121
  }
122
122
 
@@ -124,7 +124,10 @@ function isDcaRebalance(client: SolautoClient): boolean {
124
124
  }
125
125
 
126
126
  function getTargetRateAndDcaAmount(
127
- client: SolautoClient,
127
+ state: PositionState,
128
+ settings: SolautoSettingsParameters | undefined,
129
+ dca: DCASettings | undefined,
130
+ currentUnixTime: number,
128
131
  targetLiqUtilizationRateBps?: number
129
132
  ): { targetRateBps: number; amountToDcaIn?: number } {
130
133
  if (targetLiqUtilizationRateBps !== undefined) {
@@ -133,10 +136,19 @@ function getTargetRateAndDcaAmount(
133
136
  };
134
137
  }
135
138
 
136
- if (isDcaRebalance(client)) {
137
- const amountToDcaIn = getAdditionalAmountToDcaIn(client);
138
- const targetLiqUtilizationRateBps =
139
- targetLiqUtilizationRateBpsFromDCA(client);
139
+ if (settings === undefined) {
140
+ throw new Error(
141
+ "If rebalancing a self-managed position, settings, and DCA should be provided"
142
+ );
143
+ }
144
+
145
+ if (isDcaRebalance(state, settings, dca, currentUnixTime)) {
146
+ const amountToDcaIn = getAdditionalAmountToDcaIn(dca!);
147
+ const targetLiqUtilizationRateBps = targetLiqUtilizationRateBpsFromDCA(
148
+ state,
149
+ settings,
150
+ dca!
151
+ );
140
152
 
141
153
  return {
142
154
  targetRateBps: targetLiqUtilizationRateBps,
@@ -144,7 +156,7 @@ function getTargetRateAndDcaAmount(
144
156
  };
145
157
  } else {
146
158
  return {
147
- targetRateBps: getStandardTargetLiqUtilizationRateBps(client),
159
+ targetRateBps: getStandardTargetLiqUtilizationRateBps(state, settings),
148
160
  };
149
161
  }
150
162
  }
@@ -152,17 +164,24 @@ function getTargetRateAndDcaAmount(
152
164
  export interface RebalanceValues {
153
165
  increasingLeverage: boolean;
154
166
  debtAdjustmentUsd: number;
167
+ amountToDcaIn: number;
155
168
  amountUsdToDcaIn: number;
156
169
  }
157
170
 
158
171
  export function getRebalanceValues(
159
- client: SolautoClient,
172
+ state: PositionState,
173
+ settings: SolautoSettingsParameters | undefined,
174
+ dca: DCASettings | undefined,
175
+ feeType: FeeType,
176
+ currentUnixTime: number,
177
+ supplyPrice: number,
178
+ debtPrice: number,
160
179
  targetLiqUtilizationRateBps?: number,
161
180
  limitGapBps?: number
162
181
  ): RebalanceValues {
163
182
  if (
164
- client.solautoPositionState === undefined ||
165
- client.solautoPositionState.lastUpdated <
183
+ state === undefined ||
184
+ state.lastUpdated <
166
185
  BigInt(
167
186
  Math.round(currentUnixSeconds() - MIN_POSITION_STATE_FRESHNESS_SECS)
168
187
  )
@@ -171,50 +190,41 @@ export function getRebalanceValues(
171
190
  }
172
191
 
173
192
  const { targetRateBps, amountToDcaIn } = getTargetRateAndDcaAmount(
174
- client,
193
+ state,
194
+ settings,
195
+ dca,
196
+ currentUnixTime,
175
197
  targetLiqUtilizationRateBps
176
198
  );
177
199
 
178
200
  const amountUsdToDcaIn =
179
- fromBaseUnit(
180
- BigInt(Math.round(amountToDcaIn ?? 0)),
181
- client.solautoPositionState!.debt.decimals
182
- ) * PRICES[client.debtMint.toString()].price;
201
+ fromBaseUnit(BigInt(Math.round(amountToDcaIn ?? 0)), state.debt.decimals) *
202
+ debtPrice;
183
203
 
184
204
  const increasingLeverage =
185
- amountUsdToDcaIn > 0 ||
186
- client.solautoPositionState!.liqUtilizationRateBps < targetRateBps;
205
+ amountUsdToDcaIn > 0 || state.liqUtilizationRateBps < targetRateBps;
187
206
  let adjustmentFeeBps = 0;
188
207
  if (increasingLeverage) {
189
- adjustmentFeeBps = getSolautoFeesBps(
190
- client.referredByState !== undefined,
191
- client.solautoPositionData?.feeType ?? FeeType.Small
192
- ).total;
208
+ adjustmentFeeBps = getSolautoFeesBps(false, feeType).total;
193
209
  }
194
210
 
195
211
  const supplyUsd =
196
- fromBaseUnit(
197
- client.solautoPositionState!.supply.amountUsed.baseAmountUsdValue,
198
- USD_DECIMALS
199
- ) + amountUsdToDcaIn;
212
+ fromBaseUnit(state.supply.amountUsed.baseAmountUsdValue, USD_DECIMALS) +
213
+ amountUsdToDcaIn;
200
214
  const debtUsd = fromBaseUnit(
201
- client.solautoPositionState!.debt.amountUsed.baseAmountUsdValue,
215
+ state.debt.amountUsed.baseAmountUsdValue,
202
216
  USD_DECIMALS
203
217
  );
204
218
  let debtAdjustmentUsd = getDebtAdjustmentUsd(
205
- client.solautoPositionState!.liqThresholdBps,
219
+ state.liqThresholdBps,
206
220
  supplyUsd,
207
221
  debtUsd,
208
222
  targetRateBps,
209
223
  adjustmentFeeBps
210
224
  );
211
225
 
212
- const input = increasingLeverage
213
- ? client.solautoPositionState!.debt
214
- : client.solautoPositionState!.supply;
215
- const inputMarketPrice = increasingLeverage
216
- ? PRICES[client.debtMint.toString()].price
217
- : PRICES[client.supplyMint.toString()].price;
226
+ const input = increasingLeverage ? state.debt : state.supply;
227
+ const inputMarketPrice = increasingLeverage ? debtPrice : supplyPrice;
218
228
 
219
229
  const limitGap = limitGapBps
220
230
  ? fromBps(limitGapBps)
@@ -235,6 +245,7 @@ export function getRebalanceValues(
235
245
  return {
236
246
  increasingLeverage,
237
247
  debtAdjustmentUsd,
248
+ amountToDcaIn: amountToDcaIn ?? 0,
238
249
  amountUsdToDcaIn,
239
250
  };
240
251
  }
@@ -269,7 +280,7 @@ export function getFlashLoanDetails(
269
280
  values.debtAdjustmentUsd > 0
270
281
  ? debtUsd + debtAdjustmentWithSlippage
271
282
  : debtUsd;
272
-
283
+
273
284
  const tempLiqUtilizationRateBps = getLiqUtilzationRateBps(
274
285
  supplyUsd,
275
286
  debtUsd,
@@ -342,7 +353,7 @@ export function getJupSwapRebalanceDetails(
342
353
  inputMint: toWeb3JsPublicKey(input.mint),
343
354
  outputMint: toWeb3JsPublicKey(output.mint),
344
355
  destinationWallet: client.solautoPosition,
345
- slippageBpsIncFactor: 0.25 + ((attemptNum ?? 0) * 0.2),
356
+ slippageBpsIncFactor: 0.25 + (attemptNum ?? 0) * 0.2,
346
357
  amount: rebalancingToZero
347
358
  ? client.solautoPositionState!.debt.amountUsed.baseUnit +
348
359
  BigInt(
package/tests/shared.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  import { Signer, createSignerFromKeypair } from "@metaplex-foundation/umi";
2
- import { Connection, clusterApiUrl } from "@solana/web3.js";
2
+ import { Connection, Keypair, clusterApiUrl } from "@solana/web3.js";
3
3
  import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
4
4
  import { getSecretKey } from "../local/shared";
5
+ import { fromWeb3JsKeypair } from "@metaplex-foundation/umi-web3js-adapters";
5
6
 
6
- export function setupTest(keypairFilename?: string): Signer {
7
+ export function setupTest(keypairFilename?: string, random?: boolean): Signer {
7
8
  const umi = createUmi(
8
9
  new Connection(clusterApiUrl("mainnet-beta"), "confirmed")
9
10
  );
10
11
  const secretKey = getSecretKey(keypairFilename);
11
- const signerKeypair = umi.eddsa.createKeypairFromSecretKey(secretKey);
12
+ const signerKeypair = random
13
+ ? fromWeb3JsKeypair(Keypair.generate())
14
+ : umi.eddsa.createKeypairFromSecretKey(secretKey);
12
15
  const signer = createSignerFromKeypair(umi, signerKeypair);
13
16
 
14
17
  return signer;