@exponent-labs/exponent-sdk 0.9.0 → 0.9.1

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 (153) hide show
  1. package/build/client/vaults/index.d.ts +2 -0
  2. package/build/client/vaults/index.js +2 -0
  3. package/build/client/vaults/index.js.map +1 -1
  4. package/build/client/vaults/types/index.d.ts +2 -0
  5. package/build/client/vaults/types/index.js +2 -0
  6. package/build/client/vaults/types/index.js.map +1 -1
  7. package/build/client/vaults/types/kaminoFarmEntry.d.ts +15 -0
  8. package/build/client/vaults/types/kaminoFarmEntry.js +17 -0
  9. package/build/client/vaults/types/kaminoFarmEntry.js.map +1 -0
  10. package/build/client/vaults/types/kaminoObligationEntry.d.ts +24 -7
  11. package/build/client/vaults/types/kaminoObligationEntry.js +2 -1
  12. package/build/client/vaults/types/kaminoObligationEntry.js.map +1 -1
  13. package/build/client/vaults/types/obligationType.d.ts +1 -1
  14. package/build/client/vaults/types/positionUpdate.d.ts +9 -0
  15. package/build/client/vaults/types/positionUpdate.js +23 -0
  16. package/build/client/vaults/types/positionUpdate.js.map +1 -1
  17. package/build/client/vaults/types/proposalAction.d.ts +54 -54
  18. package/build/client/vaults/types/proposalAction.js +0 -3
  19. package/build/client/vaults/types/proposalAction.js.map +1 -1
  20. package/build/client/vaults/types/reserveFarmMapping.d.ts +19 -0
  21. package/build/client/vaults/types/reserveFarmMapping.js +18 -0
  22. package/build/client/vaults/types/reserveFarmMapping.js.map +1 -0
  23. package/build/client/vaults/types/strategyPosition.d.ts +6 -1
  24. package/build/client/vaults/types/strategyPosition.js +5 -0
  25. package/build/client/vaults/types/strategyPosition.js.map +1 -1
  26. package/build/exponentVaults/aumCalculator.d.ts +25 -4
  27. package/build/exponentVaults/aumCalculator.js +236 -15
  28. package/build/exponentVaults/aumCalculator.js.map +1 -1
  29. package/build/exponentVaults/fetcher.d.ts +52 -0
  30. package/build/exponentVaults/fetcher.js +199 -0
  31. package/build/exponentVaults/fetcher.js.map +1 -0
  32. package/build/exponentVaults/index.d.ts +10 -9
  33. package/build/exponentVaults/index.js +25 -8
  34. package/build/exponentVaults/index.js.map +1 -1
  35. package/build/exponentVaults/kamino-farms.d.ts +144 -0
  36. package/build/exponentVaults/kamino-farms.js +396 -0
  37. package/build/exponentVaults/kamino-farms.js.map +1 -0
  38. package/build/exponentVaults/loopscale/client.d.ts +240 -0
  39. package/build/exponentVaults/loopscale/client.js +590 -0
  40. package/build/exponentVaults/loopscale/client.js.map +1 -0
  41. package/build/exponentVaults/loopscale/client.test.d.ts +1 -0
  42. package/build/exponentVaults/loopscale/client.test.js +183 -0
  43. package/build/exponentVaults/loopscale/client.test.js.map +1 -0
  44. package/build/exponentVaults/loopscale/helpers.d.ts +29 -0
  45. package/build/exponentVaults/loopscale/helpers.js +119 -0
  46. package/build/exponentVaults/loopscale/helpers.js.map +1 -0
  47. package/build/exponentVaults/loopscale/index.d.ts +3 -0
  48. package/build/exponentVaults/loopscale/index.js +12 -0
  49. package/build/exponentVaults/loopscale/index.js.map +1 -0
  50. package/build/exponentVaults/loopscale/prepared-transactions.d.ts +13 -0
  51. package/build/exponentVaults/loopscale/prepared-transactions.js +271 -0
  52. package/build/exponentVaults/loopscale/prepared-transactions.js.map +1 -0
  53. package/build/exponentVaults/loopscale/prepared-transactions.test.d.ts +1 -0
  54. package/build/exponentVaults/loopscale/prepared-transactions.test.js +400 -0
  55. package/build/exponentVaults/loopscale/prepared-transactions.test.js.map +1 -0
  56. package/build/exponentVaults/loopscale/prepared-types.d.ts +62 -0
  57. package/build/exponentVaults/loopscale/prepared-types.js +3 -0
  58. package/build/exponentVaults/loopscale/prepared-types.js.map +1 -0
  59. package/build/exponentVaults/loopscale/response-plan.d.ts +69 -0
  60. package/build/exponentVaults/loopscale/response-plan.js +141 -0
  61. package/build/exponentVaults/loopscale/response-plan.js.map +1 -0
  62. package/build/exponentVaults/loopscale/response-plan.test.d.ts +1 -0
  63. package/build/exponentVaults/loopscale/response-plan.test.js +139 -0
  64. package/build/exponentVaults/loopscale/response-plan.test.js.map +1 -0
  65. package/build/exponentVaults/loopscale/send-plan.d.ts +75 -0
  66. package/build/exponentVaults/loopscale/send-plan.js +235 -0
  67. package/build/exponentVaults/loopscale/send-plan.js.map +1 -0
  68. package/build/exponentVaults/loopscale/types.d.ts +443 -0
  69. package/build/exponentVaults/loopscale/types.js +3 -0
  70. package/build/exponentVaults/loopscale/types.js.map +1 -0
  71. package/build/exponentVaults/loopscale-client.d.ts +113 -524
  72. package/build/exponentVaults/loopscale-client.js +296 -539
  73. package/build/exponentVaults/loopscale-client.js.map +1 -1
  74. package/build/exponentVaults/loopscale-client.test.d.ts +1 -0
  75. package/build/exponentVaults/loopscale-client.test.js +162 -0
  76. package/build/exponentVaults/loopscale-client.test.js.map +1 -0
  77. package/build/exponentVaults/loopscale-client.types.d.ts +425 -0
  78. package/build/exponentVaults/loopscale-client.types.js +3 -0
  79. package/build/exponentVaults/loopscale-client.types.js.map +1 -0
  80. package/build/exponentVaults/loopscale-execution.d.ts +125 -0
  81. package/build/exponentVaults/loopscale-execution.js +341 -0
  82. package/build/exponentVaults/loopscale-execution.js.map +1 -0
  83. package/build/exponentVaults/loopscale-execution.test.d.ts +1 -0
  84. package/build/exponentVaults/loopscale-execution.test.js +139 -0
  85. package/build/exponentVaults/loopscale-execution.test.js.map +1 -0
  86. package/build/exponentVaults/loopscale-vault.d.ts +115 -0
  87. package/build/exponentVaults/loopscale-vault.js +275 -0
  88. package/build/exponentVaults/loopscale-vault.js.map +1 -0
  89. package/build/exponentVaults/loopscale-vault.test.d.ts +1 -0
  90. package/build/exponentVaults/loopscale-vault.test.js +102 -0
  91. package/build/exponentVaults/loopscale-vault.test.js.map +1 -0
  92. package/build/exponentVaults/policyBuilders.d.ts +62 -0
  93. package/build/exponentVaults/policyBuilders.js +119 -2
  94. package/build/exponentVaults/policyBuilders.js.map +1 -1
  95. package/build/exponentVaults/pricePathResolver.d.ts +45 -0
  96. package/build/exponentVaults/pricePathResolver.js +198 -0
  97. package/build/exponentVaults/pricePathResolver.js.map +1 -0
  98. package/build/exponentVaults/pricePathResolver.test.d.ts +1 -0
  99. package/build/exponentVaults/pricePathResolver.test.js +369 -0
  100. package/build/exponentVaults/pricePathResolver.test.js.map +1 -0
  101. package/build/exponentVaults/syncTransaction.js +4 -1
  102. package/build/exponentVaults/syncTransaction.js.map +1 -1
  103. package/build/exponentVaults/titan-quote.js +170 -36
  104. package/build/exponentVaults/titan-quote.js.map +1 -1
  105. package/build/exponentVaults/vault-instruction-types.d.ts +363 -0
  106. package/build/exponentVaults/vault-instruction-types.js +128 -0
  107. package/build/exponentVaults/vault-instruction-types.js.map +1 -0
  108. package/build/exponentVaults/vault-interaction.d.ts +156 -313
  109. package/build/exponentVaults/vault-interaction.js +1581 -353
  110. package/build/exponentVaults/vault-interaction.js.map +1 -1
  111. package/build/exponentVaults/vault.d.ts +51 -2
  112. package/build/exponentVaults/vault.js +324 -48
  113. package/build/exponentVaults/vault.js.map +1 -1
  114. package/build/exponentVaults/vaultTransactionBuilder.d.ts +100 -134
  115. package/build/exponentVaults/vaultTransactionBuilder.js +359 -266
  116. package/build/exponentVaults/vaultTransactionBuilder.js.map +1 -1
  117. package/build/exponentVaults/vaultTransactionBuilder.test.d.ts +1 -0
  118. package/build/exponentVaults/vaultTransactionBuilder.test.js +214 -0
  119. package/build/exponentVaults/vaultTransactionBuilder.test.js.map +1 -0
  120. package/build/marketThree.d.ts +6 -2
  121. package/build/marketThree.js +10 -8
  122. package/build/marketThree.js.map +1 -1
  123. package/package.json +32 -32
  124. package/src/client/vaults/index.ts +2 -0
  125. package/src/client/vaults/types/index.ts +2 -0
  126. package/src/client/vaults/types/kaminoFarmEntry.ts +32 -0
  127. package/src/client/vaults/types/kaminoObligationEntry.ts +6 -3
  128. package/src/client/vaults/types/positionUpdate.ts +62 -0
  129. package/src/client/vaults/types/proposalAction.ts +0 -3
  130. package/src/client/vaults/types/reserveFarmMapping.ts +35 -0
  131. package/src/client/vaults/types/strategyPosition.ts +18 -1
  132. package/src/exponentVaults/aumCalculator.ts +353 -16
  133. package/src/exponentVaults/fetcher.ts +257 -0
  134. package/src/exponentVaults/index.ts +64 -40
  135. package/src/exponentVaults/kamino-farms.ts +538 -0
  136. package/src/exponentVaults/loopscale/client.ts +808 -0
  137. package/src/exponentVaults/loopscale/helpers.ts +172 -0
  138. package/src/exponentVaults/loopscale/index.ts +57 -0
  139. package/src/exponentVaults/loopscale/prepared-transactions.ts +435 -0
  140. package/src/exponentVaults/loopscale/prepared-types.ts +73 -0
  141. package/src/exponentVaults/loopscale/types.ts +466 -0
  142. package/src/exponentVaults/policyBuilders.ts +170 -0
  143. package/src/exponentVaults/pricePathResolver.test.ts +466 -0
  144. package/src/exponentVaults/pricePathResolver.ts +273 -0
  145. package/src/exponentVaults/syncTransaction.ts +6 -1
  146. package/src/exponentVaults/titan-quote.ts +231 -45
  147. package/src/exponentVaults/vault-instruction-types.ts +493 -0
  148. package/src/exponentVaults/vault-interaction.ts +2227 -636
  149. package/src/exponentVaults/vault.ts +474 -63
  150. package/src/exponentVaults/vaultTransactionBuilder.test.ts +256 -0
  151. package/src/exponentVaults/vaultTransactionBuilder.ts +555 -413
  152. package/src/marketThree.ts +14 -6
  153. package/src/exponentVaults/loopscale-client.ts +0 -1373
@@ -31,12 +31,10 @@ import {
31
31
  ExponentPrices,
32
32
  ExponentVault as ExponentVaultAccountType,
33
33
  ExponentVault as ExponentVaultType,
34
- ExponentVaultsFetcher,
35
34
  OrderbookEntry,
36
35
  PriceId,
37
36
  TokenAccountEntry,
38
37
  TokenEntry,
39
- transformAnchorData,
40
38
  } from "@exponent-labs/exponent-vaults-fetcher"
41
39
  import { PROGRAM_ID as EXPONENT_VAULTS_PROGRAM_ID } from "@exponent-labs/exponent-vaults-idl"
42
40
  import { ExponentVaultsPDA, SeedId, SquadsPDA } from "@exponent-labs/exponent-vaults-pda"
@@ -44,9 +42,13 @@ import { ExponentVaultsPDA, SeedId, SquadsPDA } from "@exponent-labs/exponent-va
44
42
  import { fetchAllMaybeMarketThreeAccounts } from "../client/clmm/accounts/marketThree"
45
43
  import * as exponentVaults from "../client/vaults"
46
44
  import { Environment, LOCAL_ENV } from "../environment"
47
- import { emitEventAuthority } from "../utils"
45
+ import { emitEventAuthority, uniqueRemainingAccounts } from "../utils"
48
46
  import { AumCalculator } from "./aumCalculator"
47
+ import { deserializeExponentStrategyVaultFetcherShape, ExponentVaultsFetcher } from "./fetcher"
48
+ import { getKaminoFarmScopePricesAddress, decodeKaminoFarmState } from "./kamino-farms"
49
+ import { extractPriceIds } from "./pricePathResolver"
49
50
  import { SquadsVaultTxnResolver } from "./squadsVaultTxnResolver"
51
+ import { getKaminoFarmsObligationFarm } from "./../../../kamino-lend-standard/src/constants"
50
52
 
51
53
  type Numeric = bigint | number
52
54
 
@@ -54,6 +56,8 @@ export const SYNTHETIC_USD_MINT = new PublicKey("USD1111111111111111111111111111
54
56
  export const SYNTHETIC_USD_DECIMALS = 6
55
57
  export const SYNTHETIC_USD9_MINT = new PublicKey("USD1111111111111111111111111111111111111119")
56
58
  export const SYNTHETIC_USD9_DECIMALS = 9
59
+ const LOCAL_UPDATE_PRICE_DISCRIMINATOR = Buffer.from([7])
60
+ const KAMINO_FARMS_PROGRAM_ID = new PublicKey("FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr")
57
61
 
58
62
  export const getSyntheticMintDecimals = (mint: PublicKey): number | null => {
59
63
  if (mint.equals(SYNTHETIC_USD_MINT)) return SYNTHETIC_USD_DECIMALS
@@ -61,6 +65,103 @@ export const getSyntheticMintDecimals = (mint: PublicKey): number | null => {
61
65
  return null
62
66
  }
63
67
 
68
+ function unwrapTupleLikeValue(value: unknown): unknown {
69
+ if (Array.isArray(value)) {
70
+ return value[0]
71
+ }
72
+ if (value && typeof value === "object" && "0" in value) {
73
+ return (value as { 0?: unknown })[0]
74
+ }
75
+ return value ?? undefined
76
+ }
77
+
78
+ function addTrackedPriceIds(target: Set<number>, priceIdValue: unknown) {
79
+ for (const id of extractPriceIds(priceIdValue)) {
80
+ const numericId = Number(id)
81
+ if (numericId !== 0) {
82
+ target.add(numericId)
83
+ }
84
+ }
85
+ }
86
+
87
+ export function collectTrackedStrategyVaultPriceIds(state: {
88
+ tokenEntries: Array<{ priceId: unknown }>
89
+ strategyPositions: Array<Record<string, unknown>>
90
+ }): Set<number> {
91
+ const allPriceIds = new Set<number>()
92
+
93
+ for (const entry of state.tokenEntries) {
94
+ addTrackedPriceIds(allPriceIds, entry.priceId)
95
+ }
96
+
97
+ for (const position of state.strategyPositions) {
98
+ if ("orderbook" in position) {
99
+ const orderbook = unwrapTupleLikeValue(position.orderbook) as { priceIdPt?: unknown } | undefined
100
+ if (orderbook?.priceIdPt) {
101
+ addTrackedPriceIds(allPriceIds, orderbook.priceIdPt)
102
+ }
103
+ continue
104
+ }
105
+
106
+ if ("tokenAccount" in position) {
107
+ const tokenAccount = unwrapTupleLikeValue(position.tokenAccount) as {
108
+ balances?: Array<{ priceId?: unknown }>
109
+ } | undefined
110
+ for (const balance of tokenAccount?.balances ?? []) {
111
+ if (balance.priceId) {
112
+ addTrackedPriceIds(allPriceIds, balance.priceId)
113
+ }
114
+ }
115
+ continue
116
+ }
117
+
118
+ if ("yieldPosition" in position) {
119
+ const yieldPosition = unwrapTupleLikeValue(position.yieldPosition) as { priceIdPt?: unknown } | undefined
120
+ if (yieldPosition?.priceIdPt) {
121
+ addTrackedPriceIds(allPriceIds, yieldPosition.priceIdPt)
122
+ }
123
+ continue
124
+ }
125
+
126
+ if ("clmmPosition" in position) {
127
+ const clmmPosition = unwrapTupleLikeValue(position.clmmPosition) as {
128
+ priceIdPt?: unknown
129
+ priceIdSy?: unknown
130
+ } | undefined
131
+ if (clmmPosition?.priceIdPt) {
132
+ addTrackedPriceIds(allPriceIds, clmmPosition.priceIdPt)
133
+ }
134
+ if (clmmPosition?.priceIdSy) {
135
+ addTrackedPriceIds(allPriceIds, clmmPosition.priceIdSy)
136
+ }
137
+ continue
138
+ }
139
+
140
+ if ("obligation" in position) {
141
+ const rawObligation = position.obligation as Record<string, unknown> | Array<unknown>
142
+ const inner = unwrapTupleLikeValue(rawObligation as unknown[] | { 0?: unknown })
143
+ const kaminoVariant =
144
+ inner && typeof inner === "object" && "kaminoObligation" in inner
145
+ ? (inner as { kaminoObligation?: unknown }).kaminoObligation
146
+ : inner
147
+ const kaminoEntry = unwrapTupleLikeValue(kaminoVariant) as {
148
+ quotePriceId?: unknown
149
+ reservePriceMappings?: Array<{ reservePriceId?: unknown }>
150
+ } | undefined
151
+ if (kaminoEntry?.quotePriceId) {
152
+ addTrackedPriceIds(allPriceIds, kaminoEntry.quotePriceId)
153
+ }
154
+ for (const mapping of kaminoEntry?.reservePriceMappings ?? []) {
155
+ if (mapping.reservePriceId) {
156
+ addTrackedPriceIds(allPriceIds, mapping.reservePriceId)
157
+ }
158
+ }
159
+ }
160
+ }
161
+
162
+ return allPriceIds
163
+ }
164
+
64
165
  const getMintDecimals = async (connection: Connection, mint: PublicKey): Promise<number> => {
65
166
  const syntheticDecimals = getSyntheticMintDecimals(mint)
66
167
  if (syntheticDecimals !== null) {
@@ -76,6 +177,15 @@ const pushPriceMintAccount = (remainingAccounts: AccountMeta[], mint: PublicKey)
76
177
  remainingAccounts.push({ pubkey: mint, isSigner: false, isWritable: false })
77
178
  }
78
179
 
180
+ const isLocalRpcEndpoint = (rpcEndpoint: string): boolean => {
181
+ try {
182
+ const hostname = new URL(rpcEndpoint).hostname
183
+ return hostname === "127.0.0.1" || hostname === "localhost"
184
+ } catch {
185
+ return rpcEndpoint.includes("127.0.0.1") || rpcEndpoint.includes("localhost")
186
+ }
187
+ }
188
+
79
189
  // ============================================================================
80
190
  // Policy Types (matching on-chain structures)
81
191
  // ============================================================================
@@ -403,6 +513,11 @@ export interface UpdatePriceParams {
403
513
  priceId: number
404
514
  }
405
515
 
516
+ export interface UpdateStrategyVaultPricesOptions {
517
+ manager?: PublicKey
518
+ priceIds?: Iterable<number>
519
+ }
520
+
406
521
  export interface QueueWithdrawalParams {
407
522
  depositor: PublicKey
408
523
  lpAmount: Numeric
@@ -691,6 +806,29 @@ export interface WrapperManagerUpdatePositionParams {
691
806
  systemProgram?: PublicKey
692
807
  }
693
808
 
809
+ export interface WrapperAddKaminoObligationPositionParams {
810
+ manager: PublicKey
811
+ obligation: PublicKey
812
+ lendingProgramId: PublicKey
813
+ quotePriceId: exponentVaults.PriceId
814
+ reservePriceMappings: exponentVaults.ReservePriceMapping[]
815
+ reserveFarmMappings?: exponentVaults.ReserveFarmMapping[]
816
+ minPriceStatusFlags: number
817
+ remainingAccounts?: AccountMeta[]
818
+ exponentPrices?: PublicKey
819
+ systemProgram?: PublicKey
820
+ }
821
+
822
+ export interface WrapperUpsertKaminoObligationReservePriceMappingsParams {
823
+ manager: PublicKey
824
+ obligation: PublicKey
825
+ reservePriceMappings: exponentVaults.ReservePriceMapping[]
826
+ reserveFarmMappings?: exponentVaults.ReserveFarmMapping[]
827
+ remainingAccounts?: AccountMeta[]
828
+ exponentPrices?: PublicKey
829
+ systemProgram?: PublicKey
830
+ }
831
+
694
832
  export type WrapperAddPolicyParams = AddPolicyParams
695
833
 
696
834
  export type WrapperRemovePolicyParams = RemovePolicyParams
@@ -722,6 +860,8 @@ export class ExponentVault {
722
860
  public pda: ExponentVaultsPDA
723
861
  public aumCalculator: AumCalculator
724
862
  private clmmTicksMap: Map<string, PublicKey> = new Map()
863
+ private kaminoFarmAuxAccountsMap: Map<string, AccountMeta[]> = new Map()
864
+ private kaminoObligationFarmUserStates: Set<string> = new Set()
725
865
  static squadsVaultTxnResolver = new SquadsVaultTxnResolver()
726
866
 
727
867
  constructor(params: {
@@ -755,7 +895,10 @@ export class ExponentVault {
755
895
  connection: params.connection,
756
896
  programId: fetcher.programId,
757
897
  })
758
- await vault.populateClmmTicksCache(params.connection)
898
+ await Promise.all([
899
+ vault.populateClmmTicksCache(params.connection),
900
+ vault.populateKaminoFarmAuxAccountsCache(params.connection),
901
+ ])
759
902
  return vault
760
903
  }
761
904
 
@@ -776,7 +919,12 @@ export class ExponentVault {
776
919
  programId: fetcher.programId,
777
920
  }),
778
921
  )
779
- await Promise.all(results.map((v) => v.populateClmmTicksCache(params.connection)))
922
+ await Promise.all(
923
+ results.flatMap((vault) => [
924
+ vault.populateClmmTicksCache(params.connection),
925
+ vault.populateKaminoFarmAuxAccountsCache(params.connection),
926
+ ]),
927
+ )
780
928
  return results
781
929
  }
782
930
 
@@ -784,6 +932,8 @@ export class ExponentVault {
784
932
  const refreshed = await ExponentVault.load({ env: this.env, connection, address: this.selfAddress })
785
933
  this.state = refreshed.state
786
934
  this.clmmTicksMap = refreshed.clmmTicksMap
935
+ this.kaminoFarmAuxAccountsMap = refreshed.kaminoFarmAuxAccountsMap
936
+ this.kaminoObligationFarmUserStates = refreshed.kaminoObligationFarmUserStates
787
937
  this.connection = connection
788
938
  this.aumCalculator = new AumCalculator(this.state, this.connection, this.env)
789
939
  return refreshed
@@ -810,6 +960,128 @@ export class ExponentVault {
810
960
  }
811
961
  }
812
962
 
963
+ private async populateKaminoFarmAuxAccountsCache(connection: Connection): Promise<void> {
964
+ this.kaminoFarmAuxAccountsMap.clear()
965
+ this.kaminoObligationFarmUserStates.clear()
966
+
967
+ const farmStates: PublicKey[] = []
968
+ const delegatedFarmUsers: Array<{ obligation: PublicKey; farmState: PublicKey; userState: PublicKey }> = []
969
+ for (const pos of this.state.strategyPositions) {
970
+ if ("kaminoFarm" in pos && pos.kaminoFarm && typeof pos.kaminoFarm === "object") {
971
+ const entry = (pos as { kaminoFarm: { 0: { farmState: PublicKey } } }).kaminoFarm[0]
972
+ if (!farmStates.some((farmState) => farmState.equals(entry.farmState))) {
973
+ farmStates.push(entry.farmState)
974
+ }
975
+ continue
976
+ }
977
+
978
+ if (!("obligation" in pos) || !pos.obligation || typeof pos.obligation !== "object") {
979
+ continue
980
+ }
981
+
982
+ const rawObligation = pos["obligation"] as Record<string, unknown> | Array<unknown>
983
+ const inner = Array.isArray(rawObligation)
984
+ ? (rawObligation[0] as Record<string, unknown> | undefined)
985
+ : rawObligation && typeof rawObligation === "object" && "0" in rawObligation
986
+ ? (rawObligation[0] as Record<string, unknown> | undefined)
987
+ : (rawObligation as Record<string, unknown> | undefined)
988
+ const kaminoVariant = inner && "kaminoObligation" in inner ? (inner.kaminoObligation as unknown) : inner
989
+ const entry = Array.isArray(kaminoVariant)
990
+ ? (kaminoVariant[0] as
991
+ | {
992
+ obligation?: PublicKey
993
+ reserveFarmMappings?: Array<{ farmState: PublicKey }>
994
+ }
995
+ | undefined)
996
+ : kaminoVariant && typeof kaminoVariant === "object" && "0" in (kaminoVariant as Record<string, unknown>)
997
+ ? ((
998
+ kaminoVariant as {
999
+ 0?: {
1000
+ obligation?: PublicKey
1001
+ reserveFarmMappings?: Array<{ farmState: PublicKey }>
1002
+ }
1003
+ }
1004
+ )[0] ?? undefined)
1005
+ : (kaminoVariant as
1006
+ | {
1007
+ obligation?: PublicKey
1008
+ reserveFarmMappings?: Array<{ farmState: PublicKey }>
1009
+ }
1010
+ | undefined)
1011
+ if (!(entry?.obligation instanceof PublicKey)) {
1012
+ continue
1013
+ }
1014
+
1015
+ for (const mapping of entry.reserveFarmMappings ?? []) {
1016
+ if (!farmStates.some((farmState) => farmState.equals(mapping.farmState))) {
1017
+ farmStates.push(mapping.farmState)
1018
+ }
1019
+ delegatedFarmUsers.push({
1020
+ obligation: entry.obligation,
1021
+ farmState: mapping.farmState,
1022
+ userState: getKaminoFarmsObligationFarm(entry.obligation, mapping.farmState, KAMINO_FARMS_PROGRAM_ID),
1023
+ })
1024
+ }
1025
+ }
1026
+
1027
+ if (farmStates.length === 0) {
1028
+ return
1029
+ }
1030
+
1031
+ const farmInfos = await connection.getMultipleAccountsInfo(farmStates)
1032
+ for (const [index, farmInfo] of farmInfos.entries()) {
1033
+ const farmState = farmStates[index]
1034
+ if (!farmState || !farmInfo?.data) {
1035
+ if (ExponentVault.debugAum && farmState) {
1036
+ console.log("[VAULT_AUM] kaminoFarmAux: missing farm info", farmState.toBase58())
1037
+ }
1038
+ continue
1039
+ }
1040
+
1041
+ const farm = decodeKaminoFarmState(Buffer.from(farmInfo.data))
1042
+ const auxAccounts: AccountMeta[] = [{ pubkey: farm.globalConfig, isSigner: false, isWritable: false }]
1043
+ const scopePrices = getKaminoFarmScopePricesAddress(farm)
1044
+ if (scopePrices) {
1045
+ auxAccounts.push({ pubkey: scopePrices, isSigner: false, isWritable: false })
1046
+ }
1047
+
1048
+ this.kaminoFarmAuxAccountsMap.set(farmState.toBase58(), auxAccounts)
1049
+ if (ExponentVault.debugAum) {
1050
+ console.log(
1051
+ "[VAULT_AUM] kaminoFarmAux:",
1052
+ farmState.toBase58(),
1053
+ auxAccounts.map((account) => account.pubkey.toBase58()).join(", "),
1054
+ )
1055
+ }
1056
+ }
1057
+
1058
+ if (delegatedFarmUsers.length === 0) {
1059
+ return
1060
+ }
1061
+
1062
+ const uniqueUsers = Array.from(
1063
+ new Map(
1064
+ delegatedFarmUsers.map((entry) => [
1065
+ `${entry.obligation.toBase58()}:${entry.farmState.toBase58()}`,
1066
+ entry,
1067
+ ]),
1068
+ ).values(),
1069
+ )
1070
+ const userInfos = await connection.getMultipleAccountsInfo(uniqueUsers.map((entry) => entry.userState))
1071
+ for (const [index, userInfo] of userInfos.entries()) {
1072
+ if (!userInfo?.data) {
1073
+ continue
1074
+ }
1075
+
1076
+ const entry = uniqueUsers[index]
1077
+ if (!entry) {
1078
+ continue
1079
+ }
1080
+
1081
+ this.kaminoObligationFarmUserStates.add(`${entry.obligation.toBase58()}:${entry.farmState.toBase58()}`)
1082
+ }
1083
+ }
1084
+
813
1085
  /**
814
1086
  * Derive the PDA for a vault using its 8-byte seed identifier.
815
1087
  */
@@ -898,13 +1170,19 @@ export class ExponentVault {
898
1170
  }
899
1171
  }
900
1172
  } else if ("obligation" in pos && pos.obligation && typeof pos.obligation === "object") {
901
- const inner = pos["obligation"][0] as Record<string, unknown> | undefined
1173
+ const rawObligation = pos["obligation"] as Record<string, unknown> | Array<unknown>
1174
+ const inner = Array.isArray(rawObligation)
1175
+ ? (rawObligation[0] as Record<string, unknown> | undefined)
1176
+ : rawObligation && typeof rawObligation === "object" && "0" in rawObligation
1177
+ ? (rawObligation[0] as Record<string, unknown> | undefined)
1178
+ : (rawObligation as Record<string, unknown> | undefined)
902
1179
  const kaminoVariant = inner && "kaminoObligation" in inner ? (inner.kaminoObligation as unknown) : inner
903
1180
  const entry = Array.isArray(kaminoVariant)
904
1181
  ? (kaminoVariant[0] as
905
1182
  | {
906
1183
  obligation?: PublicKey
907
1184
  reservePriceMappings?: Array<{ reserve: PublicKey }>
1185
+ reserveFarmMappings?: Array<{ farmState: PublicKey }>
908
1186
  }
909
1187
  | undefined)
910
1188
  : kaminoVariant && typeof kaminoVariant === "object" && "0" in (kaminoVariant as Record<string, unknown>)
@@ -913,6 +1191,7 @@ export class ExponentVault {
913
1191
  0?: {
914
1192
  obligation?: PublicKey
915
1193
  reservePriceMappings?: Array<{ reserve: PublicKey }>
1194
+ reserveFarmMappings?: Array<{ farmState: PublicKey }>
916
1195
  }
917
1196
  }
918
1197
  )[0] ?? undefined)
@@ -920,6 +1199,7 @@ export class ExponentVault {
920
1199
  | {
921
1200
  obligation?: PublicKey
922
1201
  reservePriceMappings?: Array<{ reserve: PublicKey }>
1202
+ reserveFarmMappings?: Array<{ farmState: PublicKey }>
923
1203
  }
924
1204
  | undefined)
925
1205
  if (entry?.obligation instanceof PublicKey) {
@@ -935,11 +1215,30 @@ export class ExponentVault {
935
1215
  isWritable: false,
936
1216
  })
937
1217
  }
1218
+ for (const mapping of entry.reserveFarmMappings ?? []) {
1219
+ metas.push({
1220
+ pubkey: mapping.farmState,
1221
+ isSigner: false,
1222
+ isWritable: false,
1223
+ })
1224
+ const obligationFarmUserKey = `${entry.obligation.toBase58()}:${mapping.farmState.toBase58()}`
1225
+ if (this.kaminoObligationFarmUserStates.has(obligationFarmUserKey)) {
1226
+ metas.push({
1227
+ pubkey: getKaminoFarmsObligationFarm(entry.obligation, mapping.farmState, KAMINO_FARMS_PROGRAM_ID),
1228
+ isSigner: false,
1229
+ isWritable: false,
1230
+ })
1231
+ }
1232
+ metas.push(...(this.kaminoFarmAuxAccountsMap.get(mapping.farmState.toBase58()) ?? []))
1233
+ }
938
1234
  if (ExponentVault.debugAum) {
939
1235
  console.log("[VAULT_AUM] strategyPosition: Obligation", entry.obligation.toBase58())
940
1236
  for (const mapping of entry.reservePriceMappings ?? []) {
941
1237
  console.log("[VAULT_AUM] strategyPosition: Kamino reserve", mapping.reserve.toBase58())
942
1238
  }
1239
+ for (const mapping of entry.reserveFarmMappings ?? []) {
1240
+ console.log("[VAULT_AUM] strategyPosition: Kamino delegated farm", mapping.farmState.toBase58())
1241
+ }
943
1242
  }
944
1243
  }
945
1244
  } else if ("yieldPosition" in pos && pos.yieldPosition && typeof pos.yieldPosition === "object") {
@@ -990,6 +1289,31 @@ export class ExponentVault {
990
1289
  ticksKey ? ticksKey.toBase58() : "(ticks not cached)",
991
1290
  )
992
1291
  }
1292
+ } else if ("kaminoFarm" in pos && pos.kaminoFarm && typeof pos.kaminoFarm === "object") {
1293
+ const entry = (pos as { kaminoFarm: { 0: { farmState: PublicKey; userState: PublicKey } } }).kaminoFarm[0]
1294
+ metas.push({
1295
+ pubkey: entry.farmState,
1296
+ isSigner: false,
1297
+ isWritable: false,
1298
+ })
1299
+ metas.push({
1300
+ pubkey: entry.userState,
1301
+ isSigner: false,
1302
+ isWritable: false,
1303
+ })
1304
+ const auxAccounts = this.kaminoFarmAuxAccountsMap.get(entry.farmState.toBase58()) ?? []
1305
+ metas.push(...auxAccounts)
1306
+ if (ExponentVault.debugAum) {
1307
+ console.log(
1308
+ "[VAULT_AUM] strategyPosition: KaminoFarm",
1309
+ entry.farmState.toBase58(),
1310
+ entry.userState.toBase58(),
1311
+ )
1312
+ console.log(
1313
+ "[VAULT_AUM] strategyPosition: KaminoFarm aux",
1314
+ auxAccounts.map((account) => account.pubkey.toBase58()).join(", ") || "(none)",
1315
+ )
1316
+ }
993
1317
  } else if ("loopscaleStrategy" in pos && pos.loopscaleStrategy && typeof pos.loopscaleStrategy === "object") {
994
1318
  const entry = (pos as any).loopscaleStrategy[0]
995
1319
  if (entry?.strategy instanceof PublicKey) {
@@ -1044,6 +1368,59 @@ export class ExponentVault {
1044
1368
  return [...this.tokenEntryAccountMetas(), ...this.strategyPositionAccountMetas()]
1045
1369
  }
1046
1370
 
1371
+ /**
1372
+ * Returns the readonly account metas required to recalculate the vault's current tracked AUM.
1373
+ * Extra metas can be appended for pending positions or newly introduced Kamino reserves.
1374
+ */
1375
+ getAumRemainingAccountMetas(extraRemainingAccounts: AccountMeta[] = []): AccountMeta[] {
1376
+ return uniqueRemainingAccounts([...this.aumRemainingAccounts(), ...extraRemainingAccounts])
1377
+ }
1378
+
1379
+ private ixManagePriceUpdates({
1380
+ manager,
1381
+ updates,
1382
+ }: {
1383
+ manager?: PublicKey
1384
+ updates: Array<{
1385
+ priceId: number
1386
+ priceType: exponentVaults.PriceType
1387
+ priceInterfaceAccounts?: PublicKey
1388
+ }>
1389
+ }): TransactionInstruction {
1390
+ if (updates.length === 0) {
1391
+ throw new Error("ixManagePriceUpdates requires at least one update")
1392
+ }
1393
+
1394
+ const resolvedManager = manager ?? this.state.roles.manager[0]
1395
+ if (!resolvedManager) {
1396
+ throw new Error("Vault manager not found")
1397
+ }
1398
+
1399
+ const [exponentPrices] = this.pda.exponentPrices()
1400
+ const remainingAccounts: AccountMeta[] = []
1401
+ const actions: exponentVaults.UpdatePriceAction[] = updates.map((update) => {
1402
+ if (!update.priceInterfaceAccounts) {
1403
+ throw new Error(`priceInterfaceAccounts required for priceId ${update.priceId}`)
1404
+ }
1405
+
1406
+ remainingAccounts.push({ pubkey: update.priceInterfaceAccounts, isSigner: false, isWritable: false })
1407
+
1408
+ return {
1409
+ __kind: "UpdatePrice",
1410
+ priceId: BigInt(update.priceId),
1411
+ priceType: update.priceType,
1412
+ }
1413
+ })
1414
+
1415
+ const ix = exponentVaults.createManagePricesInstruction(
1416
+ { manager: resolvedManager, exponentPrices, systemProgram: SystemProgram.programId },
1417
+ { actions },
1418
+ this.programId,
1419
+ )
1420
+ ix.keys.push(...remainingAccounts)
1421
+ return ix
1422
+ }
1423
+
1047
1424
  ixUpdatePrice({
1048
1425
  priceInterfaceAccounts,
1049
1426
  pythPriceFeedId,
@@ -1062,8 +1439,12 @@ export class ExponentVault {
1062
1439
  const ix = exponentVaults.createUpdatePriceInstruction(
1063
1440
  { exponentPrices },
1064
1441
  { updates: [{ priceId: BigInt(priceId), interfaceAccountsIndex: 0 }] },
1442
+ this.programId,
1065
1443
  )
1066
1444
  ix.keys.push(...updateRemainingAccounts)
1445
+ if (isLocalRpcEndpoint(this.connection.rpcEndpoint)) {
1446
+ ix.data = Buffer.concat([LOCAL_UPDATE_PRICE_DISCRIMINATOR, ix.data.subarray(1)])
1447
+ }
1067
1448
  return ix
1068
1449
  }
1069
1450
 
@@ -1084,6 +1465,7 @@ export class ExponentVault {
1084
1465
  const vault = entryTokenVault ?? entry.tokenSquadsAccount
1085
1466
  const lpDst = tokenLpDst ?? defaultTokenAccount(this.state.mintLp, depositor, tokenProgram)
1086
1467
  const exponentPrices = this.pda.exponentPrices()[0]
1468
+ const tokenProgramLp = useLegacyTokenAccounts ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID
1087
1469
 
1088
1470
  const preIxs: TransactionInstruction[] = []
1089
1471
 
@@ -1139,7 +1521,7 @@ export class ExponentVault {
1139
1521
  tokenLpDst: lpDst,
1140
1522
  mintLp: this.state.mintLp,
1141
1523
  tokenProgram,
1142
- tokenProgramLp: TOKEN_PROGRAM_ID,
1524
+ tokenProgramLp,
1143
1525
  eventAuthority: this.eventAuthority,
1144
1526
  program: this.programId,
1145
1527
  },
@@ -1444,6 +1826,66 @@ export class ExponentVault {
1444
1826
  return ix
1445
1827
  }
1446
1828
 
1829
+ /**
1830
+ * Convenience wrapper for adding a tracked Kamino obligation position.
1831
+ */
1832
+ ixWrapperAddKaminoObligationPosition({
1833
+ manager,
1834
+ obligation,
1835
+ lendingProgramId,
1836
+ quotePriceId,
1837
+ reservePriceMappings,
1838
+ reserveFarmMappings = [],
1839
+ minPriceStatusFlags,
1840
+ remainingAccounts = [],
1841
+ exponentPrices = this.pda.exponentPrices()[0],
1842
+ systemProgram = SystemProgram.programId,
1843
+ }: WrapperAddKaminoObligationPositionParams): TransactionInstruction {
1844
+ const obligationEntry: exponentVaults.KaminoObligationEntry = {
1845
+ obligation,
1846
+ lendingProgramId,
1847
+ quotePriceId,
1848
+ reservePriceMappings,
1849
+ reserveFarmMappings,
1850
+ minPriceStatusFlags,
1851
+ }
1852
+
1853
+ return this.ixWrapperManagerUpdatePosition({
1854
+ manager,
1855
+ exponentPrices,
1856
+ systemProgram,
1857
+ remainingAccounts: this.getAumRemainingAccountMetas(remainingAccounts),
1858
+ update: exponentVaults.positionUpdate("AddKaminoObligationEntry", [
1859
+ obligationEntry,
1860
+ ] as [exponentVaults.KaminoObligationEntry]),
1861
+ })
1862
+ }
1863
+
1864
+ /**
1865
+ * Convenience wrapper for updating tracked reserve price mappings on a Kamino obligation.
1866
+ */
1867
+ ixWrapperUpsertKaminoObligationReservePriceMappings({
1868
+ manager,
1869
+ obligation,
1870
+ reservePriceMappings,
1871
+ reserveFarmMappings = [],
1872
+ remainingAccounts = [],
1873
+ exponentPrices = this.pda.exponentPrices()[0],
1874
+ systemProgram = SystemProgram.programId,
1875
+ }: WrapperUpsertKaminoObligationReservePriceMappingsParams): TransactionInstruction {
1876
+ return this.ixWrapperManagerUpdatePosition({
1877
+ manager,
1878
+ exponentPrices,
1879
+ systemProgram,
1880
+ remainingAccounts: this.getAumRemainingAccountMetas(remainingAccounts),
1881
+ update: exponentVaults.positionUpdate("UpsertKaminoObligationReservePriceMappings", {
1882
+ obligation,
1883
+ reservePriceMappings,
1884
+ reserveFarmMappings,
1885
+ }),
1886
+ })
1887
+ }
1888
+
1447
1889
  // ==========================================================================
1448
1890
  // Policy Management Methods
1449
1891
  // ==========================================================================
@@ -2013,11 +2455,9 @@ export class ExponentVault {
2013
2455
  throw new Error("Jito bundle simulation failed")
2014
2456
  }
2015
2457
 
2016
- const decoded = this.fetcher.program.account.exponentStrategyVault.coder.accounts.decode(
2017
- "exponentStrategyVault",
2458
+ const vaultStateAfterSimulations = deserializeExponentStrategyVaultFetcherShape(
2018
2459
  Buffer.from(postVaultAccountData, "base64"),
2019
- )
2020
- const vaultStateAfterSimulations = transformAnchorData(decoded) as ExponentVaultType
2460
+ ) as ExponentVaultType
2021
2461
  const aum =
2022
2462
  vaultStateAfterSimulations.financials.aumInBase + vaultStateAfterSimulations.financials.aumInBaseInPositions
2023
2463
 
@@ -2028,64 +2468,35 @@ export class ExponentVault {
2028
2468
  }
2029
2469
  }
2030
2470
 
2031
- async ixsUpdateStrategyVaultPrices(pricesAccount: ExponentPrices): Promise<TransactionInstruction[]> {
2032
- type PriceIdValue = { simple: { priceId: bigint } } | { multiply: { priceIds: bigint[] } }
2033
-
2034
- const extractPriceIds = (priceId: PriceIdValue): number[] => {
2035
- const ids = "simple" in priceId ? [Number(priceId.simple.priceId)] : priceId.multiply.priceIds.map(Number)
2036
- return ids.filter((id) => id !== 0) // skip passthrough (price_id 0)
2037
- }
2038
-
2039
- const allPriceIds = new Set<number>()
2471
+ async ixsUpdateStrategyVaultPrices(
2472
+ pricesAccount: ExponentPrices,
2473
+ options: UpdateStrategyVaultPricesOptions = {},
2474
+ ): Promise<TransactionInstruction[]> {
2475
+ const trackedPriceIds =
2476
+ options.priceIds ??
2477
+ collectTrackedStrategyVaultPriceIds({
2478
+ tokenEntries: this.state.tokenEntries,
2479
+ strategyPositions: this.state.strategyPositions,
2480
+ })
2040
2481
 
2041
- for (const entry of this.state.tokenEntries) {
2042
- extractPriceIds(entry.priceId).forEach((id) => allPriceIds.add(id))
2482
+ const priceIds = [...trackedPriceIds].sort((a, b) => a - b)
2483
+ if (priceIds.length === 0) {
2484
+ return []
2043
2485
  }
2044
2486
 
2045
- for (const position of this.state.strategyPositions) {
2046
- if ("orderbook" in position) {
2047
- const orderbook = position.orderbook[0]
2048
- extractPriceIds(orderbook.priceIdPt).forEach((id) => allPriceIds.add(id))
2049
- continue
2050
- }
2051
-
2052
- if ("tokenAccount" in position) {
2053
- for (const balance of position.tokenAccount[0].balances) {
2054
- extractPriceIds(balance.priceId).forEach((id) => allPriceIds.add(id))
2055
- }
2056
- continue
2057
- }
2487
+ return priceIds.map((priceId) => {
2488
+ const priceEntry = pricesAccount.prices[priceId]
2058
2489
 
2059
- if ("obligation" in position) {
2060
- const inner = position.obligation[0]
2061
- const kaminoEntry =
2062
- inner && typeof inner === "object" && "kaminoObligation" in inner ? inner.kaminoObligation?.[0] : inner?.[0]
2063
- if (kaminoEntry?.quotePriceId) {
2064
- extractPriceIds(kaminoEntry.quotePriceId).forEach((id: number) => allPriceIds.add(id))
2065
- }
2066
- for (const mapping of kaminoEntry?.reservePriceMappings ?? []) {
2067
- if (mapping.reservePriceId) {
2068
- extractPriceIds(mapping.reservePriceId).forEach((id: number) => allPriceIds.add(id))
2069
- }
2070
- }
2490
+ if (!priceEntry) {
2491
+ throw new Error(`Price entry not found for priceId ${priceId}`)
2071
2492
  }
2072
- }
2073
-
2074
- return Promise.all(
2075
- [...allPriceIds].map((priceId) => {
2076
- const priceEntry = pricesAccount.prices[priceId]
2077
-
2078
- if (!priceEntry) {
2079
- throw new Error(`Price entry not found for priceId ${priceId}`)
2080
- }
2081
2493
 
2082
- return this.ixUpdatePrice({
2083
- priceInterfaceAccounts: priceEntry.priceInterfaceAccounts,
2084
- remainingAccounts: priceEntry.interfaceAccounts,
2085
- priceId,
2086
- })
2087
- }),
2088
- )
2494
+ return this.ixUpdatePrice({
2495
+ priceId,
2496
+ priceInterfaceAccounts: priceEntry.priceInterfaceAccounts,
2497
+ remainingAccounts: priceEntry.interfaceAccounts,
2498
+ })
2499
+ })
2089
2500
  }
2090
2501
 
2091
2502
  // ==========================================================================