@exponent-labs/exponent-sdk 0.9.0 → 0.9.2

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 (155) 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 +21 -4
  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/positionUpdate.d.ts +9 -0
  14. package/build/client/vaults/types/positionUpdate.js +23 -0
  15. package/build/client/vaults/types/positionUpdate.js.map +1 -1
  16. package/build/client/vaults/types/proposalAction.js +0 -3
  17. package/build/client/vaults/types/proposalAction.js.map +1 -1
  18. package/build/client/vaults/types/reserveFarmMapping.d.ts +19 -0
  19. package/build/client/vaults/types/reserveFarmMapping.js +18 -0
  20. package/build/client/vaults/types/reserveFarmMapping.js.map +1 -0
  21. package/build/client/vaults/types/strategyPosition.d.ts +5 -0
  22. package/build/client/vaults/types/strategyPosition.js +5 -0
  23. package/build/client/vaults/types/strategyPosition.js.map +1 -1
  24. package/build/exponentVaults/aumCalculator.d.ts +25 -4
  25. package/build/exponentVaults/aumCalculator.js +236 -15
  26. package/build/exponentVaults/aumCalculator.js.map +1 -1
  27. package/build/exponentVaults/fetcher.d.ts +52 -0
  28. package/build/exponentVaults/fetcher.js +199 -0
  29. package/build/exponentVaults/fetcher.js.map +1 -0
  30. package/build/exponentVaults/index.d.ts +10 -9
  31. package/build/exponentVaults/index.js +26 -8
  32. package/build/exponentVaults/index.js.map +1 -1
  33. package/build/exponentVaults/kamino-farms.d.ts +144 -0
  34. package/build/exponentVaults/kamino-farms.js +396 -0
  35. package/build/exponentVaults/kamino-farms.js.map +1 -0
  36. package/build/exponentVaults/loopscale/client.d.ts +240 -0
  37. package/build/exponentVaults/loopscale/client.js +590 -0
  38. package/build/exponentVaults/loopscale/client.js.map +1 -0
  39. package/build/exponentVaults/loopscale/client.test.d.ts +1 -0
  40. package/build/exponentVaults/loopscale/client.test.js +183 -0
  41. package/build/exponentVaults/loopscale/client.test.js.map +1 -0
  42. package/build/exponentVaults/loopscale/helpers.d.ts +29 -0
  43. package/build/exponentVaults/loopscale/helpers.js +119 -0
  44. package/build/exponentVaults/loopscale/helpers.js.map +1 -0
  45. package/build/exponentVaults/loopscale/index.d.ts +3 -0
  46. package/build/exponentVaults/loopscale/index.js +12 -0
  47. package/build/exponentVaults/loopscale/index.js.map +1 -0
  48. package/build/exponentVaults/loopscale/prepared-transactions.d.ts +13 -0
  49. package/build/exponentVaults/loopscale/prepared-transactions.js +271 -0
  50. package/build/exponentVaults/loopscale/prepared-transactions.js.map +1 -0
  51. package/build/exponentVaults/loopscale/prepared-transactions.test.d.ts +1 -0
  52. package/build/exponentVaults/loopscale/prepared-transactions.test.js +400 -0
  53. package/build/exponentVaults/loopscale/prepared-transactions.test.js.map +1 -0
  54. package/build/exponentVaults/loopscale/prepared-types.d.ts +62 -0
  55. package/build/exponentVaults/loopscale/prepared-types.js +3 -0
  56. package/build/exponentVaults/loopscale/prepared-types.js.map +1 -0
  57. package/build/exponentVaults/loopscale/response-plan.d.ts +69 -0
  58. package/build/exponentVaults/loopscale/response-plan.js +141 -0
  59. package/build/exponentVaults/loopscale/response-plan.js.map +1 -0
  60. package/build/exponentVaults/loopscale/response-plan.test.d.ts +1 -0
  61. package/build/exponentVaults/loopscale/response-plan.test.js +139 -0
  62. package/build/exponentVaults/loopscale/response-plan.test.js.map +1 -0
  63. package/build/exponentVaults/loopscale/send-plan.d.ts +75 -0
  64. package/build/exponentVaults/loopscale/send-plan.js +235 -0
  65. package/build/exponentVaults/loopscale/send-plan.js.map +1 -0
  66. package/build/exponentVaults/loopscale/types.d.ts +443 -0
  67. package/build/exponentVaults/loopscale/types.js +3 -0
  68. package/build/exponentVaults/loopscale/types.js.map +1 -0
  69. package/build/exponentVaults/loopscale-client.d.ts +113 -524
  70. package/build/exponentVaults/loopscale-client.js +296 -539
  71. package/build/exponentVaults/loopscale-client.js.map +1 -1
  72. package/build/exponentVaults/loopscale-client.test.d.ts +1 -0
  73. package/build/exponentVaults/loopscale-client.test.js +162 -0
  74. package/build/exponentVaults/loopscale-client.test.js.map +1 -0
  75. package/build/exponentVaults/loopscale-client.types.d.ts +425 -0
  76. package/build/exponentVaults/loopscale-client.types.js +3 -0
  77. package/build/exponentVaults/loopscale-client.types.js.map +1 -0
  78. package/build/exponentVaults/loopscale-execution.d.ts +125 -0
  79. package/build/exponentVaults/loopscale-execution.js +341 -0
  80. package/build/exponentVaults/loopscale-execution.js.map +1 -0
  81. package/build/exponentVaults/loopscale-execution.test.d.ts +1 -0
  82. package/build/exponentVaults/loopscale-execution.test.js +139 -0
  83. package/build/exponentVaults/loopscale-execution.test.js.map +1 -0
  84. package/build/exponentVaults/loopscale-vault.d.ts +115 -0
  85. package/build/exponentVaults/loopscale-vault.js +275 -0
  86. package/build/exponentVaults/loopscale-vault.js.map +1 -0
  87. package/build/exponentVaults/loopscale-vault.test.d.ts +1 -0
  88. package/build/exponentVaults/loopscale-vault.test.js +102 -0
  89. package/build/exponentVaults/loopscale-vault.test.js.map +1 -0
  90. package/build/exponentVaults/policyBuilders.d.ts +62 -0
  91. package/build/exponentVaults/policyBuilders.js +119 -2
  92. package/build/exponentVaults/policyBuilders.js.map +1 -1
  93. package/build/exponentVaults/pricePathResolver.d.ts +45 -0
  94. package/build/exponentVaults/pricePathResolver.js +198 -0
  95. package/build/exponentVaults/pricePathResolver.js.map +1 -0
  96. package/build/exponentVaults/pricePathResolver.test.d.ts +1 -0
  97. package/build/exponentVaults/pricePathResolver.test.js +369 -0
  98. package/build/exponentVaults/pricePathResolver.test.js.map +1 -0
  99. package/build/exponentVaults/syncTransaction.js +4 -1
  100. package/build/exponentVaults/syncTransaction.js.map +1 -1
  101. package/build/exponentVaults/titan-quote.js +170 -36
  102. package/build/exponentVaults/titan-quote.js.map +1 -1
  103. package/build/exponentVaults/vault-instruction-types.d.ts +363 -0
  104. package/build/exponentVaults/vault-instruction-types.js +128 -0
  105. package/build/exponentVaults/vault-instruction-types.js.map +1 -0
  106. package/build/exponentVaults/vault-interaction.d.ts +203 -343
  107. package/build/exponentVaults/vault-interaction.js +1894 -426
  108. package/build/exponentVaults/vault-interaction.js.map +1 -1
  109. package/build/exponentVaults/vault-interaction.kamino-vault.test.d.ts +1 -0
  110. package/build/exponentVaults/vault-interaction.kamino-vault.test.js +143 -0
  111. package/build/exponentVaults/vault-interaction.kamino-vault.test.js.map +1 -0
  112. package/build/exponentVaults/vault.d.ts +51 -2
  113. package/build/exponentVaults/vault.js +324 -48
  114. package/build/exponentVaults/vault.js.map +1 -1
  115. package/build/exponentVaults/vaultTransactionBuilder.d.ts +100 -134
  116. package/build/exponentVaults/vaultTransactionBuilder.js +383 -285
  117. package/build/exponentVaults/vaultTransactionBuilder.js.map +1 -1
  118. package/build/exponentVaults/vaultTransactionBuilder.test.d.ts +1 -0
  119. package/build/exponentVaults/vaultTransactionBuilder.test.js +297 -0
  120. package/build/exponentVaults/vaultTransactionBuilder.test.js.map +1 -0
  121. package/build/marketThree.d.ts +6 -2
  122. package/build/marketThree.js +10 -8
  123. package/build/marketThree.js.map +1 -1
  124. package/package.json +34 -32
  125. package/src/client/vaults/index.ts +2 -0
  126. package/src/client/vaults/types/index.ts +2 -0
  127. package/src/client/vaults/types/kaminoFarmEntry.ts +32 -0
  128. package/src/client/vaults/types/kaminoObligationEntry.ts +6 -3
  129. package/src/client/vaults/types/positionUpdate.ts +62 -0
  130. package/src/client/vaults/types/proposalAction.ts +0 -3
  131. package/src/client/vaults/types/reserveFarmMapping.ts +35 -0
  132. package/src/client/vaults/types/strategyPosition.ts +18 -1
  133. package/src/exponentVaults/aumCalculator.ts +353 -16
  134. package/src/exponentVaults/fetcher.ts +257 -0
  135. package/src/exponentVaults/index.ts +65 -40
  136. package/src/exponentVaults/kamino-farms.ts +538 -0
  137. package/src/exponentVaults/loopscale/client.ts +808 -0
  138. package/src/exponentVaults/loopscale/helpers.ts +172 -0
  139. package/src/exponentVaults/loopscale/index.ts +57 -0
  140. package/src/exponentVaults/loopscale/prepared-transactions.ts +435 -0
  141. package/src/exponentVaults/loopscale/prepared-types.ts +73 -0
  142. package/src/exponentVaults/loopscale/types.ts +466 -0
  143. package/src/exponentVaults/policyBuilders.ts +170 -0
  144. package/src/exponentVaults/pricePathResolver.test.ts +466 -0
  145. package/src/exponentVaults/pricePathResolver.ts +273 -0
  146. package/src/exponentVaults/syncTransaction.ts +6 -1
  147. package/src/exponentVaults/titan-quote.ts +231 -45
  148. package/src/exponentVaults/vault-instruction-types.ts +493 -0
  149. package/src/exponentVaults/vault-interaction.kamino-vault.test.ts +149 -0
  150. package/src/exponentVaults/vault-interaction.ts +2818 -799
  151. package/src/exponentVaults/vault.ts +474 -63
  152. package/src/exponentVaults/vaultTransactionBuilder.test.ts +349 -0
  153. package/src/exponentVaults/vaultTransactionBuilder.ts +581 -433
  154. package/src/marketThree.ts +14 -6
  155. package/src/exponentVaults/loopscale-client.ts +0 -1373
@@ -26,14 +26,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.SwapDirection = exports.clmmAction = exports.loopscaleAction = exports.titanAction = exports.createOrderbookSyncTransaction = exports.coreAction = exports.syAction = exports.orderbookAction = exports.createVaultSyncTransaction = exports.kaminoAction = exports.ClmmAction = exports.LoopscaleAction = exports.TitanAction = exports.SyAction = exports.CoreAction = exports.OrderbookAction = exports.OrderbookTradeDirection = exports.VaultAction = void 0;
29
+ exports.SwapDirection = exports.clmmAction = exports.loopscaleAction = exports.titanAction = exports.createOrderbookSyncTransaction = exports.coreAction = exports.syAction = exports.orderbookAction = exports.__kaminoVaultTesting = exports.buildSetupStatePriceRefreshInstructions = exports.createStrategySetupContext = exports.createVaultSyncTransaction = exports.createVaultSyncTransactions = exports.kaminoFarmAction = exports.kaminoVaultAction = exports.kaminoAction = exports.ClmmAction = exports.LoopscaleAction = exports.TitanAction = exports.SyAction = exports.CoreAction = exports.OrderbookAction = exports.OrderbookTradeDirection = exports.KaminoFarmAction = exports.KaminoVaultAction = exports.VaultAction = void 0;
30
+ const anchor_1 = require("@coral-xyz/anchor");
30
31
  const kamino_reserve_deserializer_1 = require("@exponent-labs/kamino-reserve-deserializer");
32
+ const exponent_fetcher_1 = require("@exponent-labs/exponent-fetcher");
33
+ const kamino_vault_idl_1 = require("@exponent-labs/kamino-vault-idl");
31
34
  const kamino_markets_1 = require("./kamino-markets");
32
35
  const decimal_js_1 = __importDefault(require("decimal.js"));
33
36
  const constants_1 = require("./../../../kamino-lend-standard/src/constants");
34
37
  const policyBuilders_1 = require("./policyBuilders");
35
38
  const accounts_1 = require("@exponent-labs/klend-idl/accounts");
36
- const precise_number_1 = require("@exponent-labs/precise-number");
37
39
  const orderbook_1 = require("../orderbook/orderbook");
38
40
  const marketThree_1 = require("../marketThree");
39
41
  const utils_1 = require("../utils");
@@ -44,8 +46,11 @@ const exponentVaults = __importStar(require("../client/vaults"));
44
46
  const orderbook_2 = require("../client/orderbook");
45
47
  const environment_1 = require("../environment");
46
48
  const vault_1 = require("../vault");
47
- const ytPosition_1 = require("../ytPosition");
48
49
  const vault_2 = require("./vault");
50
+ const ytPosition_1 = require("../ytPosition");
51
+ const vault_3 = require("./vault");
52
+ const kamino_farms_1 = require("./kamino-farms");
53
+ const pricePathResolver_1 = require("./pricePathResolver");
49
54
  const instructions_1 = require("@exponent-labs/klend-idl/instructions");
50
55
  const spl_token_1 = require("@solana/spl-token");
51
56
  const syncTransaction_1 = require("./syncTransaction");
@@ -54,109 +59,28 @@ const scope_refresh_1 = require("./scope-refresh");
54
59
  const web3_js_1 = require("@solana/web3.js");
55
60
  const bn_js_1 = __importDefault(require("bn.js"));
56
61
  const KAMINO_FARMS_PROGRAM_ID = new web3_js_1.PublicKey("FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr");
57
- /** Actions that can be performed through the vault instruction builder. */
58
- var VaultAction;
59
- (function (VaultAction) {
60
- VaultAction["INIT_USER_METADATA"] = "INIT_USER_METADATA";
61
- VaultAction["INIT_OBLIGATION"] = "INIT_OBLIGATION";
62
- VaultAction["DEPOSIT"] = "DEPOSIT";
63
- VaultAction["WITHDRAW"] = "WITHDRAW";
64
- VaultAction["BORROW"] = "BORROW";
65
- VaultAction["REPAY"] = "REPAY";
66
- })(VaultAction || (exports.VaultAction = VaultAction = {}));
67
- // ============================================================================
68
- // Orderbook Instruction Types
69
- // ============================================================================
70
- /** Orderbook trade direction */
71
- var OrderbookTradeDirection;
72
- (function (OrderbookTradeDirection) {
73
- OrderbookTradeDirection["BUY_PT"] = "BUY_PT";
74
- OrderbookTradeDirection["SELL_PT"] = "SELL_PT";
75
- OrderbookTradeDirection["BUY_YT"] = "BUY_YT";
76
- OrderbookTradeDirection["SELL_YT"] = "SELL_YT";
77
- })(OrderbookTradeDirection || (exports.OrderbookTradeDirection = OrderbookTradeDirection = {}));
78
- /** Actions that can be performed on the Exponent Orderbook */
79
- var OrderbookAction;
80
- (function (OrderbookAction) {
81
- OrderbookAction["POST_OFFER"] = "POST_OFFER";
82
- OrderbookAction["MARKET_OFFER"] = "MARKET_OFFER";
83
- OrderbookAction["REMOVE_OFFER"] = "REMOVE_OFFER";
84
- OrderbookAction["WITHDRAW_FUNDS"] = "WITHDRAW_FUNDS";
85
- })(OrderbookAction || (exports.OrderbookAction = OrderbookAction = {}));
86
- // ============================================================================
87
- // Core Instruction Types (Strip/Merge)
88
- // ============================================================================
89
- /** Actions that can be performed on Exponent Core */
90
- var CoreAction;
91
- (function (CoreAction) {
92
- CoreAction["STRIP"] = "STRIP";
93
- CoreAction["MERGE"] = "MERGE";
94
- CoreAction["WITHDRAW_YT"] = "WITHDRAW_YT";
95
- CoreAction["DEPOSIT_YT"] = "DEPOSIT_YT";
96
- CoreAction["INITIALIZE_YIELD_POSITION"] = "INITIALIZE_YIELD_POSITION";
97
- })(CoreAction || (exports.CoreAction = CoreAction = {}));
98
- // ============================================================================
99
- // Standard Program Instruction Types (mint_sy / redeem_sy)
100
- // ============================================================================
101
- var SyAction;
102
- (function (SyAction) {
103
- SyAction["MINT"] = "MINT_SY";
104
- SyAction["REDEEM"] = "REDEEM_SY";
105
- })(SyAction || (exports.SyAction = SyAction = {}));
106
- // ============================================================================
107
- // Titan Instruction Types
108
- // ============================================================================
109
- var TitanAction;
110
- (function (TitanAction) {
111
- TitanAction["SWAP"] = "SWAP";
112
- })(TitanAction || (exports.TitanAction = TitanAction = {}));
113
- // ============================================================================
114
- // Loopscale Instruction Types
115
- // ============================================================================
116
- /** Actions for Loopscale interactions (loans = borrower side, strategies = lender side). */
117
- var LoopscaleAction;
118
- (function (LoopscaleAction) {
119
- LoopscaleAction["CREATE_LOAN"] = "LOOPSCALE_CREATE_LOAN";
120
- LoopscaleAction["DEPOSIT_COLLATERAL"] = "LOOPSCALE_DEPOSIT_COLLATERAL";
121
- LoopscaleAction["BORROW_PRINCIPAL"] = "LOOPSCALE_BORROW_PRINCIPAL";
122
- LoopscaleAction["REPAY_PRINCIPAL"] = "LOOPSCALE_REPAY_PRINCIPAL";
123
- LoopscaleAction["WITHDRAW_COLLATERAL"] = "LOOPSCALE_WITHDRAW_COLLATERAL";
124
- LoopscaleAction["CLOSE_LOAN"] = "LOOPSCALE_CLOSE_LOAN";
125
- LoopscaleAction["UPDATE_WEIGHT_MATRIX"] = "LOOPSCALE_UPDATE_WEIGHT_MATRIX";
126
- LoopscaleAction["CREATE_STRATEGY"] = "LOOPSCALE_CREATE_STRATEGY";
127
- LoopscaleAction["DEPOSIT_STRATEGY"] = "LOOPSCALE_DEPOSIT_STRATEGY";
128
- LoopscaleAction["WITHDRAW_STRATEGY"] = "LOOPSCALE_WITHDRAW_STRATEGY";
129
- LoopscaleAction["CLOSE_STRATEGY"] = "LOOPSCALE_CLOSE_STRATEGY";
130
- LoopscaleAction["UPDATE_STRATEGY"] = "LOOPSCALE_UPDATE_STRATEGY";
131
- LoopscaleAction["LOCK_LOAN"] = "LOOPSCALE_LOCK_LOAN";
132
- LoopscaleAction["UNLOCK_LOAN"] = "LOOPSCALE_UNLOCK_LOAN";
133
- LoopscaleAction["REFINANCE_LEDGER"] = "LOOPSCALE_REFINANCE_LEDGER";
134
- })(LoopscaleAction || (exports.LoopscaleAction = LoopscaleAction = {}));
62
+ const KAMINO_VAULT_PRICE_TYPE_WIRE = 14;
63
+ const KAMINO_VAULT_ACCOUNT_DISCRIMINATOR_LEN = 8;
64
+ const KAMINO_VAULT_ALLOCATION_STRATEGY_OFFSET = 304;
65
+ const KAMINO_VAULT_ALLOCATION_SIZE = 2160;
66
+ const KAMINO_VAULT_ALLOCATION_CTOKEN_VAULT_OFFSET = 32;
67
+ const KAMINO_VAULT_GLOBAL_CONFIG_SEED = Buffer.from("global_config");
68
+ const KAMINO_VAULT_CODER = new anchor_1.BorshCoder(kamino_vault_idl_1.IDL);
135
69
  // ============================================================================
136
- // CLMM Instruction Types
70
+ // Vault Instruction Types (re-exported from vault-instruction-types.ts)
137
71
  // ============================================================================
138
- /** Actions that can be performed on the Exponent CLMM (Concentrated Liquidity Market Maker). */
139
- var ClmmAction;
140
- (function (ClmmAction) {
141
- /** Create a new LP position with a specified tick range. Generates keypair internally. */
142
- ClmmAction["DEPOSIT_LIQUIDITY"] = "CLMM_DEPOSIT_LIQUIDITY";
143
- /** Add more liquidity to an existing LP position. */
144
- ClmmAction["ADD_LIQUIDITY"] = "CLMM_ADD_LIQUIDITY";
145
- /** Remove liquidity from an LP position and receive PT + SY. */
146
- ClmmAction["WITHDRAW_LIQUIDITY"] = "CLMM_WITHDRAW_LIQUIDITY";
147
- /** Low-level PT/SY swap. Prefer buyPt/sellPt for directional trades. */
148
- ClmmAction["TRADE_PT"] = "CLMM_TRADE_PT";
149
- /** Buy PT with SY on the CLMM. */
150
- ClmmAction["BUY_PT"] = "CLMM_BUY_PT";
151
- /** Sell PT for SY on the CLMM. */
152
- ClmmAction["SELL_PT"] = "CLMM_SELL_PT";
153
- /** Buy YT with SY on the CLMM. */
154
- ClmmAction["BUY_YT"] = "CLMM_BUY_YT";
155
- /** Sell YT for SY on the CLMM. */
156
- ClmmAction["SELL_YT"] = "CLMM_SELL_YT";
157
- /** Claim farm emissions from an LP position. */
158
- ClmmAction["CLAIM_FARM_EMISSION"] = "CLMM_CLAIM_FARM_EMISSION";
159
- })(ClmmAction || (exports.ClmmAction = ClmmAction = {}));
72
+ var vault_instruction_types_1 = require("./vault-instruction-types");
73
+ Object.defineProperty(exports, "VaultAction", { enumerable: true, get: function () { return vault_instruction_types_1.VaultAction; } });
74
+ Object.defineProperty(exports, "KaminoVaultAction", { enumerable: true, get: function () { return vault_instruction_types_1.KaminoVaultAction; } });
75
+ Object.defineProperty(exports, "KaminoFarmAction", { enumerable: true, get: function () { return vault_instruction_types_1.KaminoFarmAction; } });
76
+ Object.defineProperty(exports, "OrderbookTradeDirection", { enumerable: true, get: function () { return vault_instruction_types_1.OrderbookTradeDirection; } });
77
+ Object.defineProperty(exports, "OrderbookAction", { enumerable: true, get: function () { return vault_instruction_types_1.OrderbookAction; } });
78
+ Object.defineProperty(exports, "CoreAction", { enumerable: true, get: function () { return vault_instruction_types_1.CoreAction; } });
79
+ Object.defineProperty(exports, "SyAction", { enumerable: true, get: function () { return vault_instruction_types_1.SyAction; } });
80
+ Object.defineProperty(exports, "TitanAction", { enumerable: true, get: function () { return vault_instruction_types_1.TitanAction; } });
81
+ Object.defineProperty(exports, "LoopscaleAction", { enumerable: true, get: function () { return vault_instruction_types_1.LoopscaleAction; } });
82
+ Object.defineProperty(exports, "ClmmAction", { enumerable: true, get: function () { return vault_instruction_types_1.ClmmAction; } });
83
+ const vault_instruction_types_2 = require("./vault-instruction-types");
160
84
  // ============================================================================
161
85
  // Kamino Action Builders
162
86
  // ============================================================================
@@ -193,15 +117,16 @@ exports.kaminoAction = {
193
117
  * @param market - The Kamino lending market
194
118
  */
195
119
  initUserMetadata(market) {
196
- return { action: VaultAction.INIT_USER_METADATA, market };
120
+ return { action: vault_instruction_types_2.VaultAction.INIT_USER_METADATA, market };
197
121
  },
198
122
  /**
199
123
  * Initialize a Kamino obligation for a market.
200
- * No-ops if the account already exists on-chain.
124
+ * When `autoManagePositions` is enabled, the SDK also registers the new
125
+ * obligation as a tracked strategy position after the init succeeds.
201
126
  * @param market - The Kamino lending market
202
127
  */
203
128
  initObligation(market) {
204
- return { action: VaultAction.INIT_OBLIGATION, market };
129
+ return { action: vault_instruction_types_2.VaultAction.INIT_OBLIGATION, market };
205
130
  },
206
131
  /**
207
132
  * Deposit collateral into a Kamino reserve.
@@ -211,7 +136,7 @@ exports.kaminoAction = {
211
136
  * @param amount - Amount in the asset's native units
212
137
  */
213
138
  deposit(market, asset, amount) {
214
- return { action: VaultAction.DEPOSIT, market, asset: asset, amount };
139
+ return { action: vault_instruction_types_2.VaultAction.DEPOSIT, market, asset: asset, amount };
215
140
  },
216
141
  /**
217
142
  * Withdraw collateral from a Kamino reserve.
@@ -220,7 +145,7 @@ exports.kaminoAction = {
220
145
  * @param amount - Amount in the asset's native units
221
146
  */
222
147
  withdraw(market, asset, amount) {
223
- return { action: VaultAction.WITHDRAW, market, asset: asset, amount };
148
+ return { action: vault_instruction_types_2.VaultAction.WITHDRAW, market, asset: asset, amount };
224
149
  },
225
150
  /**
226
151
  * Borrow from a Kamino reserve.
@@ -229,7 +154,7 @@ exports.kaminoAction = {
229
154
  * @param amount - Amount in the asset's native units
230
155
  */
231
156
  borrow(market, asset, amount) {
232
- return { action: VaultAction.BORROW, market, asset: asset, amount };
157
+ return { action: vault_instruction_types_2.VaultAction.BORROW, market, asset: asset, amount };
233
158
  },
234
159
  /**
235
160
  * Repay a borrow on a Kamino reserve.
@@ -238,11 +163,77 @@ exports.kaminoAction = {
238
163
  * @param amount - Amount in the asset's native units
239
164
  */
240
165
  repay(market, asset, amount) {
241
- return { action: VaultAction.REPAY, market, asset: asset, amount };
166
+ return { action: vault_instruction_types_2.VaultAction.REPAY, market, asset: asset, amount };
167
+ },
168
+ };
169
+ /**
170
+ * Builder for direct Kamino Vault action descriptors.
171
+ *
172
+ * These actions move vault-owned tokens into a Kamino Vault and, when
173
+ * `autoManagePositions` is enabled, automatically track the resulting share
174
+ * token account as a managed strategy position.
175
+ */
176
+ exports.kaminoVaultAction = {
177
+ /**
178
+ * Deposit the vault-owned token account into a Kamino Vault.
179
+ * @param params.vault - Kamino Vault address
180
+ * @param params.amount - Amount of deposit tokens to move into the vault
181
+ */
182
+ deposit(params) {
183
+ return { action: vault_instruction_types_2.KaminoVaultAction.DEPOSIT, ...params };
184
+ },
185
+ /**
186
+ * Withdraw Kamino Vault shares back into the vault-owned token account.
187
+ * `reserve` is an optional override. When omitted, the SDK plans the
188
+ * withdraw across the vault's active reserves automatically.
189
+ */
190
+ withdraw(params) {
191
+ return { action: vault_instruction_types_2.KaminoVaultAction.WITHDRAW, ...params };
242
192
  },
243
193
  };
244
194
  /**
245
- * Build vault instructions and wrap them in a Squads sync transaction.
195
+ * Builder for direct Kamino Farm action descriptors.
196
+ *
197
+ * These actions operate on a farm `user_state` derived from the managed vault
198
+ * owner by default. Pass `delegatee` when targeting a delegated farm user
199
+ * state, such as a Kamino obligation-owned farm entry.
200
+ */
201
+ exports.kaminoFarmAction = {
202
+ /** Initialize the farm `user_state` PDA. */
203
+ initializeUser(params) {
204
+ return { action: vault_instruction_types_2.KaminoFarmAction.INITIALIZE_USER, ...params };
205
+ },
206
+ /** Stake the managed vault's token ATA into the farm. */
207
+ stake(params) {
208
+ return { action: vault_instruction_types_2.KaminoFarmAction.STAKE, ...params };
209
+ },
210
+ /** Unstake a scaled share amount from the farm. */
211
+ unstake(params) {
212
+ return { action: vault_instruction_types_2.KaminoFarmAction.UNSTAKE, ...params };
213
+ },
214
+ /** Withdraw matured unstaked deposits back into the managed vault ATA. */
215
+ withdrawUnstakedDeposits(params) {
216
+ return { action: vault_instruction_types_2.KaminoFarmAction.WITHDRAW_UNSTAKED_DEPOSITS, ...params };
217
+ },
218
+ /** Harvest a specific reward index into a managed vault reward ATA. */
219
+ harvestReward(params) {
220
+ return { action: vault_instruction_types_2.KaminoFarmAction.HARVEST_REWARD, ...params };
221
+ },
222
+ };
223
+ /**
224
+ * Build one or more vault sync transactions from high-level instruction descriptors.
225
+ *
226
+ * This is the plural companion to {@link createVaultSyncTransaction}. Most
227
+ * calls return a single result. Smart Kamino Vault withdraws can expand into
228
+ * multiple reserve-specific sync transactions when the wrapper needs to split
229
+ * an oversized withdraw across sequential chunks.
230
+ */
231
+ async function createVaultSyncTransactions(params) {
232
+ return buildVaultSyncTransactionResults(params, true);
233
+ }
234
+ exports.createVaultSyncTransactions = createVaultSyncTransactions;
235
+ /**
236
+ * Build exactly one vault sync transaction and return its wrapped instruction set.
246
237
  *
247
238
  * Takes high-level `VaultInstruction` descriptors (built with `kamino.*`),
248
239
  * resolves them to raw Solana instructions, then separates them:
@@ -252,28 +243,118 @@ exports.kaminoAction = {
252
243
  * KLend's `check_refresh` requires refreshReserve to be a top-level instruction
253
244
  * so it can be found via the instruction sysvar.
254
245
  *
255
- * @returns `{ setupInstructions, preInstructions, instruction, postInstructions, signers, addressLookupTableAddresses }`
256
- *
257
- * @example
258
- * ```ts
259
- * const { setupInstructions, preInstructions, instruction, postInstructions, signers, addressLookupTableAddresses } = await createVaultSyncTransaction({
260
- * instructions: [
261
- * kamino.initUserMetadata(KaminoMarket.MAIN),
262
- * kamino.initObligation(KaminoMarket.MAIN),
263
- * kamino.deposit(KaminoMarket.MAIN, "USDC", new BN(1_000_000)),
264
- * ],
265
- * owner: vaultPda,
266
- * connection,
267
- * policyPda,
268
- * vaultPda,
269
- * signer: wallet.publicKey,
270
- * vaultAddress: VAULT_ADDRESS,
271
- * })
272
- * // Send: [...setupInstructions, ...preInstructions, instruction, ...postInstructions]
273
- * ```
246
+ * When a smart Kamino Vault withdraw expands beyond one sync transaction, this
247
+ * singular helper throws and asks the caller to use
248
+ * {@link createVaultSyncTransactions} or {@link VaultTransactionBuilder}.
274
249
  */
275
- async function createVaultSyncTransaction({ instructions, owner, connection, policyPda, vaultPda, signer, accountIndex = 0, constraintIndices, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram = syncTransaction_1.SQUADS_PROGRAM_ID, }) {
276
- const { setupInstructions, syncInstructions, preInstructions, postInstructions, signers, addressLookupTableAddresses } = await buildVaultInstructions(instructions, owner, connection, signer, policyPda, vaultPda, accountIndex, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram);
250
+ async function createVaultSyncTransaction(params) {
251
+ const results = await buildVaultSyncTransactionResults(params, false);
252
+ return results[0];
253
+ }
254
+ exports.createVaultSyncTransaction = createVaultSyncTransaction;
255
+ async function buildVaultSyncTransactionResults({ instructions, owner, connection, policyPda, vaultPda, signer, accountIndex = 0, constraintIndices, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram = syncTransaction_1.SQUADS_PROGRAM_ID, autoManagePositions = false, setupContext, }, splitOversizedKaminoVaultWithdraw) {
256
+ vaultPda ??= owner;
257
+ const resolvedSetupContext = setupContext ?? createStrategySetupContext({
258
+ connection,
259
+ env: environment_1.LOCAL_ENV,
260
+ owner,
261
+ signer,
262
+ vaultAddress,
263
+ policyPda,
264
+ vaultPda,
265
+ accountIndex,
266
+ squadsProgram,
267
+ leadingAccounts,
268
+ preHookAccounts,
269
+ postHookAccounts,
270
+ autoManagePositions,
271
+ });
272
+ const buckets = await buildVaultInstructions(instructions, owner, connection, signer, policyPda, vaultPda, accountIndex, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram, autoManagePositions, resolvedSetupContext);
273
+ const setupStatePriceRefreshInstructions = setupContext
274
+ ? []
275
+ : await buildSetupStatePriceRefreshInstructions(resolvedSetupContext);
276
+ const fullResult = await buildWrappedVaultSyncTransactionResult({
277
+ connection,
278
+ policyPda,
279
+ vaultPda,
280
+ signer,
281
+ accountIndex,
282
+ constraintIndices,
283
+ vaultAddress,
284
+ leadingAccounts,
285
+ preHookAccounts,
286
+ postHookAccounts,
287
+ squadsProgram,
288
+ syncInstructions: buckets.syncInstructions,
289
+ setupInstructions: buckets.setupInstructions,
290
+ preInstructions: [...setupStatePriceRefreshInstructions, ...buckets.preInstructions],
291
+ postInstructions: buckets.postInstructions,
292
+ signers: buckets.signers,
293
+ addressLookupTableAddresses: buckets.addressLookupTableAddresses,
294
+ });
295
+ const isPlannerExpandedKaminoVaultWithdraw = instructions.length === 1
296
+ && instructions[0]?.action === vault_instruction_types_2.KaminoVaultAction.WITHDRAW
297
+ && !instructions[0].reserve
298
+ && buckets.syncInstructions.length > 1;
299
+ if (!isPlannerExpandedKaminoVaultWithdraw) {
300
+ return [fullResult];
301
+ }
302
+ if (await vaultSyncResultFitsInSingleTransaction({
303
+ connection,
304
+ payer: signer,
305
+ result: fullResult,
306
+ })) {
307
+ return [fullResult];
308
+ }
309
+ if (!splitOversizedKaminoVaultWithdraw) {
310
+ throw new Error("Kamino Vault withdraw expands beyond one sync transaction; use createVaultSyncTransactions() or VaultTransactionBuilder.");
311
+ }
312
+ if (buckets.preInstructions.length > 0 || buckets.postInstructions.length > 0) {
313
+ throw new Error("Kamino Vault withdraw chunking only supports sync-only actions; use explicit reserve overrides if you need custom refresh handling.");
314
+ }
315
+ const splitResults = [];
316
+ for (const [index, syncInstruction] of buckets.syncInstructions.entries()) {
317
+ const chunkConstraintIndices = constraintIndices
318
+ ? constraintIndices.length === buckets.syncInstructions.length
319
+ ? [constraintIndices[index]]
320
+ : constraintIndices.length === 1
321
+ ? constraintIndices
322
+ : (() => {
323
+ throw new Error("constraintIndices must contain either one entry or one entry per generated sync instruction when splitting a Kamino Vault withdraw.");
324
+ })()
325
+ : undefined;
326
+ const chunkResult = await buildWrappedVaultSyncTransactionResult({
327
+ connection,
328
+ policyPda,
329
+ vaultPda,
330
+ signer,
331
+ accountIndex,
332
+ constraintIndices: chunkConstraintIndices,
333
+ vaultAddress,
334
+ leadingAccounts,
335
+ preHookAccounts,
336
+ postHookAccounts,
337
+ squadsProgram,
338
+ syncInstructions: [syncInstruction],
339
+ setupInstructions: index === 0 ? buckets.setupInstructions : [],
340
+ preInstructions: index === 0 ? [...setupStatePriceRefreshInstructions] : [],
341
+ postInstructions: [],
342
+ signers: buckets.signers,
343
+ addressLookupTableAddresses: buckets.addressLookupTableAddresses,
344
+ });
345
+ const chunkFits = await vaultSyncResultFitsInSingleTransaction({
346
+ connection,
347
+ payer: signer,
348
+ result: chunkResult,
349
+ });
350
+ if (!chunkFits) {
351
+ throw new Error(`Kamino Vault withdraw chunk ${index + 1} still exceeds one sync transaction; use explicit reserve overrides or smaller steps.`);
352
+ }
353
+ splitResults.push(chunkResult);
354
+ }
355
+ return splitResults;
356
+ }
357
+ async function buildWrappedVaultSyncTransactionResult({ connection, policyPda, vaultPda, signer, accountIndex, constraintIndices, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram, syncInstructions, setupInstructions, preInstructions, postInstructions, signers, addressLookupTableAddresses, }) {
277
358
  let resolvedPolicyPda = policyPda;
278
359
  let resolvedConstraintIndices = constraintIndices;
279
360
  if (!resolvedPolicyPda) {
@@ -284,9 +365,7 @@ async function createVaultSyncTransaction({ instructions, owner, connection, pol
284
365
  resolvedPolicyPda = match.policyPda;
285
366
  resolvedConstraintIndices ??= match.constraintIndices;
286
367
  }
287
- // Auto-resolve constraint indices when not explicitly provided
288
368
  resolvedConstraintIndices ??= await (0, policyMatcher_1.resolveConstraintIndices)(connection, resolvedPolicyPda, syncInstructions);
289
- // Auto-resolve policy prefix and hook accounts when not explicitly provided
290
369
  let resolvedLeadingAccounts = leadingAccounts;
291
370
  let resolvedPreHookAccounts = preHookAccounts;
292
371
  let resolvedPostHookAccounts = postHookAccounts;
@@ -308,32 +387,64 @@ async function createVaultSyncTransaction({ instructions, owner, connection, pol
308
387
  preHookAccounts: resolvedPreHookAccounts,
309
388
  postHookAccounts: resolvedPostHookAccounts,
310
389
  });
311
- return { setupInstructions, preInstructions, instruction, postInstructions, signers, addressLookupTableAddresses };
390
+ return {
391
+ setupInstructions,
392
+ preInstructions,
393
+ instruction,
394
+ postInstructions,
395
+ signers,
396
+ addressLookupTableAddresses,
397
+ };
398
+ }
399
+ async function vaultSyncResultFitsInSingleTransaction(params) {
400
+ try {
401
+ const altAccounts = await resolveVaultSyncAltAccounts(params.connection, params.result.addressLookupTableAddresses);
402
+ const message = new web3_js_1.TransactionMessage({
403
+ payerKey: params.payer,
404
+ recentBlockhash: web3_js_1.PublicKey.default.toBase58(),
405
+ instructions: [...params.result.preInstructions, params.result.instruction, ...params.result.postInstructions],
406
+ }).compileToV0Message(altAccounts);
407
+ return new web3_js_1.VersionedTransaction(message).serialize().length <= 1232;
408
+ }
409
+ catch {
410
+ return false;
411
+ }
412
+ }
413
+ async function resolveVaultSyncAltAccounts(connection, addresses) {
414
+ const lookupTables = await Promise.all(addresses.map((address) => connection.getAddressLookupTable(address)));
415
+ return lookupTables
416
+ .map((entry) => entry.value)
417
+ .filter((entry) => entry !== null);
312
418
  }
313
- exports.createVaultSyncTransaction = createVaultSyncTransaction;
314
419
  // ============================================================================
315
420
  // Internal: Instruction Assembly
316
421
  // ============================================================================
317
422
  /** KLend farm modes — collateral for deposit/withdraw, debt for borrow/repay. */
318
423
  const FARM_COLLATERAL = 0;
319
424
  const FARM_DEBT = 1;
425
+ function isKaminoVaultInstruction(ix) {
426
+ return Object.values(vault_instruction_types_2.KaminoVaultAction).includes(ix.action);
427
+ }
428
+ function isKaminoFarmInstruction(ix) {
429
+ return Object.values(vault_instruction_types_2.KaminoFarmAction).includes(ix.action);
430
+ }
320
431
  function isOrderbookInstruction(ix) {
321
- return Object.values(OrderbookAction).includes(ix.action);
432
+ return Object.values(vault_instruction_types_2.OrderbookAction).includes(ix.action);
322
433
  }
323
434
  function isCoreInstruction(ix) {
324
- return Object.values(CoreAction).includes(ix.action);
435
+ return Object.values(vault_instruction_types_2.CoreAction).includes(ix.action);
325
436
  }
326
437
  function isSyInstruction(ix) {
327
- return Object.values(SyAction).includes(ix.action);
438
+ return Object.values(vault_instruction_types_2.SyAction).includes(ix.action);
328
439
  }
329
440
  function isTitanInstruction(ix) {
330
- return Object.values(TitanAction).includes(ix.action);
441
+ return Object.values(vault_instruction_types_2.TitanAction).includes(ix.action);
331
442
  }
332
443
  function isClmmInstruction(ix) {
333
- return Object.values(ClmmAction).includes(ix.action);
444
+ return Object.values(vault_instruction_types_2.ClmmAction).includes(ix.action);
334
445
  }
335
446
  function isLoopscaleInstruction(ix) {
336
- return Object.values(LoopscaleAction).includes(ix.action);
447
+ return Object.values(vault_instruction_types_2.LoopscaleAction).includes(ix.action);
337
448
  }
338
449
  /** Cache for loaded vaults to avoid redundant fetches */
339
450
  const vaultCache = new Map();
@@ -345,7 +456,7 @@ const TITAN_INPUT_TOKEN_PROGRAM_ACCOUNT_INDEX = 6;
345
456
  const TITAN_OUTPUT_TOKEN_PROGRAM_ACCOUNT_INDEX = 7;
346
457
  const YIELD_POSITION_BASE_SIZE = 124;
347
458
  const YIELD_POSITION_TRACKER_SIZE = 40;
348
- function createStrategySetupContext({ connection, env, owner, signer, vaultAddress, policyPda, vaultPda, accountIndex = 0, squadsProgram = syncTransaction_1.SQUADS_PROGRAM_ID, leadingAccounts, preHookAccounts, postHookAccounts, }) {
459
+ function createStrategySetupContext({ connection, env, owner, signer, vaultAddress, policyPda, vaultPda, accountIndex = 0, squadsProgram = syncTransaction_1.SQUADS_PROGRAM_ID, leadingAccounts, preHookAccounts, postHookAccounts, autoManagePositions = true, pricesAccount, }) {
349
460
  return {
350
461
  connection,
351
462
  env,
@@ -359,22 +470,36 @@ function createStrategySetupContext({ connection, env, owner, signer, vaultAddre
359
470
  leadingAccounts,
360
471
  preHookAccounts,
361
472
  postHookAccounts,
473
+ autoManagePositions,
474
+ pricesAccount,
362
475
  };
363
476
  }
477
+ exports.createStrategySetupContext = createStrategySetupContext;
478
+ function trackRequiredPriceIds(requiredPriceIds, priceIdValue) {
479
+ for (const id of (0, pricePathResolver_1.extractPriceIds)(priceIdValue)) {
480
+ const numericId = Number(id);
481
+ if (numericId !== 0) {
482
+ requiredPriceIds.add(numericId);
483
+ }
484
+ }
485
+ }
364
486
  async function loadStrategySetupState(context) {
365
487
  if (!context.vaultAddress) {
366
488
  return null;
367
489
  }
368
490
  if (!context.statePromise) {
369
491
  context.statePromise = (async () => {
370
- const strategyVault = await vault_2.ExponentVault.load({
492
+ const strategyVault = await vault_3.ExponentVault.load({
371
493
  env: context.env,
372
494
  connection: context.connection,
373
495
  address: context.vaultAddress,
374
496
  });
375
- const prices = await strategyVault.fetcher.fetchExponentPrices();
497
+ const prices = context.pricesAccount ?? await strategyVault.fetcher.fetchExponentPrices();
376
498
  const trackedOrderbooks = new Set();
377
499
  const trackedYieldVaults = new Set();
500
+ const trackedKaminoObligations = new Map();
501
+ const trackedKaminoFarms = new Set();
502
+ const trackedClmmPositions = new Set();
378
503
  const tokenEntryAccountByMint = new Map();
379
504
  const tokenPositionIndexByMint = new Map();
380
505
  const trackedTokenAccounts = new Set();
@@ -391,6 +516,25 @@ async function loadStrategySetupState(context) {
391
516
  trackedYieldVaults.add(position.yieldPosition[0].vault.toBase58());
392
517
  continue;
393
518
  }
519
+ const kaminoEntry = getTrackedKaminoObligationFromPosition(position);
520
+ if (kaminoEntry) {
521
+ trackedKaminoObligations.set(kaminoEntry.obligation.toBase58(), {
522
+ quotePriceId: kaminoEntry.quotePriceId,
523
+ quoteInputMint: (0, pricePathResolver_1.getPriceInputMintFromPriceId)(prices, kaminoEntry.quotePriceId),
524
+ mappedReserves: new Set((kaminoEntry.reservePriceMappings ?? []).map((mapping) => mapping.reserve.toBase58())),
525
+ });
526
+ continue;
527
+ }
528
+ const kaminoFarmEntry = getTrackedKaminoFarmFromPosition(position);
529
+ if (kaminoFarmEntry) {
530
+ trackedKaminoFarms.add(kaminoFarmKey(kaminoFarmEntry.farmState, kaminoFarmEntry.userState));
531
+ continue;
532
+ }
533
+ const clmmEntry = getTrackedClmmPositionFromPosition(position);
534
+ if (clmmEntry) {
535
+ trackedClmmPositions.add(clmmEntry.lpPosition.toBase58());
536
+ continue;
537
+ }
394
538
  if ("tokenAccount" in position && position.tokenAccount?.[0]) {
395
539
  const entry = position.tokenAccount[0];
396
540
  tokenPositionIndexByMint.set(entry.tokenMint.toBase58(), index);
@@ -402,38 +546,148 @@ async function loadStrategySetupState(context) {
402
546
  return {
403
547
  strategyVault,
404
548
  prices,
549
+ requiredPriceIds: (0, vault_2.collectTrackedStrategyVaultPriceIds)({
550
+ tokenEntries: strategyVault.state.tokenEntries,
551
+ strategyPositions: strategyVault.state.strategyPositions,
552
+ }),
405
553
  nextStrategyPositionIndex: strategyVault.state.strategyPositions.length,
406
554
  trackedOrderbooks,
407
555
  trackedYieldVaults,
556
+ trackedKaminoObligations,
557
+ trackedKaminoFarms,
558
+ trackedClmmPositions,
408
559
  tokenEntryAccountByMint,
409
560
  tokenPositionIndexByMint,
410
561
  trackedTokenAccounts,
562
+ baseAumAccounts: mutableStrategyVault(strategyVault).aumRemainingAccounts(),
563
+ plannedAumAccounts: [],
411
564
  existingAccounts: new Map(),
412
565
  };
413
566
  })();
414
567
  }
415
568
  return context.statePromise;
416
569
  }
417
- function findSimplePrice(prices, priceMint, underlyingMint) {
418
- for (const entry of prices.prices) {
419
- if (!entry)
420
- continue;
421
- if (!entry.priceMint.equals(priceMint) || !entry.underlyingMint.equals(underlyingMint)) {
570
+ async function buildSetupStatePriceRefreshInstructions(setupContext) {
571
+ const state = await loadStrategySetupState(setupContext);
572
+ if (!state) {
573
+ return [];
574
+ }
575
+ const refreshInstructions = [];
576
+ const reserveAccounts = new Map();
577
+ for (const priceId of state.requiredPriceIds) {
578
+ const priceEntry = state.prices.prices[priceId];
579
+ if (!priceEntry || !isKaminoVaultPriceType(priceEntry.priceType)) {
422
580
  continue;
423
581
  }
424
- return {
425
- priceId: entry.priceId,
426
- price: exponentPriceToDecimal(entry),
427
- };
582
+ for (const reserveAddress of priceEntry.interfaceAccounts.slice(1)) {
583
+ const reserveKey = reserveAddress.toBase58();
584
+ if (reserveAccounts.has(reserveKey)) {
585
+ continue;
586
+ }
587
+ const reserveAccount = await kamino_reserve_deserializer_1.Reserve.fetch(setupContext.connection, reserveAddress);
588
+ if (!reserveAccount) {
589
+ throw new Error(`Missing Kamino reserve account ${reserveKey} required to refresh Kamino vault prices`);
590
+ }
591
+ reserveAccounts.set(reserveKey, { account: reserveAccount });
592
+ }
428
593
  }
429
- return null;
594
+ if (reserveAccounts.size > 0) {
595
+ const reserves = [...reserveAccounts.values()];
596
+ const defaultKey = web3_js_1.PublicKey.default;
597
+ const oracleOrSentinel = (key) => key.equals(defaultKey) ? policyBuilders_1.KAMINO_LENDING_PROGRAM_ID : key;
598
+ refreshInstructions.push(...await (0, scope_refresh_1.buildScopeRefreshInstructions)(setupContext.connection, reserves));
599
+ for (const [reserveKey, { account }] of reserveAccounts.entries()) {
600
+ const tokenInfo = account.config.tokenInfo;
601
+ refreshInstructions.push((0, instructions_1.refreshReserve)({
602
+ reserve: new web3_js_1.PublicKey(reserveKey),
603
+ lendingMarket: account.lendingMarket,
604
+ pythOracle: oracleOrSentinel(tokenInfo.pythConfiguration.price),
605
+ switchboardPriceOracle: oracleOrSentinel(tokenInfo.switchboardConfiguration.priceAggregator),
606
+ switchboardTwapOracle: oracleOrSentinel(tokenInfo.switchboardConfiguration.twapAggregator),
607
+ scopePrices: oracleOrSentinel(tokenInfo.scopeConfiguration.priceFeed),
608
+ }));
609
+ }
610
+ }
611
+ const updatePriceInstructions = await state.strategyVault.ixsUpdateStrategyVaultPrices(state.prices, {
612
+ manager: setupContext.signer,
613
+ priceIds: [...state.requiredPriceIds].sort((a, b) => a - b),
614
+ });
615
+ return [...refreshInstructions, ...updatePriceInstructions];
616
+ }
617
+ exports.buildSetupStatePriceRefreshInstructions = buildSetupStatePriceRefreshInstructions;
618
+ function isAutoManagePositionsEnabled(setupContext) {
619
+ return setupContext?.autoManagePositions ?? true;
620
+ }
621
+ function unwrapTupleLikeValue(value) {
622
+ if (Array.isArray(value)) {
623
+ return value[0];
624
+ }
625
+ if (value && typeof value === "object" && "0" in value) {
626
+ return value[0];
627
+ }
628
+ return value ?? undefined;
629
+ }
630
+ function getTrackedKaminoObligationFromPosition(position) {
631
+ if (!position || typeof position !== "object" || !("obligation" in position)) {
632
+ return null;
633
+ }
634
+ const obligationContainer = position.obligation;
635
+ const obligationValue = unwrapTupleLikeValue(obligationContainer);
636
+ const kaminoContainer = (obligationValue
637
+ && typeof obligationValue === "object"
638
+ && "kaminoObligation" in obligationValue)
639
+ ? obligationValue.kaminoObligation
640
+ : obligationValue;
641
+ const kaminoEntry = unwrapTupleLikeValue(kaminoContainer);
642
+ if (!kaminoEntry
643
+ || typeof kaminoEntry !== "object"
644
+ || !("obligation" in kaminoEntry)
645
+ || !(kaminoEntry.obligation instanceof web3_js_1.PublicKey)) {
646
+ return null;
647
+ }
648
+ const typedKaminoEntry = kaminoEntry;
649
+ return {
650
+ obligation: typedKaminoEntry.obligation,
651
+ quotePriceId: typedKaminoEntry.quotePriceId,
652
+ reservePriceMappings: typedKaminoEntry.reservePriceMappings ?? [],
653
+ };
430
654
  }
431
- function exponentPriceToDecimal(entry) {
432
- const raw = entry.price[0];
433
- if (!Array.isArray(raw)) {
434
- throw new Error(`Invalid Exponent price format for ${entry.priceId.toString()}`);
655
+ function getTrackedKaminoFarmFromPosition(position) {
656
+ if (!position || typeof position !== "object" || !("kaminoFarm" in position)) {
657
+ return null;
435
658
  }
436
- return new decimal_js_1.default((0, precise_number_1.bigintU256ToString)(raw.map((value) => BigInt(value.toString()))));
659
+ const kaminoFarmContainer = position.kaminoFarm;
660
+ const kaminoFarmEntry = unwrapTupleLikeValue(kaminoFarmContainer);
661
+ if (!kaminoFarmEntry
662
+ || typeof kaminoFarmEntry !== "object"
663
+ || !(kaminoFarmEntry.farmState instanceof web3_js_1.PublicKey)
664
+ || !(kaminoFarmEntry.userState instanceof web3_js_1.PublicKey)) {
665
+ return null;
666
+ }
667
+ return {
668
+ farmState: kaminoFarmEntry.farmState,
669
+ userState: kaminoFarmEntry.userState,
670
+ };
671
+ }
672
+ function kaminoFarmKey(farmState, userState) {
673
+ return `${farmState.toBase58()}:${userState.toBase58()}`;
674
+ }
675
+ function getTrackedClmmPositionFromPosition(position) {
676
+ if (!position || typeof position !== "object" || !("clmmPosition" in position)) {
677
+ return null;
678
+ }
679
+ const clmmContainer = position.clmmPosition;
680
+ const clmmEntry = unwrapTupleLikeValue(clmmContainer);
681
+ if (!clmmEntry
682
+ || typeof clmmEntry !== "object"
683
+ || !(clmmEntry.lpPosition instanceof web3_js_1.PublicKey)
684
+ || !(clmmEntry.market instanceof web3_js_1.PublicKey)) {
685
+ return null;
686
+ }
687
+ return {
688
+ lpPosition: clmmEntry.lpPosition,
689
+ market: clmmEntry.market,
690
+ };
437
691
  }
438
692
  async function accountExists(state, connection, address) {
439
693
  const cacheKey = address.toBase58();
@@ -445,6 +699,173 @@ async function accountExists(state, connection, address) {
445
699
  state.existingAccounts.set(cacheKey, exists);
446
700
  return exists;
447
701
  }
702
+ function mutableStrategyVault(value) {
703
+ return ("strategyVault" in value ? value.strategyVault : value);
704
+ }
705
+ function buildTrackedAumRemainingAccounts(state, extraAccounts = []) {
706
+ return (0, utils_1.uniqueRemainingAccounts)([
707
+ ...state.baseAumAccounts,
708
+ ...state.plannedAumAccounts,
709
+ ...extraAccounts,
710
+ ]);
711
+ }
712
+ function recordPlannedOrderbookEntry(state, params) {
713
+ state.trackedOrderbooks.add(params.orderbook.toBase58());
714
+ state.plannedAumAccounts.push({ pubkey: params.orderbook, isSigner: false, isWritable: false });
715
+ state.strategyVault.state.strategyPositions.push({
716
+ orderbook: [{
717
+ orderbook: params.orderbook,
718
+ mint: params.mint,
719
+ offerIdxVec: [],
720
+ priceIdPt: params.priceIdPt,
721
+ baseMint: params.baseMint,
722
+ }],
723
+ });
724
+ state.nextStrategyPositionIndex += 1;
725
+ }
726
+ function recordPlannedYieldPosition(state, params) {
727
+ state.trackedYieldVaults.add(params.vault.toBase58());
728
+ state.existingAccounts.set(params.yieldPosition.toBase58(), true);
729
+ state.strategyVault.state.strategyPositions.push({
730
+ yieldPosition: [{
731
+ yieldPosition: params.yieldPosition,
732
+ vault: params.vault,
733
+ priceIdPt: params.priceIdPt,
734
+ }],
735
+ });
736
+ state.nextStrategyPositionIndex += 1;
737
+ }
738
+ function recordPlannedTokenAccountEntry(state, params) {
739
+ const tokenMintKey = params.tokenMint.toBase58();
740
+ const tokenAccountKey = params.tokenAccount.toBase58();
741
+ state.tokenPositionIndexByMint.set(tokenMintKey, state.nextStrategyPositionIndex);
742
+ state.trackedTokenAccounts.add(tokenAccountKey);
743
+ state.plannedAumAccounts.push({ pubkey: params.tokenAccount, isSigner: false, isWritable: false });
744
+ state.strategyVault.state.strategyPositions.push({
745
+ tokenAccount: [{
746
+ tokenMint: params.tokenMint,
747
+ balances: [{
748
+ tokenAccount: params.tokenAccount,
749
+ mint: params.tokenMint,
750
+ priceId: params.priceId,
751
+ }],
752
+ }],
753
+ });
754
+ state.nextStrategyPositionIndex += 1;
755
+ }
756
+ function recordPlannedTokenAccountBalance(state, params) {
757
+ const tokenMintKey = params.tokenMint.toBase58();
758
+ const tokenAccountKey = params.tokenAccount.toBase58();
759
+ state.trackedTokenAccounts.add(tokenAccountKey);
760
+ state.plannedAumAccounts.push({ pubkey: params.tokenAccount, isSigner: false, isWritable: false });
761
+ const positions = state.strategyVault.state.strategyPositions;
762
+ const target = positions.find((position) => {
763
+ const tokenAccountEntry = Array.isArray(position.tokenAccount) ? position.tokenAccount[0] : undefined;
764
+ return tokenAccountEntry && tokenAccountEntry.tokenMint instanceof web3_js_1.PublicKey && tokenAccountEntry.tokenMint.equals(params.tokenMint);
765
+ });
766
+ const tokenAccountEntry = target?.tokenAccount?.[0];
767
+ tokenAccountEntry?.balances?.push({
768
+ tokenAccount: params.tokenAccount,
769
+ mint: params.tokenMint,
770
+ priceId: params.priceId,
771
+ });
772
+ }
773
+ function recordPlannedKaminoObligation(state, entry) {
774
+ state.existingAccounts.set(entry.obligation.toBase58(), true);
775
+ state.trackedKaminoObligations.set(entry.obligation.toBase58(), {
776
+ quotePriceId: entry.quotePriceId,
777
+ quoteInputMint: entry.quoteInputMint,
778
+ mappedReserves: new Set(entry.reservePriceMappings.map((mapping) => mapping.reserve.toBase58())),
779
+ });
780
+ state.plannedAumAccounts.push({ pubkey: entry.obligation, isSigner: false, isWritable: false });
781
+ for (const mapping of entry.reservePriceMappings) {
782
+ state.plannedAumAccounts.push({ pubkey: mapping.reserve, isSigner: false, isWritable: false });
783
+ }
784
+ ;
785
+ state.strategyVault.state.strategyPositions.push({
786
+ obligation: [{
787
+ kaminoObligation: [{
788
+ obligation: entry.obligation,
789
+ lendingProgramId: policyBuilders_1.KAMINO_LENDING_PROGRAM_ID,
790
+ quotePriceId: entry.quotePriceId,
791
+ reservePriceMappings: entry.reservePriceMappings,
792
+ remainingAccountsAmount: entry.remainingAccountsAmount,
793
+ minPriceStatusFlags: entry.minPriceStatusFlags,
794
+ }],
795
+ }],
796
+ });
797
+ state.nextStrategyPositionIndex += 1;
798
+ }
799
+ function recordPlannedKaminoReserveMappings(state, params) {
800
+ const tracked = state.trackedKaminoObligations.get(params.obligation.toBase58());
801
+ if (!tracked) {
802
+ return;
803
+ }
804
+ tracked.quoteInputMint = params.quoteInputMint;
805
+ for (const mapping of params.reservePriceMappings) {
806
+ tracked.mappedReserves.add(mapping.reserve.toBase58());
807
+ }
808
+ const positions = state.strategyVault.state.strategyPositions;
809
+ const target = positions.find((position) => {
810
+ const entry = getTrackedKaminoObligationFromPosition(position);
811
+ return entry?.obligation.equals(params.obligation);
812
+ });
813
+ const kaminoEntry = (Array.isArray(target?.obligation)
814
+ ? target?.obligation?.[0]?.kaminoObligation?.[0]
815
+ : undefined);
816
+ if (!kaminoEntry) {
817
+ return;
818
+ }
819
+ for (const mapping of params.reservePriceMappings) {
820
+ const existing = kaminoEntry.reservePriceMappings?.find((item) => item.reserve.equals(mapping.reserve));
821
+ if (existing) {
822
+ existing.reservePriceId = mapping.reservePriceId;
823
+ }
824
+ else {
825
+ kaminoEntry.reservePriceMappings ??= [];
826
+ kaminoEntry.reservePriceMappings.push(mapping);
827
+ }
828
+ state.plannedAumAccounts.push({ pubkey: mapping.reserve, isSigner: false, isWritable: false });
829
+ }
830
+ kaminoEntry.remainingAccountsAmount = BigInt(1 + (kaminoEntry.reservePriceMappings?.length ?? 0));
831
+ }
832
+ function recordPlannedKaminoFarmPosition(state, params) {
833
+ const trackingKey = kaminoFarmKey(params.farmState, params.userState);
834
+ if (state.trackedKaminoFarms.has(trackingKey)) {
835
+ return;
836
+ }
837
+ state.trackedKaminoFarms.add(trackingKey);
838
+ state.existingAccounts.set(params.userState.toBase58(), true);
839
+ state.plannedAumAccounts.push({ pubkey: params.farmState, isSigner: false, isWritable: false });
840
+ state.plannedAumAccounts.push({ pubkey: params.userState, isSigner: false, isWritable: false });
841
+ state.plannedAumAccounts.push({ pubkey: params.globalConfig, isSigner: false, isWritable: false });
842
+ if (params.scopePrices) {
843
+ state.plannedAumAccounts.push({ pubkey: params.scopePrices, isSigner: false, isWritable: false });
844
+ }
845
+ ;
846
+ state.strategyVault.state.strategyPositions.push({
847
+ kaminoFarm: [{
848
+ farmState: params.farmState,
849
+ userState: params.userState,
850
+ }],
851
+ });
852
+ state.nextStrategyPositionIndex += 1;
853
+ }
854
+ function recordPlannedClmmPosition(state, params) {
855
+ state.existingAccounts.set(params.lpPosition.toBase58(), true);
856
+ state.trackedClmmPositions.add(params.lpPosition.toBase58());
857
+ state.plannedAumAccounts.push({ pubkey: params.lpPosition, isSigner: false, isWritable: false });
858
+ mutableStrategyVault(state).clmmTicksMap.set(params.market.toBase58(), params.ticksKey);
859
+ state.strategyVault.state.strategyPositions.push({
860
+ clmmPosition: [{
861
+ lpPosition: params.lpPosition,
862
+ market: params.market,
863
+ priceIdPt: params.priceIdPt,
864
+ priceIdSy: params.priceIdSy,
865
+ }],
866
+ });
867
+ state.nextStrategyPositionIndex += 1;
868
+ }
448
869
  async function maybeCreateOwnedAtaSetupInstruction({ state, connection, payer, owner, mint, tokenProgram, tokenAccount, }) {
449
870
  if (await accountExists(state, connection, tokenAccount)) {
450
871
  return null;
@@ -465,10 +886,13 @@ async function ensureOrderbookPositionSetup(orderbook, buckets, setupContext) {
465
886
  if (state.trackedOrderbooks.has(orderbookKey)) {
466
887
  return;
467
888
  }
468
- const ptPrice = findSimplePrice(state.prices, orderbook.mintPt, state.strategyVault.state.underlyingMint);
469
- if (!ptPrice) {
470
- throw new Error(`Missing Exponent price for orderbook setup (${orderbook.selfAddress.toBase58()})`);
471
- }
889
+ const priceIdPt = (0, pricePathResolver_1.resolvePriceIdFromMintToUnderlyingOrThrow)({
890
+ prices: state.prices,
891
+ sourceMint: orderbook.mintPt,
892
+ targetMint: state.strategyVault.state.underlyingMint,
893
+ label: `orderbook setup (${orderbook.selfAddress.toBase58()})`,
894
+ });
895
+ trackRequiredPriceIds(state.requiredPriceIds, priceIdPt);
472
896
  buckets.setupInstructions.push(state.strategyVault.ixWrapperManageVaultSettings({
473
897
  manager: setupContext.signer,
474
898
  actions: [
@@ -479,7 +903,7 @@ async function ensureOrderbookPositionSetup(orderbook, buckets, setupContext) {
479
903
  userEscrowIdx: 0,
480
904
  mint: orderbook.vault.mintSy,
481
905
  offerIdxVec: [],
482
- priceIdPt: exponentVaults.priceId("Simple", { priceId: ptPrice.priceId }),
906
+ priceIdPt,
483
907
  baseMint: state.strategyVault.state.underlyingMint,
484
908
  }]),
485
909
  ],
@@ -488,8 +912,12 @@ async function ensureOrderbookPositionSetup(orderbook, buckets, setupContext) {
488
912
  { pubkey: orderbook.state.vault, isSigner: false, isWritable: false },
489
913
  ],
490
914
  }));
491
- state.trackedOrderbooks.add(orderbookKey);
492
- state.nextStrategyPositionIndex += 1;
915
+ recordPlannedOrderbookEntry(state, {
916
+ orderbook: orderbook.selfAddress,
917
+ mint: orderbook.vault.mintSy,
918
+ priceIdPt,
919
+ baseMint: state.strategyVault.state.underlyingMint,
920
+ });
493
921
  }
494
922
  async function ensureYieldPositionSetup(coreVault, buckets, setupContext) {
495
923
  const state = await loadStrategySetupState(setupContext);
@@ -500,10 +928,13 @@ async function ensureYieldPositionSetup(coreVault, buckets, setupContext) {
500
928
  if (state.trackedYieldVaults.has(coreVaultKey)) {
501
929
  return;
502
930
  }
503
- const ptPrice = findSimplePrice(state.prices, coreVault.mintPt, state.strategyVault.state.underlyingMint);
504
- if (!ptPrice) {
505
- throw new Error(`Missing Exponent price for core vault setup (${coreVault.selfAddress.toBase58()})`);
506
- }
931
+ const priceIdPt = (0, pricePathResolver_1.resolvePriceIdFromMintToUnderlyingOrThrow)({
932
+ prices: state.prices,
933
+ sourceMint: coreVault.mintPt,
934
+ targetMint: state.strategyVault.state.underlyingMint,
935
+ label: `core vault setup (${coreVault.selfAddress.toBase58()})`,
936
+ });
937
+ trackRequiredPriceIds(state.requiredPriceIds, priceIdPt);
507
938
  const yieldPosition = coreVault.pda.yieldPosition({ owner: setupContext.owner, vault: coreVault.selfAddress });
508
939
  if (!(await accountExists(state, setupContext.connection, yieldPosition))) {
509
940
  const requiredLamports = await setupContext.connection.getMinimumBalanceForRentExemption(YIELD_POSITION_BASE_SIZE + (coreVault.state.emissions.length * YIELD_POSITION_TRACKER_SIZE));
@@ -534,7 +965,7 @@ async function ensureYieldPositionSetup(coreVault, buckets, setupContext) {
534
965
  exponentVaults.vaultSettingsAction("AddYieldPositionEntry", {
535
966
  yieldPosition,
536
967
  vault: coreVault.selfAddress,
537
- priceIdPt: exponentVaults.priceId("Simple", { priceId: ptPrice.priceId }),
968
+ priceIdPt,
538
969
  }),
539
970
  ],
540
971
  remainingAccounts: [
@@ -542,8 +973,11 @@ async function ensureYieldPositionSetup(coreVault, buckets, setupContext) {
542
973
  { pubkey: yieldPosition, isSigner: false, isWritable: false },
543
974
  ],
544
975
  }));
545
- state.trackedYieldVaults.add(coreVaultKey);
546
- state.nextStrategyPositionIndex += 1;
976
+ recordPlannedYieldPosition(state, {
977
+ yieldPosition,
978
+ vault: coreVault.selfAddress,
979
+ priceIdPt,
980
+ });
547
981
  }
548
982
  async function wrapVaultSignedSetupInstruction({ instruction, setupContext, }) {
549
983
  if (!setupContext.vaultPda) {
@@ -567,7 +1001,12 @@ async function wrapVaultSignedSetupInstruction({ instruction, setupContext, }) {
567
1001
  let resolvedPostHookAccounts = setupContext.postHookAccounts;
568
1002
  if (setupContext.vaultAddress
569
1003
  && (!resolvedLeadingAccounts || !resolvedPreHookAccounts || !resolvedPostHookAccounts)) {
570
- const hooks = await (0, policyMatcher_1.resolveHookAccounts)(setupContext.connection, resolvedPolicyPda, setupContext.vaultAddress, setupContext.signer);
1004
+ // Cache hook resolution on the context to avoid redundant RPC calls
1005
+ // when wrapping multiple setup instructions in the same build.
1006
+ if (!setupContext.resolvedHooksPromise) {
1007
+ setupContext.resolvedHooksPromise = (0, policyMatcher_1.resolveHookAccounts)(setupContext.connection, resolvedPolicyPda, setupContext.vaultAddress, setupContext.signer);
1008
+ }
1009
+ const hooks = await setupContext.resolvedHooksPromise;
571
1010
  resolvedLeadingAccounts ??= hooks.leadingAccounts;
572
1011
  resolvedPreHookAccounts ??= hooks.preHookAccounts;
573
1012
  resolvedPostHookAccounts ??= hooks.postHookAccounts;
@@ -603,10 +1042,13 @@ async function ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenPr
603
1042
  if (state.trackedTokenAccounts.has(tokenAccountKey)) {
604
1043
  return;
605
1044
  }
606
- const price = findSimplePrice(state.prices, tokenMint, state.strategyVault.state.underlyingMint);
607
- if (!price) {
608
- throw new Error(`Missing Exponent price for token position setup (${tokenMint.toBase58()})`);
609
- }
1045
+ const priceId = (0, pricePathResolver_1.resolvePriceIdFromMintToUnderlyingOrThrow)({
1046
+ prices: state.prices,
1047
+ sourceMint: tokenMint,
1048
+ targetMint: state.strategyVault.state.underlyingMint,
1049
+ label: `token position setup (${tokenMint.toBase58()})`,
1050
+ });
1051
+ trackRequiredPriceIds(state.requiredPriceIds, priceId);
610
1052
  const maybeAtaIx = await maybeCreateOwnedAtaSetupInstruction({
611
1053
  state,
612
1054
  connection: setupContext.connection,
@@ -629,7 +1071,7 @@ async function ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenPr
629
1071
  balances: [{
630
1072
  tokenAccount,
631
1073
  mint: tokenMint,
632
- priceId: exponentVaults.priceId("Simple", { priceId: price.priceId }),
1074
+ priceId,
633
1075
  }],
634
1076
  }]),
635
1077
  ],
@@ -637,8 +1079,11 @@ async function ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenPr
637
1079
  { pubkey: tokenAccount, isSigner: false, isWritable: false },
638
1080
  ],
639
1081
  }));
640
- state.tokenPositionIndexByMint.set(tokenMintKey, state.nextStrategyPositionIndex);
641
- state.nextStrategyPositionIndex += 1;
1082
+ recordPlannedTokenAccountEntry(state, {
1083
+ tokenMint,
1084
+ tokenAccount,
1085
+ priceId,
1086
+ });
642
1087
  }
643
1088
  else {
644
1089
  buckets.setupInstructions.push(state.strategyVault.ixWrapperManagerUpdatePosition({
@@ -649,86 +1094,811 @@ async function ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenPr
649
1094
  balance: {
650
1095
  tokenAccount,
651
1096
  mint: tokenMint,
652
- priceId: exponentVaults.priceId("Simple", { priceId: price.priceId }),
1097
+ priceId,
653
1098
  },
654
1099
  },
655
1100
  remainingAccounts: [
656
1101
  { pubkey: tokenAccount, isSigner: false, isWritable: false },
657
1102
  ],
658
1103
  }));
1104
+ recordPlannedTokenAccountBalance(state, {
1105
+ tokenMint,
1106
+ tokenAccount,
1107
+ priceId,
1108
+ });
1109
+ }
1110
+ state.trackedTokenAccounts.add(tokenAccountKey);
1111
+ }
1112
+ async function buildVaultInstructions(instructions, owner, connection, signer, policyPda, vaultPda, accountIndex, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram = syncTransaction_1.SQUADS_PROGRAM_ID, autoManagePositions = true, sharedSetupContext) {
1113
+ const buckets = {
1114
+ setupInstructions: [],
1115
+ syncInstructions: [],
1116
+ preInstructions: [],
1117
+ postInstructions: [],
1118
+ signers: [],
1119
+ addressLookupTableAddresses: [],
1120
+ };
1121
+ const setupContext = sharedSetupContext ?? createStrategySetupContext({
1122
+ connection,
1123
+ env: environment_1.LOCAL_ENV,
1124
+ owner,
1125
+ signer,
1126
+ vaultAddress,
1127
+ policyPda,
1128
+ vaultPda,
1129
+ accountIndex,
1130
+ squadsProgram,
1131
+ leadingAccounts,
1132
+ preHookAccounts,
1133
+ postHookAccounts,
1134
+ autoManagePositions,
1135
+ });
1136
+ for (const ix of instructions) {
1137
+ if (isKaminoVaultInstruction(ix)) {
1138
+ await buildKaminoVaultInstruction(ix, buckets, setupContext);
1139
+ continue;
1140
+ }
1141
+ if (isKaminoFarmInstruction(ix)) {
1142
+ await buildKaminoFarmInstruction(ix, buckets, setupContext);
1143
+ continue;
1144
+ }
1145
+ if (isOrderbookInstruction(ix)) {
1146
+ await buildOrderbookInstruction(ix, owner, connection, buckets, setupContext);
1147
+ continue;
1148
+ }
1149
+ if (isCoreInstruction(ix)) {
1150
+ await buildCoreInstruction(ix, owner, connection, buckets, setupContext);
1151
+ continue;
1152
+ }
1153
+ if (isSyInstruction(ix)) {
1154
+ await buildSyInstruction(ix, owner, connection, buckets, setupContext);
1155
+ continue;
1156
+ }
1157
+ if (isTitanInstruction(ix)) {
1158
+ await buildTitanInstruction(ix, buckets, setupContext);
1159
+ continue;
1160
+ }
1161
+ if (isLoopscaleInstruction(ix)) {
1162
+ await buildLoopscaleInstruction(ix, buckets, setupContext);
1163
+ continue;
1164
+ }
1165
+ if (isClmmInstruction(ix)) {
1166
+ await buildClmmInstruction(ix, owner, connection, buckets, setupContext);
1167
+ continue;
1168
+ }
1169
+ switch (ix.action) {
1170
+ case vault_instruction_types_2.VaultAction.INIT_USER_METADATA:
1171
+ await buildInitUserMetadata(owner, connection, buckets);
1172
+ break;
1173
+ case vault_instruction_types_2.VaultAction.INIT_OBLIGATION:
1174
+ await buildInitObligation(ix, owner, connection, buckets, setupContext);
1175
+ break;
1176
+ case vault_instruction_types_2.VaultAction.DEPOSIT:
1177
+ await buildDeposit(ix, owner, connection, signer, buckets, setupContext);
1178
+ break;
1179
+ case vault_instruction_types_2.VaultAction.WITHDRAW:
1180
+ await buildWithdraw(ix, owner, connection, signer, buckets, setupContext);
1181
+ break;
1182
+ case vault_instruction_types_2.VaultAction.BORROW:
1183
+ await buildBorrow(ix, owner, connection, signer, buckets, setupContext);
1184
+ break;
1185
+ case vault_instruction_types_2.VaultAction.REPAY:
1186
+ await buildRepay(ix, owner, connection, signer, buckets, setupContext);
1187
+ break;
1188
+ }
1189
+ }
1190
+ return buckets;
1191
+ }
1192
+ const KAMINO_VAULT_EVENT_AUTHORITY = (0, utils_1.emitEventAuthority)(policyBuilders_1.KAMINO_VAULT_PROGRAM_ID);
1193
+ const KAMINO_FARM_USER_STATE_SIZE = 920;
1194
+ const KAMINO_STAKE_ALL_AMOUNT = new bn_js_1.default("18446744073709551615");
1195
+ function toBn(value) {
1196
+ if (bn_js_1.default.isBN(value)) {
1197
+ return value;
1198
+ }
1199
+ return new bn_js_1.default(value.toString());
1200
+ }
1201
+ function minBn(lhs, rhs) {
1202
+ return lhs.lte(rhs) ? lhs : rhs;
1203
+ }
1204
+ function encodeU64InstructionData(discriminator, value) {
1205
+ return Buffer.concat([discriminator, toBn(value).toArrayLike(Buffer, "le", 8)]);
1206
+ }
1207
+ function encodeU128InstructionData(discriminator, value) {
1208
+ return Buffer.concat([discriminator, toBn(value).toArrayLike(Buffer, "le", 16)]);
1209
+ }
1210
+ function getKaminoFarmUserStateAddress(delegatee, farmState) {
1211
+ return (0, constants_1.getKaminoFarmsObligationFarm)(delegatee, farmState, KAMINO_FARMS_PROGRAM_ID);
1212
+ }
1213
+ async function accountExistsMaybeTracked(setupContext, address) {
1214
+ if (!setupContext) {
1215
+ return false;
1216
+ }
1217
+ const state = await loadStrategySetupState(setupContext);
1218
+ if (state) {
1219
+ return accountExists(state, setupContext.connection, address);
1220
+ }
1221
+ return (await setupContext.connection.getAccountInfo(address)) !== null;
1222
+ }
1223
+ function matchesKaminoVaultInterfaceAccounts(entry, interfaceAccounts) {
1224
+ return (entry.interfaceAccounts.length === interfaceAccounts.length
1225
+ && entry.interfaceAccounts.every((account, index) => account.equals(interfaceAccounts[index])));
1226
+ }
1227
+ // ExponentPrices stores explicit wire discriminators, which can drift from the
1228
+ // generated TypeScript enum ordinals. Accept both so Kamino vault share
1229
+ // tracking works across current program/IDL combinations.
1230
+ function isKaminoVaultPriceType(priceType) {
1231
+ return priceType === KAMINO_VAULT_PRICE_TYPE_WIRE || priceType === exponentVaults.PriceType.KaminoVault;
1232
+ }
1233
+ function resolveKaminoVaultPriceEntry(params) {
1234
+ const candidates = params.prices.prices.filter((entry) => entry !== null).filter((entry) => isKaminoVaultPriceType(entry.priceType)
1235
+ && entry.priceMint.equals(params.sharesMint)
1236
+ && entry.underlyingMint.equals(params.depositTokenMint)
1237
+ && matchesKaminoVaultInterfaceAccounts(entry, params.interfaceAccounts));
1238
+ if (candidates.length === 0) {
1239
+ const interfaceAccountsLabel = params.interfaceAccounts.map((account) => account.toBase58()).join(", ");
1240
+ throw new Error(`Missing Exponent KaminoVault price for shares mint ${params.sharesMint.toBase58()} and deposit mint ${params.depositTokenMint.toBase58()} (interface accounts: ${interfaceAccountsLabel}). Register a PriceType.KaminoVault price for this vault before using auto-managed Kamino vault share tracking.`);
1241
+ }
1242
+ if (candidates.length > 1) {
1243
+ throw new Error(`Multiple Exponent KaminoVault prices matched shares mint ${params.sharesMint.toBase58()} and deposit mint ${params.depositTokenMint.toBase58()}`);
1244
+ }
1245
+ return candidates[0];
1246
+ }
1247
+ function resolveKaminoVaultTrackedPriceId(params) {
1248
+ if (params.depositTokenMint.equals(params.state.strategyVault.state.underlyingMint)) {
1249
+ return { __kind: "Simple", priceId: params.sharePriceId };
1250
+ }
1251
+ const reservePriceId = (0, pricePathResolver_1.resolvePriceIdFromMintToUnderlyingOrThrow)({
1252
+ prices: params.state.prices,
1253
+ sourceMint: params.depositTokenMint,
1254
+ targetMint: params.state.strategyVault.state.underlyingMint,
1255
+ label: params.label,
1256
+ });
1257
+ const reservePriceIds = (0, pricePathResolver_1.extractPriceIds)(reservePriceId).map((id) => BigInt(id));
1258
+ return { __kind: "Multiply", priceIds: [...reservePriceIds, params.sharePriceId] };
1259
+ }
1260
+ function decodeKaminoVaultState(data) {
1261
+ const decoded = KAMINO_VAULT_CODER.accounts.decode("VaultState", data);
1262
+ return {
1263
+ tokenAvailable: decoded.token_available,
1264
+ sharesIssued: decoded.shares_issued,
1265
+ pendingFeesSf: decoded.pending_fees_sf,
1266
+ allocations: decoded.vault_allocation_strategy
1267
+ .filter((allocation) => !allocation.reserve.equals(web3_js_1.PublicKey.default))
1268
+ .map((allocation) => ({
1269
+ reserve: allocation.reserve,
1270
+ ctokenAllocation: allocation.ctoken_allocation,
1271
+ })),
1272
+ };
1273
+ }
1274
+ function calculateKaminoVaultTokensPerShareFromSnapshot(snapshot) {
1275
+ if (snapshot.sharesIssued.isZero()) {
1276
+ return new decimal_js_1.default(0);
1277
+ }
1278
+ const investedTotal = snapshot.reserves.reduce((total, reserve) => total.add(reserve.investedLiquidityAmount), new decimal_js_1.default(0));
1279
+ const pendingFees = new kamino_reserve_deserializer_1.Fraction(snapshot.pendingFeesSf).toDecimal();
1280
+ const currentVaultAum = new decimal_js_1.default(snapshot.tokenAvailable.toString())
1281
+ .add(investedTotal)
1282
+ .sub(pendingFees);
1283
+ if (currentVaultAum.lte(0)) {
1284
+ return new decimal_js_1.default(0);
1285
+ }
1286
+ return currentVaultAum.div(snapshot.sharesIssued.toString());
1287
+ }
1288
+ function planKaminoVaultWithdrawLegsFromSnapshot(params) {
1289
+ if (params.reserve) {
1290
+ return [{ reserveAddress: params.reserve, sharesAmount: params.sharesAmount }];
1291
+ }
1292
+ const actualSharesToWithdraw = minBn(params.sharesAmount, params.snapshot.sharesBalance);
1293
+ if (actualSharesToWithdraw.isZero()) {
1294
+ throw new Error("Cannot withdraw zero Kamino Vault shares");
1295
+ }
1296
+ if (params.snapshot.reserves.length === 0) {
1297
+ throw new Error("Kamino Vault has no active reserves to anchor a reserve-specific withdraw instruction");
1298
+ }
1299
+ const withdrawAllShares = params.sharesAmount.gte(params.snapshot.sharesBalance);
1300
+ const tokensPerShare = calculateKaminoVaultTokensPerShareFromSnapshot(params.snapshot);
1301
+ if (tokensPerShare.lte(0)) {
1302
+ throw new Error("Kamino Vault has zero share price; cannot plan a withdraw");
1303
+ }
1304
+ const tokensToWithdraw = new decimal_js_1.default(actualSharesToWithdraw.toString()).mul(tokensPerShare);
1305
+ const availableTokens = new decimal_js_1.default(params.snapshot.tokenAvailable.toString());
1306
+ if (tokensToWithdraw.lte(availableTokens)) {
1307
+ return [{
1308
+ reserveAddress: params.snapshot.reserves[0].reserveAddress,
1309
+ sharesAmount: withdrawAllShares ? params.snapshot.sharesBalance : actualSharesToWithdraw,
1310
+ }];
1311
+ }
1312
+ let tokensRemaining = tokensToWithdraw;
1313
+ const plannedLegs = [];
1314
+ const sortedReserves = [...params.snapshot.reserves].sort((lhs, rhs) => rhs.availableLiquidityToWithdraw.comparedTo(lhs.availableLiquidityToWithdraw));
1315
+ for (const [index, reserve] of sortedReserves.entries()) {
1316
+ const legCapacity = reserve.availableLiquidityToWithdraw.add(index === 0 ? availableTokens : 0);
1317
+ if (legCapacity.lte(0)) {
1318
+ continue;
1319
+ }
1320
+ const tokensForLeg = decimal_js_1.default.min(tokensRemaining, legCapacity);
1321
+ if (tokensForLeg.lte(0)) {
1322
+ continue;
1323
+ }
1324
+ const sharesForLeg = withdrawAllShares
1325
+ ? params.snapshot.sharesBalance
1326
+ : new bn_js_1.default(tokensForLeg.div(tokensPerShare).floor().toFixed(0));
1327
+ if (sharesForLeg.isZero()) {
1328
+ continue;
1329
+ }
1330
+ plannedLegs.push({
1331
+ reserveAddress: reserve.reserveAddress,
1332
+ sharesAmount: sharesForLeg,
1333
+ });
1334
+ tokensRemaining = tokensRemaining.sub(tokensForLeg);
1335
+ if (tokensRemaining.lte(0)) {
1336
+ break;
1337
+ }
1338
+ }
1339
+ if (plannedLegs.length === 0) {
1340
+ throw new Error("Unable to plan a Kamino Vault withdraw across the vault's active reserves");
1341
+ }
1342
+ return plannedLegs;
1343
+ }
1344
+ exports.__kaminoVaultTesting = {
1345
+ calculateKaminoVaultTokensPerShareFromSnapshot,
1346
+ planKaminoVaultWithdrawLegsFromSnapshot,
1347
+ };
1348
+ async function resolveKaminoVaultContext(vaultAddress, setupContext) {
1349
+ const rawIndex = await (0, exponent_fetcher_1.fetchKaminoVaultIndex)({
1350
+ connection: setupContext.connection,
1351
+ kaminoVaultAccount: vaultAddress,
1352
+ });
1353
+ const reserveAddresses = rawIndex.reserves.map((reserve) => reserve.reserveAddress);
1354
+ const [vaultInfo, tokenMintInfo, sharesMintInfo, reserveInfos] = await Promise.all([
1355
+ setupContext.connection.getAccountInfo(vaultAddress),
1356
+ setupContext.connection.getAccountInfo(rawIndex.tokenMint),
1357
+ setupContext.connection.getAccountInfo(rawIndex.sharesMint),
1358
+ setupContext.connection.getMultipleAccountsInfo(reserveAddresses),
1359
+ ]);
1360
+ if (!vaultInfo?.data) {
1361
+ throw new Error(`Kamino vault account not found: ${vaultAddress.toBase58()}`);
1362
+ }
1363
+ const decodedVaultState = decodeKaminoVaultState(Buffer.from(vaultInfo.data));
1364
+ const decodedReserves = reserveInfos.map((reserveInfo, index) => {
1365
+ if (!reserveInfo?.data) {
1366
+ throw new Error(`Missing Kamino reserve account ${reserveAddresses[index].toBase58()}`);
1367
+ }
1368
+ return kamino_reserve_deserializer_1.Reserve.decode(reserveInfo.data);
1369
+ });
1370
+ const collateralMintInfos = await setupContext.connection.getMultipleAccountsInfo(decodedReserves.map((reserve) => reserve.collateral.mintPubkey));
1371
+ const normalizedReserves = reserveAddresses.map((reserveAddress, index) => {
1372
+ const reserveAccount = decodedReserves[index];
1373
+ const allocation = decodedVaultState.allocations[index];
1374
+ const rawReserve = rawIndex.reserves[index];
1375
+ const allocationOffset = KAMINO_VAULT_ACCOUNT_DISCRIMINATOR_LEN
1376
+ + KAMINO_VAULT_ALLOCATION_STRATEGY_OFFSET
1377
+ + (index * KAMINO_VAULT_ALLOCATION_SIZE);
1378
+ const ctokenVaultOffset = allocationOffset + KAMINO_VAULT_ALLOCATION_CTOKEN_VAULT_OFFSET;
1379
+ const [lendingMarketAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("lma"), reserveAccount.lendingMarket.toBuffer()], policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
1380
+ const investedLiquidityAmount = allocation
1381
+ ? new decimal_js_1.default(allocation.ctokenAllocation.toString()).mul(reserveAccount.getCollateralExchangeRate())
1382
+ : new decimal_js_1.default(0);
1383
+ const availableLiquidityToWithdraw = decimal_js_1.default.min(investedLiquidityAmount, reserveAccount.getLiquidityAvailableAmount());
1384
+ return {
1385
+ reserveAddress,
1386
+ marketAddress: rawReserve.marketAddress ?? rawReserve.reserve ?? reserveAccount.lendingMarket,
1387
+ ctokenVault: rawReserve.ctokenVault
1388
+ ?? new web3_js_1.PublicKey(vaultInfo.data.subarray(ctokenVaultOffset, ctokenVaultOffset + 32)),
1389
+ lendingMarketAuthority: rawReserve.lendingMarketAuthority ?? lendingMarketAuthority,
1390
+ pythOracle: rawReserve.pythOracle ?? reserveAccount.config.tokenInfo.pythConfiguration.price,
1391
+ switchboardPriceOracle: rawReserve.switchboardPriceOracle
1392
+ ?? reserveAccount.config.tokenInfo.switchboardConfiguration.priceAggregator,
1393
+ switchboardTwapOracle: rawReserve.switchboardTwapOracle
1394
+ ?? reserveAccount.config.tokenInfo.switchboardConfiguration.twapAggregator,
1395
+ scopePrices: rawReserve.scopePrices ?? reserveAccount.config.tokenInfo.scopeConfiguration.priceFeed,
1396
+ reserveLiquiditySupply: rawReserve.reserveLiquiditySupply ?? reserveAccount.liquidity.supplyVault,
1397
+ reserveCollateralMint: rawReserve.reserveCollateralMint ?? reserveAccount.collateral.mintPubkey,
1398
+ reserveCollateralTokenProgram: rawReserve.reserveCollateralTokenProgram ?? collateralMintInfos[index]?.owner ?? web3_js_1.PublicKey.default,
1399
+ account: reserveAccount,
1400
+ investedLiquidityAmount,
1401
+ availableLiquidityToWithdraw,
1402
+ };
1403
+ });
1404
+ const index = {
1405
+ ...rawIndex,
1406
+ reserves: normalizedReserves,
1407
+ tokenProgram: rawIndex.tokenProgram ?? tokenMintInfo?.owner ?? spl_token_1.TOKEN_PROGRAM_ID,
1408
+ sharesTokenProgram: rawIndex.sharesTokenProgram
1409
+ ?? sharesMintInfo?.owner
1410
+ ?? spl_token_1.TOKEN_PROGRAM_ID,
1411
+ vaultLookupTable: rawIndex.vaultLookupTable ?? web3_js_1.PublicKey.default,
1412
+ };
1413
+ const tokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(index.tokenMint, setupContext.owner, true, index.tokenProgram);
1414
+ const sharesAta = (0, spl_token_1.getAssociatedTokenAddressSync)(index.sharesMint, setupContext.owner, true, index.sharesTokenProgram);
1415
+ const sharesAtaInfo = await setupContext.connection.getAccountInfo(sharesAta);
1416
+ const sharesBalance = sharesAtaInfo?.data
1417
+ ? new bn_js_1.default(spl_token_1.AccountLayout.decode(sharesAtaInfo.data).amount.toString())
1418
+ : new bn_js_1.default(0);
1419
+ return { index, tokenAta, sharesAta, sharesBalance, state: decodedVaultState };
1420
+ }
1421
+ async function queueKaminoVaultSharesTracking(params) {
1422
+ const state = await loadStrategySetupState(params.setupContext);
1423
+ if (!state) {
1424
+ return;
1425
+ }
1426
+ const sharesMintKey = params.vaultContext.index.sharesMint.toBase58();
1427
+ const sharesAtaKey = params.vaultContext.sharesAta.toBase58();
1428
+ if (state.trackedTokenAccounts.has(sharesAtaKey)) {
1429
+ return;
1430
+ }
1431
+ const tokenEntryAccount = state.tokenEntryAccountByMint.get(sharesMintKey);
1432
+ if (tokenEntryAccount) {
1433
+ throw new Error(`Kamino Vault shares mint ${sharesMintKey} is already configured as a token entry on ${tokenEntryAccount}`);
1434
+ }
1435
+ const interfaceAccounts = [
1436
+ params.kaminoVaultAddress,
1437
+ ...params.vaultContext.index.reserves.map((reserve) => reserve.reserveAddress),
1438
+ ];
1439
+ const priceEntry = resolveKaminoVaultPriceEntry({
1440
+ prices: state.prices,
1441
+ sharesMint: params.vaultContext.index.sharesMint,
1442
+ depositTokenMint: params.vaultContext.index.tokenMint,
1443
+ interfaceAccounts,
1444
+ });
1445
+ const resolvedPriceId = resolveKaminoVaultTrackedPriceId({
1446
+ state,
1447
+ depositTokenMint: params.vaultContext.index.tokenMint,
1448
+ sharePriceId: priceEntry.priceId,
1449
+ label: `Kamino Vault shares tracking (${params.kaminoVaultAddress.toBase58()})`,
1450
+ });
1451
+ const remainingAccounts = (0, utils_1.uniqueRemainingAccounts)([
1452
+ { pubkey: params.vaultContext.sharesAta, isSigner: false, isWritable: false },
1453
+ { pubkey: priceEntry.priceInterfaceAccounts, isSigner: false, isWritable: false },
1454
+ ...priceEntry.interfaceAccounts.map((account) => ({
1455
+ pubkey: account,
1456
+ isSigner: false,
1457
+ isWritable: false,
1458
+ })),
1459
+ ...buildTrackedAumRemainingAccounts(state),
1460
+ ]);
1461
+ // The hook validates Kamino vault deposits against the currently tracked
1462
+ // shares ATA, so this registration must happen before the Squads sync step.
1463
+ params.buckets.preInstructions.push(state.strategyVault.ixWrapperManagerUpdatePosition({
1464
+ manager: params.setupContext.signer,
1465
+ update: exponentVaults.positionUpdate("TrackKaminoVaultShares", {
1466
+ sharesMint: params.vaultContext.index.sharesMint,
1467
+ depositTokenMint: params.vaultContext.index.tokenMint,
1468
+ sharesTokenAccount: params.vaultContext.sharesAta,
1469
+ priceInterfaceAccounts: priceEntry.priceInterfaceAccounts,
1470
+ }),
1471
+ remainingAccounts,
1472
+ }));
1473
+ trackRequiredPriceIds(state.requiredPriceIds, resolvedPriceId);
1474
+ if (state.tokenPositionIndexByMint.get(sharesMintKey) === undefined) {
1475
+ recordPlannedTokenAccountEntry(state, {
1476
+ tokenMint: params.vaultContext.index.sharesMint,
1477
+ tokenAccount: params.vaultContext.sharesAta,
1478
+ priceId: resolvedPriceId,
1479
+ });
1480
+ }
1481
+ else {
1482
+ recordPlannedTokenAccountBalance(state, {
1483
+ tokenMint: params.vaultContext.index.sharesMint,
1484
+ tokenAccount: params.vaultContext.sharesAta,
1485
+ priceId: resolvedPriceId,
1486
+ });
1487
+ }
1488
+ }
1489
+ async function resolveKaminoVaultValidationAccounts(params) {
1490
+ const state = await loadStrategySetupState(params.setupContext);
1491
+ if (!state) {
1492
+ return [];
1493
+ }
1494
+ const priceEntry = resolveKaminoVaultPriceEntry({
1495
+ prices: state.prices,
1496
+ sharesMint: params.vaultContext.index.sharesMint,
1497
+ depositTokenMint: params.vaultContext.index.tokenMint,
1498
+ interfaceAccounts: [
1499
+ params.kaminoVaultAddress,
1500
+ ...params.vaultContext.index.reserves.map((reserve) => reserve.reserveAddress),
1501
+ ],
1502
+ });
1503
+ return [
1504
+ { pubkey: priceEntry.priceInterfaceAccounts, isSigner: false, isWritable: false },
1505
+ ];
1506
+ }
1507
+ function buildKaminoVaultDepositInstruction(params) {
1508
+ const reserveAccounts = params.vaultContext.index.reserves.flatMap((reserve) => [
1509
+ { pubkey: reserve.reserveAddress, isSigner: false, isWritable: true },
1510
+ { pubkey: reserve.ctokenVault, isSigner: false, isWritable: true },
1511
+ { pubkey: reserve.marketAddress, isSigner: false, isWritable: false },
1512
+ { pubkey: reserve.lendingMarketAuthority, isSigner: false, isWritable: false },
1513
+ { pubkey: reserve.reserveLiquiditySupply, isSigner: false, isWritable: true },
1514
+ { pubkey: reserve.reserveCollateralMint, isSigner: false, isWritable: true },
1515
+ { pubkey: reserve.reserveCollateralTokenProgram, isSigner: false, isWritable: false },
1516
+ { pubkey: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false },
1517
+ { pubkey: KAMINO_VAULT_EVENT_AUTHORITY, isSigner: false, isWritable: false },
1518
+ { pubkey: policyBuilders_1.KAMINO_VAULT_PROGRAM_ID, isSigner: false, isWritable: false },
1519
+ ]);
1520
+ return new web3_js_1.TransactionInstruction({
1521
+ programId: policyBuilders_1.KAMINO_VAULT_PROGRAM_ID,
1522
+ keys: [
1523
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1524
+ { pubkey: params.kaminoVaultAddress, isSigner: false, isWritable: true },
1525
+ { pubkey: params.vaultContext.index.tokenVault, isSigner: false, isWritable: true },
1526
+ { pubkey: params.vaultContext.index.tokenMint, isSigner: false, isWritable: false },
1527
+ { pubkey: params.vaultContext.index.baseVaultAuthority, isSigner: false, isWritable: false },
1528
+ { pubkey: params.vaultContext.index.sharesMint, isSigner: false, isWritable: true },
1529
+ { pubkey: params.vaultContext.tokenAta, isSigner: false, isWritable: true },
1530
+ { pubkey: params.vaultContext.sharesAta, isSigner: false, isWritable: true },
1531
+ { pubkey: policyBuilders_1.KAMINO_LENDING_PROGRAM_ID, isSigner: false, isWritable: false },
1532
+ { pubkey: params.vaultContext.index.tokenProgram, isSigner: false, isWritable: false },
1533
+ { pubkey: params.vaultContext.index.sharesTokenProgram, isSigner: false, isWritable: false },
1534
+ { pubkey: KAMINO_VAULT_EVENT_AUTHORITY, isSigner: false, isWritable: false },
1535
+ { pubkey: policyBuilders_1.KAMINO_VAULT_PROGRAM_ID, isSigner: false, isWritable: false },
1536
+ ...reserveAccounts,
1537
+ ...params.vaultContext.index.reserves.map((reserve) => ({
1538
+ pubkey: reserve.reserveAddress,
1539
+ isSigner: false,
1540
+ isWritable: true,
1541
+ })),
1542
+ ...(params.validationAccounts ?? []),
1543
+ ],
1544
+ data: encodeU64InstructionData(policyBuilders_1.KAMINO_VAULT_DISCRIMINATORS.deposit, params.amount),
1545
+ });
1546
+ }
1547
+ function resolveExplicitKaminoVaultWithdrawReserve(ix, vaultContext) {
1548
+ if (!ix.reserve) {
1549
+ throw new Error("reserve is required when resolving an explicit Kamino Vault withdraw reserve");
1550
+ }
1551
+ const reserve = vaultContext.index.reserves.find((entry) => entry.reserveAddress.equals(ix.reserve));
1552
+ if (!reserve) {
1553
+ throw new Error(`Kamino Vault ${ix.vault.toBase58()} does not use reserve ${ix.reserve.toBase58()}`);
1554
+ }
1555
+ return reserve;
1556
+ }
1557
+ function planKaminoVaultWithdrawLegs(ix, vaultContext) {
1558
+ if (ix.reserve) {
1559
+ return [{
1560
+ reserve: resolveExplicitKaminoVaultWithdrawReserve(ix, vaultContext),
1561
+ sharesAmount: ix.sharesAmount,
1562
+ }];
1563
+ }
1564
+ const plannedLegs = planKaminoVaultWithdrawLegsFromSnapshot({
1565
+ sharesAmount: ix.sharesAmount,
1566
+ snapshot: {
1567
+ sharesBalance: vaultContext.sharesBalance,
1568
+ tokenAvailable: vaultContext.state.tokenAvailable,
1569
+ sharesIssued: vaultContext.state.sharesIssued,
1570
+ pendingFeesSf: vaultContext.state.pendingFeesSf,
1571
+ reserves: vaultContext.index.reserves.map((reserve) => ({
1572
+ reserveAddress: reserve.reserveAddress,
1573
+ investedLiquidityAmount: reserve.investedLiquidityAmount,
1574
+ availableLiquidityToWithdraw: reserve.availableLiquidityToWithdraw,
1575
+ })),
1576
+ },
1577
+ });
1578
+ return plannedLegs.map((leg) => {
1579
+ const reserve = vaultContext.index.reserves.find((entry) => entry.reserveAddress.equals(leg.reserveAddress));
1580
+ if (!reserve) {
1581
+ throw new Error(`Kamino Vault ${ix.vault.toBase58()} does not use reserve ${leg.reserveAddress.toBase58()}`);
1582
+ }
1583
+ return {
1584
+ reserve,
1585
+ sharesAmount: leg.sharesAmount,
1586
+ };
1587
+ });
1588
+ }
1589
+ function buildKaminoVaultWithdrawInstruction(params) {
1590
+ const [globalConfig] = web3_js_1.PublicKey.findProgramAddressSync([KAMINO_VAULT_GLOBAL_CONFIG_SEED], policyBuilders_1.KAMINO_VAULT_PROGRAM_ID);
1591
+ return new web3_js_1.TransactionInstruction({
1592
+ programId: policyBuilders_1.KAMINO_VAULT_PROGRAM_ID,
1593
+ keys: [
1594
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1595
+ { pubkey: params.ix.vault, isSigner: false, isWritable: true },
1596
+ { pubkey: globalConfig, isSigner: false, isWritable: false },
1597
+ { pubkey: params.vaultContext.index.tokenVault, isSigner: false, isWritable: true },
1598
+ { pubkey: params.vaultContext.index.baseVaultAuthority, isSigner: false, isWritable: false },
1599
+ { pubkey: params.vaultContext.tokenAta, isSigner: false, isWritable: true },
1600
+ { pubkey: params.vaultContext.index.tokenMint, isSigner: false, isWritable: true },
1601
+ { pubkey: params.vaultContext.sharesAta, isSigner: false, isWritable: true },
1602
+ { pubkey: params.vaultContext.index.sharesMint, isSigner: false, isWritable: true },
1603
+ { pubkey: params.vaultContext.index.tokenProgram, isSigner: false, isWritable: false },
1604
+ { pubkey: params.vaultContext.index.sharesTokenProgram, isSigner: false, isWritable: false },
1605
+ { pubkey: policyBuilders_1.KAMINO_LENDING_PROGRAM_ID, isSigner: false, isWritable: false },
1606
+ { pubkey: KAMINO_VAULT_EVENT_AUTHORITY, isSigner: false, isWritable: false },
1607
+ { pubkey: policyBuilders_1.KAMINO_VAULT_PROGRAM_ID, isSigner: false, isWritable: false },
1608
+ { pubkey: params.ix.vault, isSigner: false, isWritable: true },
1609
+ { pubkey: params.reserve.reserveAddress, isSigner: false, isWritable: true },
1610
+ { pubkey: params.reserve.ctokenVault, isSigner: false, isWritable: true },
1611
+ { pubkey: params.reserve.marketAddress, isSigner: false, isWritable: false },
1612
+ { pubkey: params.reserve.lendingMarketAuthority, isSigner: false, isWritable: false },
1613
+ { pubkey: params.reserve.reserveLiquiditySupply, isSigner: false, isWritable: true },
1614
+ { pubkey: params.reserve.reserveCollateralMint, isSigner: false, isWritable: true },
1615
+ { pubkey: params.reserve.reserveCollateralTokenProgram, isSigner: false, isWritable: false },
1616
+ { pubkey: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY, isSigner: false, isWritable: false },
1617
+ { pubkey: KAMINO_VAULT_EVENT_AUTHORITY, isSigner: false, isWritable: false },
1618
+ { pubkey: policyBuilders_1.KAMINO_VAULT_PROGRAM_ID, isSigner: false, isWritable: false },
1619
+ ...params.vaultContext.index.reserves.map((entry) => ({
1620
+ pubkey: entry.reserveAddress,
1621
+ isSigner: false,
1622
+ isWritable: true,
1623
+ })),
1624
+ ...(params.validationAccounts ?? []),
1625
+ ],
1626
+ data: encodeU64InstructionData(policyBuilders_1.KAMINO_VAULT_DISCRIMINATORS.withdraw, params.sharesAmount),
1627
+ });
1628
+ }
1629
+ async function buildKaminoVaultInstruction(ix, buckets, setupContext) {
1630
+ const vaultContext = await resolveKaminoVaultContext(ix.vault, setupContext);
1631
+ const validationAccounts = await resolveKaminoVaultValidationAccounts({
1632
+ setupContext,
1633
+ kaminoVaultAddress: ix.vault,
1634
+ vaultContext,
1635
+ });
1636
+ buckets.setupInstructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(setupContext.signer, vaultContext.sharesAta, setupContext.owner, vaultContext.index.sharesMint, vaultContext.index.sharesTokenProgram));
1637
+ if (setupContext.autoManagePositions) {
1638
+ await ensureTrackedTokenAccountSetup({
1639
+ tokenMint: vaultContext.index.tokenMint,
1640
+ tokenAccount: vaultContext.tokenAta,
1641
+ tokenProgram: vaultContext.index.tokenProgram,
1642
+ buckets,
1643
+ setupContext,
1644
+ });
1645
+ }
1646
+ const vaultLookupTable = vaultContext.index.vaultLookupTable ?? web3_js_1.PublicKey.default;
1647
+ if (!vaultLookupTable.equals(web3_js_1.PublicKey.default)) {
1648
+ buckets.addressLookupTableAddresses.push(vaultLookupTable);
1649
+ }
1650
+ if (ix.action === vault_instruction_types_2.KaminoVaultAction.DEPOSIT) {
1651
+ buckets.syncInstructions.push(buildKaminoVaultDepositInstruction({
1652
+ owner: setupContext.owner,
1653
+ kaminoVaultAddress: ix.vault,
1654
+ vaultContext,
1655
+ amount: ix.amount,
1656
+ validationAccounts,
1657
+ }));
1658
+ if (isAutoManagePositionsEnabled(setupContext)) {
1659
+ await queueKaminoVaultSharesTracking({
1660
+ setupContext,
1661
+ buckets,
1662
+ kaminoVaultAddress: ix.vault,
1663
+ vaultContext,
1664
+ });
1665
+ }
1666
+ return;
1667
+ }
1668
+ const withdrawLegs = planKaminoVaultWithdrawLegs(ix, vaultContext);
1669
+ for (const leg of withdrawLegs) {
1670
+ buckets.syncInstructions.push(buildKaminoVaultWithdrawInstruction({
1671
+ owner: setupContext.owner,
1672
+ ix,
1673
+ reserve: leg.reserve,
1674
+ sharesAmount: leg.sharesAmount,
1675
+ vaultContext,
1676
+ validationAccounts,
1677
+ }));
659
1678
  }
660
- state.trackedTokenAccounts.add(tokenAccountKey);
661
1679
  }
662
- async function buildVaultInstructions(instructions, owner, connection, signer, policyPda, vaultPda, accountIndex, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram = syncTransaction_1.SQUADS_PROGRAM_ID) {
663
- const buckets = {
664
- setupInstructions: [],
665
- syncInstructions: [],
666
- preInstructions: [],
667
- postInstructions: [],
668
- signers: [],
669
- addressLookupTableAddresses: [],
1680
+ function getOptionalReadonlyAccountMeta(account, placeholderProgram) {
1681
+ return {
1682
+ pubkey: account ?? placeholderProgram,
1683
+ isSigner: false,
1684
+ isWritable: false,
670
1685
  };
671
- const setupContext = createStrategySetupContext({
672
- connection,
673
- env: environment_1.LOCAL_ENV,
674
- owner,
675
- signer,
676
- vaultAddress,
677
- policyPda,
678
- vaultPda,
679
- accountIndex,
680
- squadsProgram,
681
- leadingAccounts,
682
- preHookAccounts,
683
- postHookAccounts,
1686
+ }
1687
+ async function resolveKaminoFarmContext(ix, setupContext) {
1688
+ const farmInfo = await setupContext.connection.getAccountInfo(ix.farmState);
1689
+ if (!farmInfo?.data) {
1690
+ throw new Error(`Kamino farm not found: ${ix.farmState.toBase58()}`);
1691
+ }
1692
+ const farm = (0, kamino_farms_1.decodeKaminoFarmState)(Buffer.from(farmInfo.data));
1693
+ const delegatee = ix.delegatee ?? setupContext.owner;
1694
+ const userState = getKaminoFarmUserStateAddress(delegatee, ix.farmState);
1695
+ const sourceAta = (0, spl_token_1.getAssociatedTokenAddressSync)(farm.underlyingMint, setupContext.owner, true, farm.tokenProgram);
1696
+ return {
1697
+ farm,
1698
+ farmState: ix.farmState,
1699
+ userState,
1700
+ delegatee,
1701
+ sourceAta,
1702
+ scopePrices: (0, kamino_farms_1.getKaminoFarmScopePricesAddress)(farm),
1703
+ };
1704
+ }
1705
+ function buildKaminoFarmInitializeUserRawInstruction(params) {
1706
+ return new web3_js_1.TransactionInstruction({
1707
+ programId: KAMINO_FARMS_PROGRAM_ID,
1708
+ keys: [
1709
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1710
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1711
+ { pubkey: params.owner, isSigner: false, isWritable: false },
1712
+ { pubkey: params.delegatee, isSigner: false, isWritable: false },
1713
+ { pubkey: params.userState, isSigner: false, isWritable: true },
1714
+ { pubkey: params.farmState, isSigner: false, isWritable: true },
1715
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
1716
+ { pubkey: web3_js_1.SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
1717
+ ],
1718
+ data: Buffer.from(policyBuilders_1.KAMINO_FARM_DISCRIMINATORS.initializeUser),
684
1719
  });
685
- for (const ix of instructions) {
686
- if (isOrderbookInstruction(ix)) {
687
- await buildOrderbookInstruction(ix, owner, connection, buckets, setupContext);
688
- continue;
689
- }
690
- if (isCoreInstruction(ix)) {
691
- await buildCoreInstruction(ix, owner, connection, buckets, setupContext);
692
- continue;
693
- }
694
- if (isSyInstruction(ix)) {
695
- await buildSyInstruction(ix, owner, connection, buckets, setupContext);
696
- continue;
1720
+ }
1721
+ async function ensureKaminoFarmUserSetup(params) {
1722
+ if (await accountExistsMaybeTracked(params.setupContext, params.farmContext.userState)) {
1723
+ return;
1724
+ }
1725
+ const requiredLamports = await params.setupContext.connection.getMinimumBalanceForRentExemption(KAMINO_FARM_USER_STATE_SIZE);
1726
+ const ownerLamports = await params.setupContext.connection.getBalance(params.setupContext.owner);
1727
+ if (ownerLamports < requiredLamports) {
1728
+ params.buckets.setupInstructions.push(web3_js_1.SystemProgram.transfer({
1729
+ fromPubkey: params.setupContext.signer,
1730
+ toPubkey: params.setupContext.owner,
1731
+ lamports: requiredLamports - ownerLamports,
1732
+ }));
1733
+ }
1734
+ params.buckets.syncInstructions.push(buildKaminoFarmInitializeUserRawInstruction({
1735
+ owner: params.setupContext.owner,
1736
+ delegatee: params.farmContext.delegatee,
1737
+ userState: params.farmContext.userState,
1738
+ farmState: params.farmContext.farmState,
1739
+ }));
1740
+ const state = await loadStrategySetupState(params.setupContext);
1741
+ state?.existingAccounts.set(params.farmContext.userState.toBase58(), true);
1742
+ }
1743
+ function buildKaminoFarmStakeInstructionRaw(params) {
1744
+ const amount = params.amount === "ALL" ? KAMINO_STAKE_ALL_AMOUNT : params.amount;
1745
+ return new web3_js_1.TransactionInstruction({
1746
+ programId: KAMINO_FARMS_PROGRAM_ID,
1747
+ keys: [
1748
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1749
+ { pubkey: params.farmContext.userState, isSigner: false, isWritable: true },
1750
+ { pubkey: params.farmContext.farmState, isSigner: false, isWritable: true },
1751
+ { pubkey: params.farmContext.farm.farmVault, isSigner: false, isWritable: true },
1752
+ { pubkey: params.farmContext.sourceAta, isSigner: false, isWritable: true },
1753
+ { pubkey: params.farmContext.farm.underlyingMint, isSigner: false, isWritable: false },
1754
+ getOptionalReadonlyAccountMeta(params.farmContext.scopePrices, KAMINO_FARMS_PROGRAM_ID),
1755
+ { pubkey: params.farmContext.farm.tokenProgram, isSigner: false, isWritable: false },
1756
+ ],
1757
+ data: encodeU64InstructionData(policyBuilders_1.KAMINO_FARM_DISCRIMINATORS.stake, amount),
1758
+ });
1759
+ }
1760
+ function buildKaminoFarmUnstakeInstructionRaw(params) {
1761
+ return new web3_js_1.TransactionInstruction({
1762
+ programId: KAMINO_FARMS_PROGRAM_ID,
1763
+ keys: [
1764
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1765
+ { pubkey: params.farmContext.userState, isSigner: false, isWritable: true },
1766
+ { pubkey: params.farmContext.farmState, isSigner: false, isWritable: true },
1767
+ getOptionalReadonlyAccountMeta(params.farmContext.scopePrices, KAMINO_FARMS_PROGRAM_ID),
1768
+ ],
1769
+ data: encodeU128InstructionData(policyBuilders_1.KAMINO_FARM_DISCRIMINATORS.unstake, params.stakeSharesScaled),
1770
+ });
1771
+ }
1772
+ function buildKaminoFarmWithdrawUnstakedDepositsRawInstruction(params) {
1773
+ return new web3_js_1.TransactionInstruction({
1774
+ programId: KAMINO_FARMS_PROGRAM_ID,
1775
+ keys: [
1776
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1777
+ { pubkey: params.farmContext.userState, isSigner: false, isWritable: true },
1778
+ { pubkey: params.farmContext.farmState, isSigner: false, isWritable: true },
1779
+ { pubkey: params.farmContext.sourceAta, isSigner: false, isWritable: true },
1780
+ { pubkey: params.farmContext.farm.farmVault, isSigner: false, isWritable: true },
1781
+ { pubkey: params.farmContext.farm.farmVaultsAuthority, isSigner: false, isWritable: false },
1782
+ { pubkey: params.farmContext.farm.tokenProgram, isSigner: false, isWritable: false },
1783
+ ],
1784
+ data: Buffer.from(policyBuilders_1.KAMINO_FARM_DISCRIMINATORS.withdrawUnstakedDeposits),
1785
+ });
1786
+ }
1787
+ function buildKaminoFarmHarvestRewardRawInstruction(params) {
1788
+ const rewardInfo = params.farmContext.farm.rewardInfos[params.rewardIndex];
1789
+ if (!rewardInfo) {
1790
+ throw new Error(`Reward index ${params.rewardIndex} is out of range for Kamino farm ${params.farmContext.farmState.toBase58()}`);
1791
+ }
1792
+ return new web3_js_1.TransactionInstruction({
1793
+ programId: KAMINO_FARMS_PROGRAM_ID,
1794
+ keys: [
1795
+ { pubkey: params.owner, isSigner: true, isWritable: true },
1796
+ { pubkey: params.farmContext.userState, isSigner: false, isWritable: true },
1797
+ { pubkey: params.farmContext.farmState, isSigner: false, isWritable: true },
1798
+ { pubkey: params.farmContext.farm.globalConfig, isSigner: false, isWritable: false },
1799
+ { pubkey: rewardInfo.rewardMint, isSigner: false, isWritable: false },
1800
+ { pubkey: params.rewardAta, isSigner: false, isWritable: true },
1801
+ { pubkey: rewardInfo.rewardsVault, isSigner: false, isWritable: true },
1802
+ {
1803
+ pubkey: (0, constants_1.getKaminoFarmsRewardsTreasuryVault)(rewardInfo.rewardMint, params.farmContext.farm.globalConfig, KAMINO_FARMS_PROGRAM_ID),
1804
+ isSigner: false,
1805
+ isWritable: true,
1806
+ },
1807
+ { pubkey: params.farmContext.farm.farmVaultsAuthority, isSigner: false, isWritable: false },
1808
+ getOptionalReadonlyAccountMeta(params.farmContext.scopePrices, KAMINO_FARMS_PROGRAM_ID),
1809
+ { pubkey: rewardInfo.tokenProgram, isSigner: false, isWritable: false },
1810
+ ],
1811
+ data: encodeU64InstructionData(policyBuilders_1.KAMINO_FARM_DISCRIMINATORS.harvestReward, params.rewardIndex),
1812
+ });
1813
+ }
1814
+ async function buildKaminoFarmInstruction(ix, buckets, setupContext) {
1815
+ const farmContext = await resolveKaminoFarmContext(ix, setupContext);
1816
+ switch (ix.action) {
1817
+ case vault_instruction_types_2.KaminoFarmAction.INITIALIZE_USER: {
1818
+ if (farmContext.farm.isDelegated && !farmContext.delegatee.equals(setupContext.owner)) {
1819
+ throw new Error(`Delegated Kamino farm initialization is not supported for ${ix.farmState.toBase58()}`);
1820
+ }
1821
+ await ensureKaminoFarmUserSetup({ farmContext, buckets, setupContext });
1822
+ return;
697
1823
  }
698
- if (isTitanInstruction(ix)) {
699
- await buildTitanInstruction(ix, buckets, setupContext);
700
- continue;
1824
+ case vault_instruction_types_2.KaminoFarmAction.STAKE: {
1825
+ if (farmContext.farm.isDelegated) {
1826
+ throw new Error(`Kamino farm ${ix.farmState.toBase58()} is delegated and cannot be staked directly`);
1827
+ }
1828
+ if (isAutoManagePositionsEnabled(setupContext)) {
1829
+ await ensureKaminoFarmUserSetup({ farmContext, buckets, setupContext });
1830
+ await ensureTrackedTokenAccountSetup({
1831
+ tokenMint: farmContext.farm.underlyingMint,
1832
+ tokenAccount: farmContext.sourceAta,
1833
+ tokenProgram: farmContext.farm.tokenProgram,
1834
+ buckets,
1835
+ setupContext,
1836
+ });
1837
+ }
1838
+ buckets.syncInstructions.push(buildKaminoFarmStakeInstructionRaw({
1839
+ owner: setupContext.owner,
1840
+ farmContext,
1841
+ amount: ix.amount,
1842
+ }));
1843
+ if (isAutoManagePositionsEnabled(setupContext)) {
1844
+ const state = await loadStrategySetupState(setupContext);
1845
+ if (state) {
1846
+ recordPlannedKaminoFarmPosition(state, {
1847
+ farmState: ix.farmState,
1848
+ userState: farmContext.userState,
1849
+ globalConfig: farmContext.farm.globalConfig,
1850
+ scopePrices: farmContext.scopePrices,
1851
+ });
1852
+ }
1853
+ }
1854
+ return;
701
1855
  }
702
- if (isLoopscaleInstruction(ix)) {
703
- await buildLoopscaleInstruction(ix, buckets, setupContext);
704
- continue;
1856
+ case vault_instruction_types_2.KaminoFarmAction.UNSTAKE: {
1857
+ if (farmContext.farm.isDelegated) {
1858
+ throw new Error(`Kamino farm ${ix.farmState.toBase58()} is delegated and cannot be unstaked directly`);
1859
+ }
1860
+ buckets.syncInstructions.push(buildKaminoFarmUnstakeInstructionRaw({
1861
+ owner: setupContext.owner,
1862
+ farmContext,
1863
+ stakeSharesScaled: ix.stakeSharesScaled,
1864
+ }));
1865
+ return;
705
1866
  }
706
- if (isClmmInstruction(ix)) {
707
- await buildClmmInstruction(ix, owner, connection, buckets, setupContext);
708
- continue;
1867
+ case vault_instruction_types_2.KaminoFarmAction.WITHDRAW_UNSTAKED_DEPOSITS: {
1868
+ if (farmContext.farm.isDelegated) {
1869
+ throw new Error(`Kamino farm ${ix.farmState.toBase58()} is delegated and cannot withdraw unstaked deposits directly`);
1870
+ }
1871
+ buckets.syncInstructions.push(buildKaminoFarmWithdrawUnstakedDepositsRawInstruction({
1872
+ owner: setupContext.owner,
1873
+ farmContext,
1874
+ }));
1875
+ return;
709
1876
  }
710
- switch (ix.action) {
711
- case VaultAction.INIT_USER_METADATA:
712
- await buildInitUserMetadata(owner, connection, buckets);
713
- break;
714
- case VaultAction.INIT_OBLIGATION:
715
- await buildInitObligation(ix, owner, connection, buckets);
716
- break;
717
- case VaultAction.DEPOSIT:
718
- await buildDeposit(ix, owner, connection, signer, buckets);
719
- break;
720
- case VaultAction.WITHDRAW:
721
- await buildWithdraw(ix, owner, connection, signer, buckets);
722
- break;
723
- case VaultAction.BORROW:
724
- await buildBorrow(ix, owner, connection, signer, buckets);
725
- break;
726
- case VaultAction.REPAY:
727
- await buildRepay(ix, owner, connection, signer, buckets);
728
- break;
1877
+ case vault_instruction_types_2.KaminoFarmAction.HARVEST_REWARD: {
1878
+ const rewardInfo = farmContext.farm.rewardInfos[ix.rewardIndex];
1879
+ if (!rewardInfo) {
1880
+ throw new Error(`Reward index ${ix.rewardIndex} is out of range for Kamino farm ${ix.farmState.toBase58()}`);
1881
+ }
1882
+ const rewardAta = (0, spl_token_1.getAssociatedTokenAddressSync)(rewardInfo.rewardMint, setupContext.owner, true, rewardInfo.tokenProgram);
1883
+ if (isAutoManagePositionsEnabled(setupContext)) {
1884
+ await ensureTrackedTokenAccountSetup({
1885
+ tokenMint: rewardInfo.rewardMint,
1886
+ tokenAccount: rewardAta,
1887
+ tokenProgram: rewardInfo.tokenProgram,
1888
+ buckets,
1889
+ setupContext,
1890
+ });
1891
+ }
1892
+ buckets.setupInstructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(setupContext.signer, rewardAta, setupContext.owner, rewardInfo.rewardMint, rewardInfo.tokenProgram));
1893
+ buckets.syncInstructions.push(buildKaminoFarmHarvestRewardRawInstruction({
1894
+ owner: setupContext.owner,
1895
+ farmContext,
1896
+ rewardIndex: ix.rewardIndex,
1897
+ rewardAta,
1898
+ }));
1899
+ return;
729
1900
  }
730
1901
  }
731
- return buckets;
732
1902
  }
733
1903
  async function buildTitanInstruction(ix, buckets, setupContext) {
734
1904
  const inputMint = ix.instruction.keys[TITAN_INPUT_MINT_ACCOUNT_INDEX]?.pubkey;
@@ -759,94 +1929,37 @@ async function buildTitanInstruction(ix, buckets, setupContext) {
759
1929
  buckets,
760
1930
  setupContext,
761
1931
  });
1932
+ if (ix.addressLookupTableAddresses?.length) {
1933
+ buckets.addressLookupTableAddresses.push(...ix.addressLookupTableAddresses);
1934
+ }
762
1935
  buckets.syncInstructions.push(ix.instruction);
763
1936
  }
764
- // Loopscale account indices per instruction (from the Loopscale IDL)
765
- const LOOPSCALE_DEPOSIT_COLLATERAL_MINT_INDEX = 6;
766
- const LOOPSCALE_DEPOSIT_COLLATERAL_BORROWER_TA_INDEX = 4;
767
- const LOOPSCALE_DEPOSIT_COLLATERAL_TOKEN_PROGRAM_INDEX = 9;
768
- const LOOPSCALE_BORROW_PRINCIPAL_MINT_INDEX = 6;
769
- const LOOPSCALE_BORROW_PRINCIPAL_BORROWER_TA_INDEX = 7;
770
- const LOOPSCALE_BORROW_PRINCIPAL_TOKEN_PROGRAM_INDEX = 10;
771
- const LOOPSCALE_REPAY_PRINCIPAL_MINT_INDEX = 6;
772
- const LOOPSCALE_REPAY_PRINCIPAL_BORROWER_TA_INDEX = 7;
773
- const LOOPSCALE_REPAY_PRINCIPAL_TOKEN_PROGRAM_INDEX = 10;
774
- const LOOPSCALE_WITHDRAW_COLLATERAL_MINT_INDEX = 7;
775
- const LOOPSCALE_WITHDRAW_COLLATERAL_BORROWER_TA_INDEX = 4;
776
- const LOOPSCALE_WITHDRAW_COLLATERAL_TOKEN_PROGRAM_INDEX = 8;
777
- const LOOPSCALE_DEPOSIT_STRATEGY_MINT_INDEX = 4;
778
- const LOOPSCALE_DEPOSIT_STRATEGY_LENDER_TA_INDEX = 6;
779
- const LOOPSCALE_DEPOSIT_STRATEGY_TOKEN_PROGRAM_INDEX = 8;
780
- const LOOPSCALE_WITHDRAW_STRATEGY_MINT_INDEX = 4;
781
- const LOOPSCALE_WITHDRAW_STRATEGY_LENDER_TA_INDEX = 6;
782
- const LOOPSCALE_WITHDRAW_STRATEGY_TOKEN_PROGRAM_INDEX = 9;
1937
+ /**
1938
+ * Loopscale account index mapping per instruction (from the Loopscale IDL).
1939
+ * Actions not listed here (create_loan, close_loan, update_weight_matrix,
1940
+ * create_strategy, close_strategy, lock_loan, unlock_loan, refinance_ledger,
1941
+ * update_strategy) have no token accounts to track — the on-chain hook handles
1942
+ * TrackLoopscaleLoan/UntrackLoopscaleLoan mutations.
1943
+ */
1944
+ const LOOPSCALE_TOKEN_INDICES = {
1945
+ [vault_instruction_types_2.LoopscaleAction.DEPOSIT_COLLATERAL]: { mint: 6, account: 4, program: 9 },
1946
+ [vault_instruction_types_2.LoopscaleAction.BORROW_PRINCIPAL]: { mint: 6, account: 7, program: 10 },
1947
+ [vault_instruction_types_2.LoopscaleAction.REPAY_PRINCIPAL]: { mint: 6, account: 7, program: 10 },
1948
+ [vault_instruction_types_2.LoopscaleAction.WITHDRAW_COLLATERAL]: { mint: 7, account: 4, program: 8 },
1949
+ [vault_instruction_types_2.LoopscaleAction.DEPOSIT_STRATEGY]: { mint: 4, account: 6, program: 8 },
1950
+ [vault_instruction_types_2.LoopscaleAction.WITHDRAW_STRATEGY]: { mint: 4, account: 6, program: 9 },
1951
+ };
783
1952
  /** Build a single Loopscale instruction (loan or strategy). Extracts token accounts for tracking. */
784
1953
  async function buildLoopscaleInstruction(ix, buckets, setupContext) {
785
- switch (ix.action) {
786
- case LoopscaleAction.DEPOSIT_COLLATERAL: {
787
- const tokenMint = ix.instruction.keys[LOOPSCALE_DEPOSIT_COLLATERAL_MINT_INDEX]?.pubkey;
788
- const tokenAccount = ix.instruction.keys[LOOPSCALE_DEPOSIT_COLLATERAL_BORROWER_TA_INDEX]?.pubkey;
789
- const tokenProgram = ix.instruction.keys[LOOPSCALE_DEPOSIT_COLLATERAL_TOKEN_PROGRAM_INDEX]?.pubkey;
790
- if (!tokenMint || !tokenAccount || !tokenProgram) {
791
- throw new Error("Loopscale deposit_collateral instruction is missing expected token accounts");
792
- }
793
- await ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenProgram, buckets, setupContext });
794
- break;
795
- }
796
- case LoopscaleAction.BORROW_PRINCIPAL: {
797
- const tokenMint = ix.instruction.keys[LOOPSCALE_BORROW_PRINCIPAL_MINT_INDEX]?.pubkey;
798
- const tokenAccount = ix.instruction.keys[LOOPSCALE_BORROW_PRINCIPAL_BORROWER_TA_INDEX]?.pubkey;
799
- const tokenProgram = ix.instruction.keys[LOOPSCALE_BORROW_PRINCIPAL_TOKEN_PROGRAM_INDEX]?.pubkey;
800
- if (!tokenMint || !tokenAccount || !tokenProgram) {
801
- throw new Error("Loopscale borrow_principal instruction is missing expected token accounts");
802
- }
803
- await ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenProgram, buckets, setupContext });
804
- break;
805
- }
806
- case LoopscaleAction.REPAY_PRINCIPAL: {
807
- const tokenMint = ix.instruction.keys[LOOPSCALE_REPAY_PRINCIPAL_MINT_INDEX]?.pubkey;
808
- const tokenAccount = ix.instruction.keys[LOOPSCALE_REPAY_PRINCIPAL_BORROWER_TA_INDEX]?.pubkey;
809
- const tokenProgram = ix.instruction.keys[LOOPSCALE_REPAY_PRINCIPAL_TOKEN_PROGRAM_INDEX]?.pubkey;
810
- if (!tokenMint || !tokenAccount || !tokenProgram) {
811
- throw new Error("Loopscale repay_principal instruction is missing expected token accounts");
812
- }
813
- await ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenProgram, buckets, setupContext });
814
- break;
815
- }
816
- case LoopscaleAction.WITHDRAW_COLLATERAL: {
817
- const tokenMint = ix.instruction.keys[LOOPSCALE_WITHDRAW_COLLATERAL_MINT_INDEX]?.pubkey;
818
- const tokenAccount = ix.instruction.keys[LOOPSCALE_WITHDRAW_COLLATERAL_BORROWER_TA_INDEX]?.pubkey;
819
- const tokenProgram = ix.instruction.keys[LOOPSCALE_WITHDRAW_COLLATERAL_TOKEN_PROGRAM_INDEX]?.pubkey;
820
- if (!tokenMint || !tokenAccount || !tokenProgram) {
821
- throw new Error("Loopscale withdraw_collateral instruction is missing expected token accounts");
822
- }
823
- await ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenProgram, buckets, setupContext });
824
- break;
825
- }
826
- // create_loan, close_loan, and update_weight_matrix have no token accounts to track —
827
- // the on-chain hook handles TrackLoopscaleLoan/UntrackLoopscaleLoan mutations.
828
- // update_weight_matrix only has 3 accounts (bs_auth, borrower, loan).
829
- case LoopscaleAction.DEPOSIT_STRATEGY: {
830
- const tokenMint = ix.instruction.keys[LOOPSCALE_DEPOSIT_STRATEGY_MINT_INDEX]?.pubkey;
831
- const tokenAccount = ix.instruction.keys[LOOPSCALE_DEPOSIT_STRATEGY_LENDER_TA_INDEX]?.pubkey;
832
- const tokenProgram = ix.instruction.keys[LOOPSCALE_DEPOSIT_STRATEGY_TOKEN_PROGRAM_INDEX]?.pubkey;
833
- if (!tokenMint || !tokenAccount || !tokenProgram) {
834
- throw new Error("Loopscale deposit_strategy instruction is missing expected token accounts");
835
- }
836
- await ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenProgram, buckets, setupContext });
837
- break;
838
- }
839
- case LoopscaleAction.WITHDRAW_STRATEGY: {
840
- const tokenMint = ix.instruction.keys[LOOPSCALE_WITHDRAW_STRATEGY_MINT_INDEX]?.pubkey;
841
- const tokenAccount = ix.instruction.keys[LOOPSCALE_WITHDRAW_STRATEGY_LENDER_TA_INDEX]?.pubkey;
842
- const tokenProgram = ix.instruction.keys[LOOPSCALE_WITHDRAW_STRATEGY_TOKEN_PROGRAM_INDEX]?.pubkey;
843
- if (!tokenMint || !tokenAccount || !tokenProgram) {
844
- throw new Error("Loopscale withdraw_strategy instruction is missing expected token accounts");
845
- }
846
- await ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenProgram, buckets, setupContext });
847
- break;
1954
+ const indices = LOOPSCALE_TOKEN_INDICES[ix.action];
1955
+ if (indices) {
1956
+ const tokenMint = ix.instruction.keys[indices.mint]?.pubkey;
1957
+ const tokenAccount = ix.instruction.keys[indices.account]?.pubkey;
1958
+ const tokenProgram = ix.instruction.keys[indices.program]?.pubkey;
1959
+ if (!tokenMint || !tokenAccount || !tokenProgram) {
1960
+ throw new Error(`Loopscale ${ix.action} instruction is missing expected token accounts`);
848
1961
  }
849
- // create_strategy and close_strategy have no token accounts to track
1962
+ await ensureTrackedTokenAccountSetup({ tokenMint, tokenAccount, tokenProgram, buckets, setupContext });
850
1963
  }
851
1964
  buckets.syncInstructions.push(ix.instruction);
852
1965
  }
@@ -859,16 +1972,16 @@ async function buildOrderbookInstruction(ix, owner, connection, buckets, setupCo
859
1972
  orderbookCache.set(cacheKey, orderbook);
860
1973
  }
861
1974
  switch (ix.action) {
862
- case OrderbookAction.POST_OFFER:
1975
+ case vault_instruction_types_2.OrderbookAction.POST_OFFER:
863
1976
  await buildPostOffer(ix, orderbook, owner, setupContext?.signer ?? owner, buckets, setupContext);
864
1977
  break;
865
- case OrderbookAction.MARKET_OFFER:
1978
+ case vault_instruction_types_2.OrderbookAction.MARKET_OFFER:
866
1979
  await buildMarketOffer(ix, orderbook, owner, setupContext?.signer ?? owner, buckets, setupContext);
867
1980
  break;
868
- case OrderbookAction.REMOVE_OFFER:
1981
+ case vault_instruction_types_2.OrderbookAction.REMOVE_OFFER:
869
1982
  await buildRemoveOffer(ix, orderbook, owner, buckets);
870
1983
  break;
871
- case OrderbookAction.WITHDRAW_FUNDS:
1984
+ case vault_instruction_types_2.OrderbookAction.WITHDRAW_FUNDS:
872
1985
  await buildWithdrawFunds(ix, orderbook, owner, buckets);
873
1986
  break;
874
1987
  }
@@ -876,30 +1989,24 @@ async function buildOrderbookInstruction(ix, owner, connection, buckets, setupCo
876
1989
  // ============================================================================
877
1990
  // Action Builders (one per VaultAction)
878
1991
  // ============================================================================
879
- async function buildInitUserMetadata(owner, connection, { syncInstructions }) {
1992
+ function createKaminoInitUserMetadataInstruction(owner) {
880
1993
  const userMetadata = (0, constants_1.getKaminoUserMetadata)(owner, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
881
- const userMetadataAccount = await connection.getAccountInfo(userMetadata);
882
- if (userMetadataAccount)
883
- return;
884
- syncInstructions.push((0, instructions_1.initUserMetadata)({ userLookupTable: web3_js_1.PublicKey.default }, {
1994
+ return (0, instructions_1.initUserMetadata)({ userLookupTable: web3_js_1.PublicKey.default }, {
885
1995
  owner,
886
1996
  feePayer: owner,
887
1997
  userMetadata,
888
1998
  referrerUserMetadata: policyBuilders_1.KAMINO_LENDING_PROGRAM_ID,
889
1999
  rent: web3_js_1.SYSVAR_RENT_PUBKEY,
890
2000
  systemProgram: web3_js_1.SystemProgram.programId,
891
- }));
2001
+ });
892
2002
  }
893
- async function buildInitObligation(ix, owner, connection, { syncInstructions }) {
894
- const lendingMarket = kamino_markets_1.KAMINO_MARKETS[ix.market];
895
- const obligation = (0, constants_1.getKaminoLendObligation)(owner, lendingMarket, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
896
- const userMetadata = (0, constants_1.getKaminoUserMetadata)(owner, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
897
- const obligationAccount = await connection.getAccountInfo(obligation);
898
- if (obligationAccount)
899
- return;
900
- syncInstructions.push((0, instructions_1.initObligation)({ args: { tag: 0, id: 0 } }, {
901
- obligationOwner: owner,
902
- feePayer: owner,
2003
+ function createKaminoInitObligationInstruction(params) {
2004
+ const lendingMarket = kamino_markets_1.KAMINO_MARKETS[params.market];
2005
+ const obligation = (0, constants_1.getKaminoLendObligation)(params.owner, lendingMarket, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
2006
+ const userMetadata = (0, constants_1.getKaminoUserMetadata)(params.owner, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
2007
+ return (0, instructions_1.initObligation)({ args: { tag: 0, id: 0 } }, {
2008
+ obligationOwner: params.owner,
2009
+ feePayer: params.owner,
903
2010
  obligation,
904
2011
  lendingMarket,
905
2012
  seed1Account: web3_js_1.SystemProgram.programId,
@@ -907,10 +2014,250 @@ async function buildInitObligation(ix, owner, connection, { syncInstructions })
907
2014
  ownerUserMetadata: userMetadata,
908
2015
  rent: web3_js_1.SYSVAR_RENT_PUBKEY,
909
2016
  systemProgram: web3_js_1.SystemProgram.programId,
2017
+ });
2018
+ }
2019
+ async function buildInitUserMetadata(owner, connection, { syncInstructions }) {
2020
+ const userMetadata = (0, constants_1.getKaminoUserMetadata)(owner, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
2021
+ const userMetadataAccount = await connection.getAccountInfo(userMetadata);
2022
+ if (userMetadataAccount)
2023
+ return;
2024
+ syncInstructions.push(createKaminoInitUserMetadataInstruction(owner));
2025
+ }
2026
+ async function buildInitObligation(ix, owner, connection, { syncInstructions, postInstructions }, setupContext) {
2027
+ const lendingMarket = kamino_markets_1.KAMINO_MARKETS[ix.market];
2028
+ const obligation = (0, constants_1.getKaminoLendObligation)(owner, lendingMarket, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
2029
+ const obligationAccount = await connection.getAccountInfo(obligation);
2030
+ if (obligationAccount)
2031
+ return;
2032
+ syncInstructions.push(createKaminoInitObligationInstruction({ market: ix.market, owner }));
2033
+ if (setupContext && isAutoManagePositionsEnabled(setupContext)) {
2034
+ await queueKaminoObligationTrackingAfterInit({
2035
+ market: ix.market,
2036
+ obligation,
2037
+ postInstructions,
2038
+ setupContext,
2039
+ });
2040
+ }
2041
+ }
2042
+ async function queueKaminoObligationTrackingAfterInit(params) {
2043
+ const state = await loadStrategySetupState(params.setupContext);
2044
+ if (!state || state.trackedKaminoObligations.has(params.obligation.toBase58())) {
2045
+ return;
2046
+ }
2047
+ const quotePath = (0, pricePathResolver_1.resolveBestKaminoQuotePath)({
2048
+ prices: state.prices,
2049
+ vaultUnderlyingMint: state.strategyVault.state.underlyingMint,
2050
+ });
2051
+ trackRequiredPriceIds(state.requiredPriceIds, quotePath.quotePriceId);
2052
+ const remainingAccountsAmount = 1n;
2053
+ const obligationEntry = {
2054
+ obligation: params.obligation,
2055
+ lendingProgramId: policyBuilders_1.KAMINO_LENDING_PROGRAM_ID,
2056
+ quotePriceId: quotePath.quotePriceId,
2057
+ reservePriceMappings: [],
2058
+ reserveFarmMappings: [],
2059
+ minPriceStatusFlags: 0,
2060
+ };
2061
+ params.postInstructions.push(state.strategyVault.ixWrapperManagerUpdatePosition({
2062
+ manager: params.setupContext.signer,
2063
+ update: exponentVaults.positionUpdate("AddKaminoObligationEntry", [obligationEntry]),
2064
+ remainingAccounts: buildTrackedAumRemainingAccounts(state, [
2065
+ { pubkey: params.obligation, isSigner: false, isWritable: false },
2066
+ ]),
2067
+ }));
2068
+ recordPlannedKaminoObligation(state, {
2069
+ obligation: params.obligation,
2070
+ quotePriceId: quotePath.quotePriceId,
2071
+ quoteInputMint: quotePath.quoteInputMint,
2072
+ reservePriceMappings: [],
2073
+ remainingAccountsAmount,
2074
+ minPriceStatusFlags: 0,
2075
+ });
2076
+ }
2077
+ async function ensureKaminoObligationSetup(params) {
2078
+ const { ix, reserveContext, buckets, setupContext } = params;
2079
+ if (!setupContext || !isAutoManagePositionsEnabled(setupContext)) {
2080
+ return;
2081
+ }
2082
+ const state = await loadStrategySetupState(setupContext);
2083
+ if (!state) {
2084
+ return;
2085
+ }
2086
+ // Ensure user metadata exists
2087
+ const userMetadata = (0, constants_1.getKaminoUserMetadata)(setupContext.owner, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
2088
+ if (!(await accountExists(state, setupContext.connection, userMetadata))) {
2089
+ buckets.setupInstructions.push(await wrapVaultSignedSetupInstruction({
2090
+ instruction: createKaminoInitUserMetadataInstruction(setupContext.owner),
2091
+ setupContext,
2092
+ }));
2093
+ state.existingAccounts.set(userMetadata.toBase58(), true);
2094
+ }
2095
+ // Ensure obligation exists and is tracked
2096
+ const obligationKey = reserveContext.obligation.toBase58();
2097
+ const obligationExistsOnChain = await accountExists(state, setupContext.connection, reserveContext.obligation);
2098
+ const trackedObligation = state.trackedKaminoObligations.get(obligationKey);
2099
+ if (!trackedObligation) {
2100
+ if (obligationExistsOnChain) {
2101
+ throw new Error(`Kamino obligation ${obligationKey} already exists on-chain but is not tracked on vault ${state.strategyVault.selfAddress.toBase58()}. Automatic repair is disabled; manually add the Kamino obligation entry.`);
2102
+ }
2103
+ buckets.setupInstructions.push(await wrapVaultSignedSetupInstruction({
2104
+ instruction: createKaminoInitObligationInstruction({ market: ix.market, owner: setupContext.owner }),
2105
+ setupContext,
2106
+ }));
2107
+ state.existingAccounts.set(obligationKey, true);
2108
+ const quotePath = (0, pricePathResolver_1.resolveBestKaminoQuotePath)({
2109
+ prices: state.prices,
2110
+ vaultUnderlyingMint: state.strategyVault.state.underlyingMint,
2111
+ });
2112
+ const reservePriceMapping = {
2113
+ reserve: reserveContext.reservePubkey,
2114
+ reservePriceId: (0, pricePathResolver_1.resolveKaminoReservePriceIdOrThrow)({
2115
+ prices: state.prices,
2116
+ reserveMint: reserveContext.reserveAccount.liquidity.mintPubkey,
2117
+ quoteInputMint: quotePath.quoteInputMint,
2118
+ }),
2119
+ };
2120
+ trackRequiredPriceIds(state.requiredPriceIds, quotePath.quotePriceId);
2121
+ trackRequiredPriceIds(state.requiredPriceIds, reservePriceMapping.reservePriceId);
2122
+ const remainingAccountsAmount = BigInt(1 + 1);
2123
+ const obligationEntry = {
2124
+ obligation: reserveContext.obligation,
2125
+ lendingProgramId: policyBuilders_1.KAMINO_LENDING_PROGRAM_ID,
2126
+ quotePriceId: quotePath.quotePriceId,
2127
+ reservePriceMappings: [reservePriceMapping],
2128
+ reserveFarmMappings: [],
2129
+ minPriceStatusFlags: 0,
2130
+ };
2131
+ buckets.setupInstructions.push(state.strategyVault.ixWrapperManagerUpdatePosition({
2132
+ manager: setupContext.signer,
2133
+ update: exponentVaults.positionUpdate("AddKaminoObligationEntry", [obligationEntry]),
2134
+ remainingAccounts: buildTrackedAumRemainingAccounts(state, [
2135
+ { pubkey: reserveContext.obligation, isSigner: false, isWritable: false },
2136
+ { pubkey: reserveContext.reservePubkey, isSigner: false, isWritable: false },
2137
+ ]),
2138
+ }));
2139
+ recordPlannedKaminoObligation(state, {
2140
+ obligation: reserveContext.obligation,
2141
+ quotePriceId: quotePath.quotePriceId,
2142
+ quoteInputMint: quotePath.quoteInputMint,
2143
+ reservePriceMappings: [reservePriceMapping],
2144
+ remainingAccountsAmount,
2145
+ minPriceStatusFlags: 0,
2146
+ });
2147
+ return;
2148
+ }
2149
+ // Obligation exists and is tracked — ensure the reserve is mapped
2150
+ await ensureKaminoReserveMapped({
2151
+ state,
2152
+ reserveContext,
2153
+ setupContext,
2154
+ buckets,
2155
+ trackedObligation,
2156
+ });
2157
+ }
2158
+ /** Ensure a Kamino reserve is mapped on an already-tracked obligation. */
2159
+ async function ensureKaminoReserveMapped(params) {
2160
+ const { state, reserveContext, setupContext, buckets, trackedObligation } = params;
2161
+ if (trackedObligation.mappedReserves.has(reserveContext.reservePubkey.toBase58())) {
2162
+ return;
2163
+ }
2164
+ const reservePriceMapping = {
2165
+ reserve: reserveContext.reservePubkey,
2166
+ reservePriceId: (0, pricePathResolver_1.resolveKaminoReservePriceIdOrThrow)({
2167
+ prices: state.prices,
2168
+ reserveMint: reserveContext.reserveAccount.liquidity.mintPubkey,
2169
+ quoteInputMint: trackedObligation.quoteInputMint,
2170
+ }),
2171
+ };
2172
+ trackRequiredPriceIds(state.requiredPriceIds, reservePriceMapping.reservePriceId);
2173
+ buckets.setupInstructions.push(...await buildKaminoPositionFreshnessInstructions({
2174
+ reservePubkey: reserveContext.reservePubkey,
2175
+ reserveAccount: reserveContext.reserveAccount,
2176
+ lendingMarket: reserveContext.lendingMarket,
2177
+ obligation: reserveContext.obligation,
2178
+ connection: setupContext.connection,
2179
+ }));
2180
+ buckets.setupInstructions.push(state.strategyVault.ixWrapperManagerUpdatePosition({
2181
+ manager: setupContext.signer,
2182
+ update: exponentVaults.positionUpdate("UpsertKaminoObligationReservePriceMappings", {
2183
+ obligation: reserveContext.obligation,
2184
+ reservePriceMappings: [reservePriceMapping],
2185
+ reserveFarmMappings: [],
2186
+ }),
2187
+ remainingAccounts: buildTrackedAumRemainingAccounts(state, [
2188
+ { pubkey: reserveContext.reservePubkey, isSigner: false, isWritable: false },
2189
+ ]),
2190
+ }));
2191
+ recordPlannedKaminoReserveMappings(state, {
2192
+ obligation: reserveContext.obligation,
2193
+ quoteInputMint: trackedObligation.quoteInputMint,
2194
+ reservePriceMappings: [reservePriceMapping],
2195
+ });
2196
+ }
2197
+ async function buildKaminoPositionFreshnessInstructions({ reservePubkey, reserveAccount, lendingMarket, obligation, connection, }) {
2198
+ const instructions = [];
2199
+ const defaultKey = web3_js_1.PublicKey.default;
2200
+ const oracleOrSentinel = (key) => key.equals(defaultKey) ? policyBuilders_1.KAMINO_LENDING_PROGRAM_ID : key;
2201
+ const obligationState = await accounts_1.Obligation.fetch(connection, obligation);
2202
+ const otherReservePubkeys = [];
2203
+ if (obligationState) {
2204
+ for (const deposit of obligationState.deposits) {
2205
+ if (!deposit.depositReserve.equals(defaultKey) && !deposit.depositReserve.equals(reservePubkey)) {
2206
+ otherReservePubkeys.push(deposit.depositReserve);
2207
+ }
2208
+ }
2209
+ for (const borrow of obligationState.borrows) {
2210
+ if (!borrow.borrowReserve.equals(defaultKey) && !borrow.borrowReserve.equals(reservePubkey)) {
2211
+ otherReservePubkeys.push(borrow.borrowReserve);
2212
+ }
2213
+ }
2214
+ }
2215
+ const otherReserves = [];
2216
+ for (const otherReservePubkey of otherReservePubkeys) {
2217
+ const otherReserveAccount = await kamino_reserve_deserializer_1.Reserve.fetch(connection, otherReservePubkey);
2218
+ if (!otherReserveAccount) {
2219
+ continue;
2220
+ }
2221
+ otherReserves.push({ pubkey: otherReservePubkey, account: otherReserveAccount });
2222
+ }
2223
+ for (const { pubkey, account } of otherReserves) {
2224
+ const tokenInfo = account.config.tokenInfo;
2225
+ instructions.push((0, instructions_1.refreshReserve)({
2226
+ reserve: pubkey,
2227
+ lendingMarket,
2228
+ pythOracle: oracleOrSentinel(tokenInfo.pythConfiguration.price),
2229
+ switchboardPriceOracle: oracleOrSentinel(tokenInfo.switchboardConfiguration.priceAggregator),
2230
+ switchboardTwapOracle: oracleOrSentinel(tokenInfo.switchboardConfiguration.twapAggregator),
2231
+ scopePrices: oracleOrSentinel(tokenInfo.scopeConfiguration.priceFeed),
2232
+ }));
2233
+ }
2234
+ const tokenInfo = reserveAccount.config.tokenInfo;
2235
+ instructions.push((0, instructions_1.refreshReserve)({
2236
+ reserve: reservePubkey,
2237
+ lendingMarket,
2238
+ pythOracle: oracleOrSentinel(tokenInfo.pythConfiguration.price),
2239
+ switchboardPriceOracle: oracleOrSentinel(tokenInfo.switchboardConfiguration.priceAggregator),
2240
+ switchboardTwapOracle: oracleOrSentinel(tokenInfo.switchboardConfiguration.twapAggregator),
2241
+ scopePrices: oracleOrSentinel(tokenInfo.scopeConfiguration.priceFeed),
910
2242
  }));
2243
+ const refreshObligationIx = (0, instructions_1.refreshObligation)({ lendingMarket, obligation });
2244
+ if (obligationState) {
2245
+ const depositReserves = obligationState.deposits
2246
+ .map((deposit) => deposit.depositReserve)
2247
+ .filter((reserve) => !reserve.equals(defaultKey));
2248
+ const borrowReserves = obligationState.borrows
2249
+ .map((borrow) => borrow.borrowReserve)
2250
+ .filter((reserve) => !reserve.equals(defaultKey));
2251
+ for (const reserve of [...depositReserves, ...borrowReserves]) {
2252
+ refreshObligationIx.keys.push({ pubkey: reserve, isSigner: false, isWritable: false });
2253
+ }
2254
+ }
2255
+ instructions.push(refreshObligationIx);
2256
+ return instructions;
911
2257
  }
912
- async function buildDeposit(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }) {
2258
+ async function buildDeposit(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }, setupContext) {
913
2259
  const ctx = await resolveReserveContext(ix, owner, connection);
2260
+ await ensureKaminoObligationSetup({ ix, reserveContext: ctx, buckets: { setupInstructions, syncInstructions, preInstructions, postInstructions, signers: [], addressLookupTableAddresses: [] }, setupContext });
914
2261
  const refreshes = await buildRefreshInstructions({
915
2262
  ...ctx, owner, farmMode: FARM_COLLATERAL, signer, connection, needsScopeRefresh: false,
916
2263
  });
@@ -942,8 +2289,9 @@ async function buildDeposit(ix, owner, connection, signer, { setupInstructions,
942
2289
  farmsProgram: KAMINO_FARMS_PROGRAM_ID,
943
2290
  }));
944
2291
  }
945
- async function buildWithdraw(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }) {
2292
+ async function buildWithdraw(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }, setupContext) {
946
2293
  const ctx = await resolveReserveContext(ix, owner, connection);
2294
+ await ensureKaminoObligationSetup({ ix, reserveContext: ctx, buckets: { setupInstructions, syncInstructions, preInstructions, postInstructions, signers: [], addressLookupTableAddresses: [] }, setupContext });
947
2295
  const refreshes = await buildRefreshInstructions({
948
2296
  ...ctx, owner, farmMode: FARM_COLLATERAL, signer, connection, needsScopeRefresh: true,
949
2297
  });
@@ -981,8 +2329,9 @@ async function buildWithdraw(ix, owner, connection, signer, { setupInstructions,
981
2329
  farmsProgram: KAMINO_FARMS_PROGRAM_ID,
982
2330
  }));
983
2331
  }
984
- async function buildBorrow(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }) {
2332
+ async function buildBorrow(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }, setupContext) {
985
2333
  const ctx = await resolveReserveContext(ix, owner, connection);
2334
+ await ensureKaminoObligationSetup({ ix, reserveContext: ctx, buckets: { setupInstructions, syncInstructions, preInstructions, postInstructions, signers: [], addressLookupTableAddresses: [] }, setupContext });
986
2335
  const refreshes = await buildRefreshInstructions({
987
2336
  ...ctx, owner, farmMode: FARM_DEBT, signer, connection, needsScopeRefresh: true,
988
2337
  });
@@ -1011,8 +2360,9 @@ async function buildBorrow(ix, owner, connection, signer, { setupInstructions, s
1011
2360
  farmsProgram: KAMINO_FARMS_PROGRAM_ID,
1012
2361
  }));
1013
2362
  }
1014
- async function buildRepay(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }) {
2363
+ async function buildRepay(ix, owner, connection, signer, { setupInstructions, syncInstructions, preInstructions, postInstructions }, setupContext) {
1015
2364
  const ctx = await resolveReserveContext(ix, owner, connection);
2365
+ await ensureKaminoObligationSetup({ ix, reserveContext: ctx, buckets: { setupInstructions, syncInstructions, preInstructions, postInstructions, signers: [], addressLookupTableAddresses: [] }, setupContext });
1016
2366
  const refreshes = await buildRefreshInstructions({
1017
2367
  ...ctx, owner, farmMode: FARM_DEBT, signer, connection, needsScopeRefresh: false,
1018
2368
  });
@@ -1208,19 +2558,16 @@ exports.orderbookAction = {
1208
2558
  /**
1209
2559
  * Post a limit order on the orderbook.
1210
2560
  * @param params - Order parameters
1211
- * @param params.offerIdx - Required offer index for position tracking. Must be unique per trader.
1212
2561
  */
1213
2562
  postOffer(params) {
1214
2563
  return {
1215
- action: OrderbookAction.POST_OFFER,
2564
+ action: vault_instruction_types_2.OrderbookAction.POST_OFFER,
1216
2565
  orderbook: params.orderbook,
1217
2566
  mode: params.mode,
1218
2567
  direction: params.direction,
1219
2568
  priceApy: params.priceApy,
1220
2569
  amount: params.amount,
1221
- offerIdx: params.offerIdx,
1222
2570
  offerOption: params.offerOption,
1223
- virtualOffer: params.virtualOffer,
1224
2571
  expirySeconds: params.expirySeconds,
1225
2572
  };
1226
2573
  },
@@ -1230,7 +2577,7 @@ exports.orderbookAction = {
1230
2577
  */
1231
2578
  marketOffer(params) {
1232
2579
  return {
1233
- action: OrderbookAction.MARKET_OFFER,
2580
+ action: vault_instruction_types_2.OrderbookAction.MARKET_OFFER,
1234
2581
  orderbook: params.orderbook,
1235
2582
  mode: params.mode,
1236
2583
  direction: params.direction,
@@ -1246,7 +2593,7 @@ exports.orderbookAction = {
1246
2593
  */
1247
2594
  removeOffer(params) {
1248
2595
  return {
1249
- action: OrderbookAction.REMOVE_OFFER,
2596
+ action: vault_instruction_types_2.OrderbookAction.REMOVE_OFFER,
1250
2597
  orderbook: params.orderbook,
1251
2598
  offerIdx: params.offerIdx,
1252
2599
  };
@@ -1257,7 +2604,7 @@ exports.orderbookAction = {
1257
2604
  */
1258
2605
  withdrawFunds(params) {
1259
2606
  return {
1260
- action: OrderbookAction.WITHDRAW_FUNDS,
2607
+ action: vault_instruction_types_2.OrderbookAction.WITHDRAW_FUNDS,
1261
2608
  orderbook: params.orderbook,
1262
2609
  ptAmount: params.ptAmount,
1263
2610
  ytAmount: params.ytAmount,
@@ -1268,14 +2615,14 @@ exports.orderbookAction = {
1268
2615
  exports.syAction = {
1269
2616
  mint(params) {
1270
2617
  return {
1271
- action: SyAction.MINT,
2618
+ action: vault_instruction_types_2.SyAction.MINT,
1272
2619
  vault: params.vault,
1273
2620
  amountBase: params.amountBase,
1274
2621
  };
1275
2622
  },
1276
2623
  redeem(params) {
1277
2624
  return {
1278
- action: SyAction.REDEEM,
2625
+ action: vault_instruction_types_2.SyAction.REDEEM,
1279
2626
  vault: params.vault,
1280
2627
  amountSy: params.amountSy,
1281
2628
  };
@@ -1317,7 +2664,7 @@ exports.coreAction = {
1317
2664
  */
1318
2665
  strip(params) {
1319
2666
  return {
1320
- action: CoreAction.STRIP,
2667
+ action: vault_instruction_types_2.CoreAction.STRIP,
1321
2668
  vault: params.vault,
1322
2669
  amountBase: params.amountBase,
1323
2670
  };
@@ -1329,7 +2676,7 @@ exports.coreAction = {
1329
2676
  */
1330
2677
  merge(params) {
1331
2678
  return {
1332
- action: CoreAction.MERGE,
2679
+ action: vault_instruction_types_2.CoreAction.MERGE,
1333
2680
  vault: params.vault,
1334
2681
  amountPy: params.amountPy,
1335
2682
  };
@@ -1340,7 +2687,7 @@ exports.coreAction = {
1340
2687
  */
1341
2688
  withdrawYt(params) {
1342
2689
  return {
1343
- action: CoreAction.WITHDRAW_YT,
2690
+ action: vault_instruction_types_2.CoreAction.WITHDRAW_YT,
1344
2691
  vault: params.vault,
1345
2692
  amountYt: params.amountYt,
1346
2693
  };
@@ -1351,18 +2698,19 @@ exports.coreAction = {
1351
2698
  */
1352
2699
  depositYt(params) {
1353
2700
  return {
1354
- action: CoreAction.DEPOSIT_YT,
2701
+ action: vault_instruction_types_2.CoreAction.DEPOSIT_YT,
1355
2702
  vault: params.vault,
1356
2703
  amountYt: params.amountYt,
1357
2704
  };
1358
2705
  },
1359
2706
  /**
1360
2707
  * Initialize yield position for the Squads vault (owner).
1361
- * Required before buying YT or depositing YT.
2708
+ * Required before buying YT or depositing YT. When `autoManagePositions`
2709
+ * is enabled, the SDK also tracks the new yield position automatically.
1362
2710
  */
1363
2711
  initializeYieldPosition(params) {
1364
2712
  return {
1365
- action: CoreAction.INITIALIZE_YIELD_POSITION,
2713
+ action: vault_instruction_types_2.CoreAction.INITIALIZE_YIELD_POSITION,
1366
2714
  vault: params.vault,
1367
2715
  };
1368
2716
  },
@@ -1372,11 +2720,11 @@ exports.coreAction = {
1372
2720
  */
1373
2721
  function directionToOfferType(direction) {
1374
2722
  switch (direction) {
1375
- case OrderbookTradeDirection.BUY_PT:
1376
- case OrderbookTradeDirection.SELL_YT:
2723
+ case vault_instruction_types_2.OrderbookTradeDirection.BUY_PT:
2724
+ case vault_instruction_types_2.OrderbookTradeDirection.SELL_YT:
1377
2725
  return orderbook_2.OfferType.SellYt;
1378
- case OrderbookTradeDirection.SELL_PT:
1379
- case OrderbookTradeDirection.BUY_YT:
2726
+ case vault_instruction_types_2.OrderbookTradeDirection.SELL_PT:
2727
+ case vault_instruction_types_2.OrderbookTradeDirection.BUY_YT:
1380
2728
  return orderbook_2.OfferType.BuyYt;
1381
2729
  }
1382
2730
  }
@@ -1411,6 +2759,7 @@ function directionToOfferType(direction) {
1411
2759
  * ```
1412
2760
  */
1413
2761
  async function createOrderbookSyncTransaction({ instructions, owner, connection, policyPda, vaultPda, signer, accountIndex = 0, constraintIndices, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram = syncTransaction_1.SQUADS_PROGRAM_ID, env = environment_1.LOCAL_ENV, }) {
2762
+ vaultPda ??= owner;
1414
2763
  const { setupInstructions, syncInstructions, preInstructions, postInstructions, signers, addressLookupTableAddresses } = await buildOrderbookInstructions(instructions, owner, connection, signer, policyPda, vaultPda, accountIndex, vaultAddress, leadingAccounts, preHookAccounts, postHookAccounts, squadsProgram, env);
1415
2764
  let resolvedPolicyPda = policyPda;
1416
2765
  let resolvedConstraintIndices = constraintIndices;
@@ -1485,6 +2834,7 @@ async function buildOrderbookInstructions(instructions, owner, connection, signe
1485
2834
  async function buildPostOffer(ix, orderbook, owner, setupPayer, buckets, setupContext) {
1486
2835
  const offerType = directionToOfferType(ix.direction);
1487
2836
  const option = ix.offerOption ?? "FillOrKill";
2837
+ const offerIdx = orderbook.getNextOfferIndex();
1488
2838
  if (setupContext) {
1489
2839
  await ensureOrderbookPositionSetup(orderbook, buckets, setupContext);
1490
2840
  await ensureTrackedTokenAccountSetup({
@@ -1517,10 +2867,10 @@ async function buildPostOffer(ix, orderbook, owner, setupPayer, buckets, setupCo
1517
2867
  amount: ix.amount,
1518
2868
  offerType,
1519
2869
  offerOption: (0, orderbook_2.offerOptions)(option, [false]),
1520
- virtualOffer: ix.virtualOffer ?? false,
2870
+ virtualOffer: true,
1521
2871
  expirySeconds: ix.expirySeconds ?? 3600,
1522
2872
  mintSy: orderbook.vault.mintSy,
1523
- offerIdx: ix.offerIdx,
2873
+ offerIdx,
1524
2874
  }));
1525
2875
  return;
1526
2876
  }
@@ -1531,10 +2881,10 @@ async function buildPostOffer(ix, orderbook, owner, setupPayer, buckets, setupCo
1531
2881
  amount: ix.amount,
1532
2882
  offerType,
1533
2883
  offerOption: (0, orderbook_2.offerOptions)(option, [false]),
1534
- virtualOffer: ix.virtualOffer ?? false,
2884
+ virtualOffer: true,
1535
2885
  expirySeconds: ix.expirySeconds ?? 3600,
1536
2886
  mintSy: orderbook.vault.mintSy,
1537
- offerIdx: ix.offerIdx,
2887
+ offerIdx,
1538
2888
  });
1539
2889
  buckets.preInstructions.push(...setupIxs);
1540
2890
  buckets.syncInstructions.push(postOfferIx);
@@ -1619,6 +2969,42 @@ async function buildWithdrawFunds(ix, orderbook, owner, { syncInstructions }) {
1619
2969
  // ============================================================================
1620
2970
  // Core Instruction Builders (Strip/Merge)
1621
2971
  // ============================================================================
2972
+ async function queueYieldPositionTrackingAfterInit(params) {
2973
+ const state = await loadStrategySetupState(params.setupContext);
2974
+ if (!state || state.trackedYieldVaults.has(params.vault.selfAddress.toBase58())) {
2975
+ return;
2976
+ }
2977
+ const yieldPosition = params.vault.pda.yieldPosition({
2978
+ owner: params.setupContext.owner,
2979
+ vault: params.vault.selfAddress,
2980
+ });
2981
+ const priceIdPt = (0, pricePathResolver_1.resolvePriceIdFromMintToUnderlyingOrThrow)({
2982
+ prices: state.prices,
2983
+ sourceMint: params.vault.mintPt,
2984
+ targetMint: state.strategyVault.state.underlyingMint,
2985
+ label: `yield position setup (${params.vault.selfAddress.toBase58()})`,
2986
+ });
2987
+ trackRequiredPriceIds(state.requiredPriceIds, priceIdPt);
2988
+ params.postInstructions.push(state.strategyVault.ixWrapperManageVaultSettings({
2989
+ manager: params.setupContext.signer,
2990
+ actions: [
2991
+ exponentVaults.vaultSettingsAction("AddYieldPositionEntry", {
2992
+ yieldPosition,
2993
+ vault: params.vault.selfAddress,
2994
+ priceIdPt,
2995
+ }),
2996
+ ],
2997
+ remainingAccounts: [
2998
+ { pubkey: params.vault.selfAddress, isSigner: false, isWritable: false },
2999
+ { pubkey: yieldPosition, isSigner: false, isWritable: false },
3000
+ ],
3001
+ }));
3002
+ recordPlannedYieldPosition(state, {
3003
+ yieldPosition,
3004
+ vault: params.vault.selfAddress,
3005
+ priceIdPt,
3006
+ });
3007
+ }
1622
3008
  /** Build a single core instruction (strip/merge) */
1623
3009
  async function buildCoreInstruction(ix, owner, connection, buckets, setupContext) {
1624
3010
  const cacheKey = ix.vault.toBase58();
@@ -1627,7 +3013,7 @@ async function buildCoreInstruction(ix, owner, connection, buckets, setupContext
1627
3013
  vault = await vault_1.Vault.load(environment_1.LOCAL_ENV, connection, ix.vault);
1628
3014
  vaultCache.set(cacheKey, vault);
1629
3015
  }
1630
- const isInitializeYieldPosition = ix.action === CoreAction.INITIALIZE_YIELD_POSITION;
3016
+ const isInitializeYieldPosition = ix.action === vault_instruction_types_2.CoreAction.INITIALIZE_YIELD_POSITION;
1631
3017
  if (setupContext && !isInitializeYieldPosition) {
1632
3018
  await ensureTrackedTokenAccountSetup({
1633
3019
  tokenMint: vault.mintPt,
@@ -1637,7 +3023,7 @@ async function buildCoreInstruction(ix, owner, connection, buckets, setupContext
1637
3023
  setupContext,
1638
3024
  });
1639
3025
  await ensureYieldPositionSetup(vault, buckets, setupContext);
1640
- if (ix.action === CoreAction.WITHDRAW_YT) {
3026
+ if (ix.action === vault_instruction_types_2.CoreAction.WITHDRAW_YT) {
1641
3027
  await ensureTrackedTokenAccountSetup({
1642
3028
  tokenMint: vault.mintYt,
1643
3029
  tokenAccount: (0, spl_token_1.getAssociatedTokenAddressSync)(vault.mintYt, setupContext.owner, true, spl_token_1.TOKEN_PROGRAM_ID),
@@ -1648,26 +3034,33 @@ async function buildCoreInstruction(ix, owner, connection, buckets, setupContext
1648
3034
  }
1649
3035
  }
1650
3036
  switch (ix.action) {
1651
- case CoreAction.STRIP:
3037
+ case vault_instruction_types_2.CoreAction.STRIP:
1652
3038
  await buildStrip(ix, vault, owner, setupContext?.signer ?? owner, buckets);
1653
3039
  break;
1654
- case CoreAction.MERGE:
3040
+ case vault_instruction_types_2.CoreAction.MERGE:
1655
3041
  await buildMerge(ix, vault, owner, setupContext?.signer ?? owner, buckets);
1656
3042
  break;
1657
- case CoreAction.WITHDRAW_YT:
3043
+ case vault_instruction_types_2.CoreAction.WITHDRAW_YT:
1658
3044
  await buildWithdrawYt(ix, vault, owner, setupContext?.signer ?? owner, buckets);
1659
3045
  break;
1660
- case CoreAction.DEPOSIT_YT:
3046
+ case vault_instruction_types_2.CoreAction.DEPOSIT_YT:
1661
3047
  await buildDepositYt(ix, vault, owner, buckets);
1662
3048
  break;
1663
- case CoreAction.INITIALIZE_YIELD_POSITION:
1664
- await buildInitializeYieldPosition(ix, vault, owner, buckets);
3049
+ case vault_instruction_types_2.CoreAction.INITIALIZE_YIELD_POSITION:
3050
+ await buildInitializeYieldPosition(ix, vault, owner, buckets, setupContext);
1665
3051
  break;
1666
3052
  }
1667
3053
  }
1668
- async function buildInitializeYieldPosition(ix, vault, owner, { syncInstructions }) {
3054
+ async function buildInitializeYieldPosition(ix, vault, owner, { syncInstructions, postInstructions }, setupContext) {
1669
3055
  const initIx = vault.ixInitializeYieldPosition({ owner });
1670
3056
  syncInstructions.push(initIx);
3057
+ if (setupContext && isAutoManagePositionsEnabled(setupContext)) {
3058
+ await queueYieldPositionTrackingAfterInit({
3059
+ vault,
3060
+ setupContext,
3061
+ postInstructions,
3062
+ });
3063
+ }
1671
3064
  }
1672
3065
  async function buildStrip(ix, vault, owner, payer, { syncInstructions, preInstructions }) {
1673
3066
  const sySrc = (0, spl_token_1.getAssociatedTokenAddressSync)(vault.mintSy, owner, true, spl_token_1.TOKEN_PROGRAM_ID);
@@ -1742,7 +3135,7 @@ async function buildSyInstruction(ix, owner, connection, buckets, setupContext)
1742
3135
  throw new Error(`Base mint ${vault.flavor.mintBase.toBase58()} must be tracked on the strategy vault before using mint_sy/redeem_sy`);
1743
3136
  }
1744
3137
  switch (ix.action) {
1745
- case SyAction.MINT:
3138
+ case vault_instruction_types_2.SyAction.MINT:
1746
3139
  buckets.syncInstructions.push(await vault.flavor.ixMintSy({
1747
3140
  amountBase: ix.amountBase.toString(),
1748
3141
  depositor: owner,
@@ -1750,7 +3143,7 @@ async function buildSyInstruction(ix, owner, connection, buckets, setupContext)
1750
3143
  depositorSyTokenAccount: syTokenAccount,
1751
3144
  }));
1752
3145
  break;
1753
- case SyAction.REDEEM:
3146
+ case vault_instruction_types_2.SyAction.REDEEM:
1754
3147
  buckets.syncInstructions.push(await vault.flavor.ixRedeemSy({
1755
3148
  amountSy: ix.amountSy.toString(),
1756
3149
  redeemer: owner,
@@ -1785,16 +3178,21 @@ exports.titanAction = {
1785
3178
  /**
1786
3179
  * Wrap a Titan SwapRouteV2 instruction for vault execution.
1787
3180
  * @param params.instruction - Pre-built TransactionInstruction from Titan's router API
3181
+ * @param params.addressLookupTableAddresses - Optional ALT addresses returned by Titan for this route
1788
3182
  */
1789
3183
  swap(params) {
1790
- return { action: TitanAction.SWAP, instruction: params.instruction };
3184
+ return {
3185
+ action: vault_instruction_types_2.TitanAction.SWAP,
3186
+ instruction: params.instruction,
3187
+ addressLookupTableAddresses: params.addressLookupTableAddresses,
3188
+ };
1791
3189
  },
1792
3190
  };
1793
3191
  // ============================================================================
1794
3192
  // Loopscale Action Builder
1795
3193
  // ============================================================================
1796
3194
  /**
1797
- * Builder for Loopscale vault action descriptors.
3195
+ * Builder for Loopscale action descriptors used in Exponent sync transactions.
1798
3196
  *
1799
3197
  * Loans (BORROWER side): create/close loan, deposit/withdraw collateral, borrow/repay principal.
1800
3198
  * Strategies (LENDER side): create/close strategy, deposit/withdraw into strategy.
@@ -1824,54 +3222,54 @@ exports.titanAction = {
1824
3222
  */
1825
3223
  exports.loopscaleAction = {
1826
3224
  createLoan(params) {
1827
- return { action: LoopscaleAction.CREATE_LOAN, instruction: params.instruction };
3225
+ return { action: vault_instruction_types_2.LoopscaleAction.CREATE_LOAN, instruction: params.instruction };
1828
3226
  },
1829
3227
  depositCollateral(params) {
1830
- return { action: LoopscaleAction.DEPOSIT_COLLATERAL, instruction: params.instruction };
3228
+ return { action: vault_instruction_types_2.LoopscaleAction.DEPOSIT_COLLATERAL, instruction: params.instruction };
1831
3229
  },
1832
3230
  borrowPrincipal(params) {
1833
- return { action: LoopscaleAction.BORROW_PRINCIPAL, instruction: params.instruction };
3231
+ return { action: vault_instruction_types_2.LoopscaleAction.BORROW_PRINCIPAL, instruction: params.instruction };
1834
3232
  },
1835
3233
  repayPrincipal(params) {
1836
- return { action: LoopscaleAction.REPAY_PRINCIPAL, instruction: params.instruction };
3234
+ return { action: vault_instruction_types_2.LoopscaleAction.REPAY_PRINCIPAL, instruction: params.instruction };
1837
3235
  },
1838
3236
  withdrawCollateral(params) {
1839
- return { action: LoopscaleAction.WITHDRAW_COLLATERAL, instruction: params.instruction };
3237
+ return { action: vault_instruction_types_2.LoopscaleAction.WITHDRAW_COLLATERAL, instruction: params.instruction };
1840
3238
  },
1841
3239
  closeLoan(params) {
1842
- return { action: LoopscaleAction.CLOSE_LOAN, instruction: params.instruction };
3240
+ return { action: vault_instruction_types_2.LoopscaleAction.CLOSE_LOAN, instruction: params.instruction };
1843
3241
  },
1844
3242
  /** Assign collateral fractions to ledgers (BORROWER side). Must call before borrow_principal. */
1845
3243
  updateWeightMatrix(params) {
1846
- return { action: LoopscaleAction.UPDATE_WEIGHT_MATRIX, instruction: params.instruction };
3244
+ return { action: vault_instruction_types_2.LoopscaleAction.UPDATE_WEIGHT_MATRIX, instruction: params.instruction };
1847
3245
  },
1848
3246
  createStrategy(params) {
1849
- return { action: LoopscaleAction.CREATE_STRATEGY, instruction: params.instruction };
3247
+ return { action: vault_instruction_types_2.LoopscaleAction.CREATE_STRATEGY, instruction: params.instruction };
1850
3248
  },
1851
3249
  depositStrategy(params) {
1852
- return { action: LoopscaleAction.DEPOSIT_STRATEGY, instruction: params.instruction };
3250
+ return { action: vault_instruction_types_2.LoopscaleAction.DEPOSIT_STRATEGY, instruction: params.instruction };
1853
3251
  },
1854
3252
  withdrawStrategy(params) {
1855
- return { action: LoopscaleAction.WITHDRAW_STRATEGY, instruction: params.instruction };
3253
+ return { action: vault_instruction_types_2.LoopscaleAction.WITHDRAW_STRATEGY, instruction: params.instruction };
1856
3254
  },
1857
3255
  closeStrategy(params) {
1858
- return { action: LoopscaleAction.CLOSE_STRATEGY, instruction: params.instruction };
3256
+ return { action: vault_instruction_types_2.LoopscaleAction.CLOSE_STRATEGY, instruction: params.instruction };
1859
3257
  },
1860
3258
  /** Configure collateral terms on a strategy (LENDER side). */
1861
3259
  updateStrategy(params) {
1862
- return { action: LoopscaleAction.UPDATE_STRATEGY, instruction: params.instruction };
3260
+ return { action: vault_instruction_types_2.LoopscaleAction.UPDATE_STRATEGY, instruction: params.instruction };
1863
3261
  },
1864
3262
  /** Lock loan before borrow_principal (BORROWER side). Must be in same tx as unlock. */
1865
3263
  lockLoan(params) {
1866
- return { action: LoopscaleAction.LOCK_LOAN, instruction: params.instruction };
3264
+ return { action: vault_instruction_types_2.LoopscaleAction.LOCK_LOAN, instruction: params.instruction };
1867
3265
  },
1868
3266
  /** Unlock loan after borrow_principal (BORROWER side). Runs health check. */
1869
3267
  unlockLoan(params) {
1870
- return { action: LoopscaleAction.UNLOCK_LOAN, instruction: params.instruction };
3268
+ return { action: vault_instruction_types_2.LoopscaleAction.UNLOCK_LOAN, instruction: params.instruction };
1871
3269
  },
1872
3270
  /** Refinance a loan ledger from one strategy to another (BORROWER side). */
1873
3271
  refinanceLedger(params) {
1874
- return { action: LoopscaleAction.REFINANCE_LEDGER, instruction: params.instruction };
3272
+ return { action: vault_instruction_types_2.LoopscaleAction.REFINANCE_LEDGER, instruction: params.instruction };
1875
3273
  },
1876
3274
  };
1877
3275
  // ============================================================================
@@ -1912,7 +3310,9 @@ exports.clmmAction = {
1912
3310
  /**
1913
3311
  * Create a new LP position on the CLMM with a specified tick range.
1914
3312
  * The LP position keypair is generated internally — retrieve it from
1915
- * `result.signers[0]` after calling `createVaultSyncTransaction`.
3313
+ * `result.signers[0]` after calling `createVaultSyncTransaction`. When
3314
+ * `autoManagePositions` is enabled, the SDK also tracks the new LP
3315
+ * position automatically after the deposit sync instruction succeeds.
1916
3316
  *
1917
3317
  * @param params.market - CLMM MarketThree account address
1918
3318
  * @param params.ptInIntent - Maximum PT to deposit
@@ -1921,7 +3321,7 @@ exports.clmmAction = {
1921
3321
  * @param params.upperTickKey - Upper tick boundary (APY value)
1922
3322
  */
1923
3323
  depositLiquidity(params) {
1924
- return { action: ClmmAction.DEPOSIT_LIQUIDITY, ...params };
3324
+ return { action: vault_instruction_types_2.ClmmAction.DEPOSIT_LIQUIDITY, ...params };
1925
3325
  },
1926
3326
  /**
1927
3327
  * Add liquidity to an existing LP position.
@@ -1932,7 +3332,7 @@ exports.clmmAction = {
1932
3332
  * @param params.syInIntent - Maximum SY to add
1933
3333
  */
1934
3334
  addLiquidity(params) {
1935
- return { action: ClmmAction.ADD_LIQUIDITY, ...params };
3335
+ return { action: vault_instruction_types_2.ClmmAction.ADD_LIQUIDITY, ...params };
1936
3336
  },
1937
3337
  /**
1938
3338
  * Withdraw liquidity from an LP position, receiving PT + SY.
@@ -1944,7 +3344,7 @@ exports.clmmAction = {
1944
3344
  * @param params.minSyOut - Minimum SY to receive (slippage protection)
1945
3345
  */
1946
3346
  withdrawLiquidity(params) {
1947
- return { action: ClmmAction.WITHDRAW_LIQUIDITY, ...params };
3347
+ return { action: vault_instruction_types_2.ClmmAction.WITHDRAW_LIQUIDITY, ...params };
1948
3348
  },
1949
3349
  /**
1950
3350
  * Low-level PT/SY swap on the CLMM. Prefer {@link buyPt} / {@link sellPt}
@@ -1957,7 +3357,7 @@ exports.clmmAction = {
1957
3357
  * @param params.lnImpliedApyLimit - Optional price limit (ln implied APY)
1958
3358
  */
1959
3359
  tradePt(params) {
1960
- return { action: ClmmAction.TRADE_PT, ...params };
3360
+ return { action: vault_instruction_types_2.ClmmAction.TRADE_PT, ...params };
1961
3361
  },
1962
3362
  /**
1963
3363
  * Buy PT with SY on the CLMM.
@@ -1968,7 +3368,7 @@ exports.clmmAction = {
1968
3368
  * @param params.lnImpliedApyLimit - Optional price limit (ln implied APY)
1969
3369
  */
1970
3370
  buyPt(params) {
1971
- return { action: ClmmAction.BUY_PT, ...params };
3371
+ return { action: vault_instruction_types_2.ClmmAction.BUY_PT, ...params };
1972
3372
  },
1973
3373
  /**
1974
3374
  * Sell PT for SY on the CLMM.
@@ -1979,7 +3379,7 @@ exports.clmmAction = {
1979
3379
  * @param params.lnImpliedApyLimit - Optional price limit (ln implied APY)
1980
3380
  */
1981
3381
  sellPt(params) {
1982
- return { action: ClmmAction.SELL_PT, ...params };
3382
+ return { action: vault_instruction_types_2.ClmmAction.SELL_PT, ...params };
1983
3383
  },
1984
3384
  /**
1985
3385
  * Buy YT with SY on the CLMM.
@@ -1990,7 +3390,7 @@ exports.clmmAction = {
1990
3390
  * @param params.lnImpliedApyLimit - Optional price limit (ln implied APY)
1991
3391
  */
1992
3392
  buyYt(params) {
1993
- return { action: ClmmAction.BUY_YT, ...params };
3393
+ return { action: vault_instruction_types_2.ClmmAction.BUY_YT, ...params };
1994
3394
  },
1995
3395
  /**
1996
3396
  * Sell YT for SY on the CLMM.
@@ -2001,7 +3401,7 @@ exports.clmmAction = {
2001
3401
  * @param params.lnImpliedApyLimit - Optional price limit (ln implied APY)
2002
3402
  */
2003
3403
  sellYt(params) {
2004
- return { action: ClmmAction.SELL_YT, ...params };
3404
+ return { action: vault_instruction_types_2.ClmmAction.SELL_YT, ...params };
2005
3405
  },
2006
3406
  /**
2007
3407
  * Claim farm emissions from an LP position.
@@ -2011,7 +3411,7 @@ exports.clmmAction = {
2011
3411
  * @param params.farmIndex - Index of the farm emission to claim
2012
3412
  */
2013
3413
  claimFarmEmission(params) {
2014
- return { action: ClmmAction.CLAIM_FARM_EMISSION, ...params };
3414
+ return { action: vault_instruction_types_2.ClmmAction.CLAIM_FARM_EMISSION, ...params };
2015
3415
  },
2016
3416
  };
2017
3417
  // ============================================================================
@@ -2019,6 +3419,63 @@ exports.clmmAction = {
2019
3419
  // ============================================================================
2020
3420
  /** Cache for loaded MarketThree instances to avoid redundant fetches. */
2021
3421
  const marketThreeCache = new Map();
3422
+ async function ensureTrackedClmmPosition(params) {
3423
+ if (!params.setupContext || !isAutoManagePositionsEnabled(params.setupContext)) {
3424
+ return;
3425
+ }
3426
+ const state = await loadStrategySetupState(params.setupContext);
3427
+ if (!state || state.trackedClmmPositions.has(params.lpPosition.toBase58())) {
3428
+ return;
3429
+ }
3430
+ const existsOnChain = await accountExists(state, params.setupContext.connection, params.lpPosition);
3431
+ if (existsOnChain) {
3432
+ throw new Error(`CLMM lp position ${params.lpPosition.toBase58()} already exists on-chain but is not tracked on vault ${state.strategyVault.selfAddress.toBase58()}. Automatic repair is disabled; manually add the CLMM position entry.`);
3433
+ }
3434
+ throw new Error(`CLMM lp position ${params.lpPosition.toBase58()} is not tracked on vault ${state.strategyVault.selfAddress.toBase58()}. Create it with clmmAction.depositLiquidity() through this SDK flow or manually add the position entry.`);
3435
+ }
3436
+ async function queueClmmPositionTrackingAfterDeposit(params) {
3437
+ if (!params.setupContext || !isAutoManagePositionsEnabled(params.setupContext)) {
3438
+ return;
3439
+ }
3440
+ const state = await loadStrategySetupState(params.setupContext);
3441
+ if (!state || state.trackedClmmPositions.has(params.lpPosition.toBase58())) {
3442
+ return;
3443
+ }
3444
+ const priceIdPt = (0, pricePathResolver_1.resolvePriceIdFromMintToUnderlyingOrThrow)({
3445
+ prices: state.prices,
3446
+ sourceMint: params.market.mintPt,
3447
+ targetMint: state.strategyVault.state.underlyingMint,
3448
+ label: `CLMM PT position setup (${params.market.selfAddress.toBase58()})`,
3449
+ });
3450
+ trackRequiredPriceIds(state.requiredPriceIds, priceIdPt);
3451
+ const priceIdSy = (0, pricePathResolver_1.resolvePriceIdFromMintToUnderlyingOrThrow)({
3452
+ prices: state.prices,
3453
+ sourceMint: params.market.mintSy,
3454
+ targetMint: state.strategyVault.state.underlyingMint,
3455
+ label: `CLMM SY position setup (${params.market.selfAddress.toBase58()})`,
3456
+ });
3457
+ trackRequiredPriceIds(state.requiredPriceIds, priceIdSy);
3458
+ params.postInstructions.push(state.strategyVault.ixWrapperManageVaultSettings({
3459
+ manager: params.setupContext.signer,
3460
+ actions: [exponentVaults.vaultSettingsAction("AddClmmPositionEntry", [{
3461
+ lpPosition: params.lpPosition,
3462
+ market: params.market.selfAddress,
3463
+ priceIdPt,
3464
+ priceIdSy,
3465
+ }])],
3466
+ remainingAccounts: [
3467
+ { pubkey: params.lpPosition, isSigner: false, isWritable: false },
3468
+ { pubkey: params.market.selfAddress, isSigner: false, isWritable: false },
3469
+ ],
3470
+ }));
3471
+ recordPlannedClmmPosition(state, {
3472
+ lpPosition: params.lpPosition,
3473
+ market: params.market.selfAddress,
3474
+ priceIdPt,
3475
+ priceIdSy,
3476
+ ticksKey: params.market.ticksKey,
3477
+ });
3478
+ }
2022
3479
  /**
2023
3480
  * Resolve a high-level CLMM action descriptor into raw Solana instructions.
2024
3481
  * Loads the MarketThree from cache, derives token accounts from the vault
@@ -2051,31 +3508,34 @@ async function buildClmmInstruction(ix, owner, connection, buckets, setupContext
2051
3508
  // Include the CLMM market's ALT so callers can compress remaining accounts
2052
3509
  buckets.addressLookupTableAddresses.push(market.addressLookupTable);
2053
3510
  switch (ix.action) {
2054
- case ClmmAction.DEPOSIT_LIQUIDITY:
2055
- buildClmmDepositLiquidity(ix, market, owner, buckets);
3511
+ case vault_instruction_types_2.ClmmAction.DEPOSIT_LIQUIDITY:
3512
+ await buildClmmDepositLiquidity(ix, market, owner, buckets, setupContext);
2056
3513
  break;
2057
- case ClmmAction.ADD_LIQUIDITY:
3514
+ case vault_instruction_types_2.ClmmAction.ADD_LIQUIDITY:
3515
+ await ensureTrackedClmmPosition({ lpPosition: ix.lpPosition, setupContext });
2058
3516
  buildClmmAddLiquidity(ix, market, owner, buckets);
2059
3517
  break;
2060
- case ClmmAction.WITHDRAW_LIQUIDITY:
3518
+ case vault_instruction_types_2.ClmmAction.WITHDRAW_LIQUIDITY:
3519
+ await ensureTrackedClmmPosition({ lpPosition: ix.lpPosition, setupContext });
2061
3520
  buildClmmWithdrawLiquidity(ix, market, owner, buckets);
2062
3521
  break;
2063
- case ClmmAction.TRADE_PT:
3522
+ case vault_instruction_types_2.ClmmAction.TRADE_PT:
2064
3523
  buildClmmTradePt(ix, market, owner, buckets);
2065
3524
  break;
2066
- case ClmmAction.BUY_PT:
3525
+ case vault_instruction_types_2.ClmmAction.BUY_PT:
2067
3526
  buildClmmBuyPt(ix, market, owner, buckets);
2068
3527
  break;
2069
- case ClmmAction.SELL_PT:
3528
+ case vault_instruction_types_2.ClmmAction.SELL_PT:
2070
3529
  buildClmmSellPt(ix, market, owner, buckets);
2071
3530
  break;
2072
- case ClmmAction.BUY_YT:
3531
+ case vault_instruction_types_2.ClmmAction.BUY_YT:
2073
3532
  await buildClmmBuyYt(ix, market, owner, buckets, setupContext);
2074
3533
  break;
2075
- case ClmmAction.SELL_YT:
3534
+ case vault_instruction_types_2.ClmmAction.SELL_YT:
2076
3535
  await buildClmmSellYt(ix, market, owner, buckets, setupContext);
2077
3536
  break;
2078
- case ClmmAction.CLAIM_FARM_EMISSION:
3537
+ case vault_instruction_types_2.ClmmAction.CLAIM_FARM_EMISSION:
3538
+ await ensureTrackedClmmPosition({ lpPosition: ix.lpPosition, setupContext });
2079
3539
  buildClmmClaimFarmEmission(ix, market, owner, buckets);
2080
3540
  break;
2081
3541
  }
@@ -2084,7 +3544,7 @@ async function buildClmmInstruction(ix, owner, connection, buckets, setupContext
2084
3544
  * Create a new LP position. Generates the keypair internally and adds it
2085
3545
  * to `buckets.signers` so consumers can include it in the transaction.
2086
3546
  */
2087
- function buildClmmDepositLiquidity(ix, market, owner, buckets) {
3547
+ async function buildClmmDepositLiquidity(ix, market, owner, buckets, setupContext) {
2088
3548
  const ptSrc = (0, spl_token_1.getAssociatedTokenAddressSync)(market.mintPt, owner, true, spl_token_1.TOKEN_PROGRAM_ID);
2089
3549
  const sySrc = (0, spl_token_1.getAssociatedTokenAddressSync)(market.mintSy, owner, true, spl_token_1.TOKEN_PROGRAM_ID);
2090
3550
  const { ix: depositIx, signers: lpPositionKeypair } = market.ixDepositLiquidity({
@@ -2098,6 +3558,12 @@ function buildClmmDepositLiquidity(ix, market, owner, buckets) {
2098
3558
  });
2099
3559
  buckets.syncInstructions.push(depositIx);
2100
3560
  buckets.signers.push(lpPositionKeypair);
3561
+ await queueClmmPositionTrackingAfterDeposit({
3562
+ market,
3563
+ lpPosition: lpPositionKeypair.publicKey,
3564
+ postInstructions: buckets.postInstructions,
3565
+ setupContext,
3566
+ });
2101
3567
  }
2102
3568
  /** Add liquidity to an existing LP position. */
2103
3569
  function buildClmmAddLiquidity(ix, market, owner, buckets) {
@@ -2191,6 +3657,7 @@ async function buildClmmBuyYt(ix, market, owner, buckets, setupContext) {
2191
3657
  }
2192
3658
  const { ixs, setupIxs } = market.ixBuyYt({
2193
3659
  trader: owner,
3660
+ payer: setupContext?.signer,
2194
3661
  ytOut: ix.ytOut,
2195
3662
  maxSyIn: ix.maxSyIn,
2196
3663
  lnImpliedApyLimit: ix.lnImpliedApyLimit,
@@ -2212,6 +3679,7 @@ async function buildClmmSellYt(ix, market, owner, buckets, setupContext) {
2212
3679
  }
2213
3680
  const { ixs, setupIxs } = market.ixSellYt({
2214
3681
  trader: owner,
3682
+ payer: setupContext?.signer,
2215
3683
  ytIn: ix.ytIn,
2216
3684
  minSyOut: ix.minSyOut,
2217
3685
  lnImpliedApyLimit: ix.lnImpliedApyLimit,