@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.
- package/build/client/vaults/index.d.ts +2 -0
- package/build/client/vaults/index.js +2 -0
- package/build/client/vaults/index.js.map +1 -1
- package/build/client/vaults/types/index.d.ts +2 -0
- package/build/client/vaults/types/index.js +2 -0
- package/build/client/vaults/types/index.js.map +1 -1
- package/build/client/vaults/types/kaminoFarmEntry.d.ts +15 -0
- package/build/client/vaults/types/kaminoFarmEntry.js +17 -0
- package/build/client/vaults/types/kaminoFarmEntry.js.map +1 -0
- package/build/client/vaults/types/kaminoObligationEntry.d.ts +21 -4
- package/build/client/vaults/types/kaminoObligationEntry.js +2 -1
- package/build/client/vaults/types/kaminoObligationEntry.js.map +1 -1
- package/build/client/vaults/types/positionUpdate.d.ts +9 -0
- package/build/client/vaults/types/positionUpdate.js +23 -0
- package/build/client/vaults/types/positionUpdate.js.map +1 -1
- package/build/client/vaults/types/proposalAction.js +0 -3
- package/build/client/vaults/types/proposalAction.js.map +1 -1
- package/build/client/vaults/types/reserveFarmMapping.d.ts +19 -0
- package/build/client/vaults/types/reserveFarmMapping.js +18 -0
- package/build/client/vaults/types/reserveFarmMapping.js.map +1 -0
- package/build/client/vaults/types/strategyPosition.d.ts +5 -0
- package/build/client/vaults/types/strategyPosition.js +5 -0
- package/build/client/vaults/types/strategyPosition.js.map +1 -1
- package/build/exponentVaults/aumCalculator.d.ts +25 -4
- package/build/exponentVaults/aumCalculator.js +236 -15
- package/build/exponentVaults/aumCalculator.js.map +1 -1
- package/build/exponentVaults/fetcher.d.ts +52 -0
- package/build/exponentVaults/fetcher.js +199 -0
- package/build/exponentVaults/fetcher.js.map +1 -0
- package/build/exponentVaults/index.d.ts +10 -9
- package/build/exponentVaults/index.js +26 -8
- package/build/exponentVaults/index.js.map +1 -1
- package/build/exponentVaults/kamino-farms.d.ts +144 -0
- package/build/exponentVaults/kamino-farms.js +396 -0
- package/build/exponentVaults/kamino-farms.js.map +1 -0
- package/build/exponentVaults/loopscale/client.d.ts +240 -0
- package/build/exponentVaults/loopscale/client.js +590 -0
- package/build/exponentVaults/loopscale/client.js.map +1 -0
- package/build/exponentVaults/loopscale/client.test.d.ts +1 -0
- package/build/exponentVaults/loopscale/client.test.js +183 -0
- package/build/exponentVaults/loopscale/client.test.js.map +1 -0
- package/build/exponentVaults/loopscale/helpers.d.ts +29 -0
- package/build/exponentVaults/loopscale/helpers.js +119 -0
- package/build/exponentVaults/loopscale/helpers.js.map +1 -0
- package/build/exponentVaults/loopscale/index.d.ts +3 -0
- package/build/exponentVaults/loopscale/index.js +12 -0
- package/build/exponentVaults/loopscale/index.js.map +1 -0
- package/build/exponentVaults/loopscale/prepared-transactions.d.ts +13 -0
- package/build/exponentVaults/loopscale/prepared-transactions.js +271 -0
- package/build/exponentVaults/loopscale/prepared-transactions.js.map +1 -0
- package/build/exponentVaults/loopscale/prepared-transactions.test.d.ts +1 -0
- package/build/exponentVaults/loopscale/prepared-transactions.test.js +400 -0
- package/build/exponentVaults/loopscale/prepared-transactions.test.js.map +1 -0
- package/build/exponentVaults/loopscale/prepared-types.d.ts +62 -0
- package/build/exponentVaults/loopscale/prepared-types.js +3 -0
- package/build/exponentVaults/loopscale/prepared-types.js.map +1 -0
- package/build/exponentVaults/loopscale/response-plan.d.ts +69 -0
- package/build/exponentVaults/loopscale/response-plan.js +141 -0
- package/build/exponentVaults/loopscale/response-plan.js.map +1 -0
- package/build/exponentVaults/loopscale/response-plan.test.d.ts +1 -0
- package/build/exponentVaults/loopscale/response-plan.test.js +139 -0
- package/build/exponentVaults/loopscale/response-plan.test.js.map +1 -0
- package/build/exponentVaults/loopscale/send-plan.d.ts +75 -0
- package/build/exponentVaults/loopscale/send-plan.js +235 -0
- package/build/exponentVaults/loopscale/send-plan.js.map +1 -0
- package/build/exponentVaults/loopscale/types.d.ts +443 -0
- package/build/exponentVaults/loopscale/types.js +3 -0
- package/build/exponentVaults/loopscale/types.js.map +1 -0
- package/build/exponentVaults/loopscale-client.d.ts +113 -524
- package/build/exponentVaults/loopscale-client.js +296 -539
- package/build/exponentVaults/loopscale-client.js.map +1 -1
- package/build/exponentVaults/loopscale-client.test.d.ts +1 -0
- package/build/exponentVaults/loopscale-client.test.js +162 -0
- package/build/exponentVaults/loopscale-client.test.js.map +1 -0
- package/build/exponentVaults/loopscale-client.types.d.ts +425 -0
- package/build/exponentVaults/loopscale-client.types.js +3 -0
- package/build/exponentVaults/loopscale-client.types.js.map +1 -0
- package/build/exponentVaults/loopscale-execution.d.ts +125 -0
- package/build/exponentVaults/loopscale-execution.js +341 -0
- package/build/exponentVaults/loopscale-execution.js.map +1 -0
- package/build/exponentVaults/loopscale-execution.test.d.ts +1 -0
- package/build/exponentVaults/loopscale-execution.test.js +139 -0
- package/build/exponentVaults/loopscale-execution.test.js.map +1 -0
- package/build/exponentVaults/loopscale-vault.d.ts +115 -0
- package/build/exponentVaults/loopscale-vault.js +275 -0
- package/build/exponentVaults/loopscale-vault.js.map +1 -0
- package/build/exponentVaults/loopscale-vault.test.d.ts +1 -0
- package/build/exponentVaults/loopscale-vault.test.js +102 -0
- package/build/exponentVaults/loopscale-vault.test.js.map +1 -0
- package/build/exponentVaults/policyBuilders.d.ts +62 -0
- package/build/exponentVaults/policyBuilders.js +119 -2
- package/build/exponentVaults/policyBuilders.js.map +1 -1
- package/build/exponentVaults/pricePathResolver.d.ts +45 -0
- package/build/exponentVaults/pricePathResolver.js +198 -0
- package/build/exponentVaults/pricePathResolver.js.map +1 -0
- package/build/exponentVaults/pricePathResolver.test.d.ts +1 -0
- package/build/exponentVaults/pricePathResolver.test.js +369 -0
- package/build/exponentVaults/pricePathResolver.test.js.map +1 -0
- package/build/exponentVaults/syncTransaction.js +4 -1
- package/build/exponentVaults/syncTransaction.js.map +1 -1
- package/build/exponentVaults/titan-quote.js +170 -36
- package/build/exponentVaults/titan-quote.js.map +1 -1
- package/build/exponentVaults/vault-instruction-types.d.ts +363 -0
- package/build/exponentVaults/vault-instruction-types.js +128 -0
- package/build/exponentVaults/vault-instruction-types.js.map +1 -0
- package/build/exponentVaults/vault-interaction.d.ts +203 -343
- package/build/exponentVaults/vault-interaction.js +1894 -426
- package/build/exponentVaults/vault-interaction.js.map +1 -1
- package/build/exponentVaults/vault-interaction.kamino-vault.test.d.ts +1 -0
- package/build/exponentVaults/vault-interaction.kamino-vault.test.js +143 -0
- package/build/exponentVaults/vault-interaction.kamino-vault.test.js.map +1 -0
- package/build/exponentVaults/vault.d.ts +51 -2
- package/build/exponentVaults/vault.js +324 -48
- package/build/exponentVaults/vault.js.map +1 -1
- package/build/exponentVaults/vaultTransactionBuilder.d.ts +100 -134
- package/build/exponentVaults/vaultTransactionBuilder.js +383 -285
- package/build/exponentVaults/vaultTransactionBuilder.js.map +1 -1
- package/build/exponentVaults/vaultTransactionBuilder.test.d.ts +1 -0
- package/build/exponentVaults/vaultTransactionBuilder.test.js +297 -0
- package/build/exponentVaults/vaultTransactionBuilder.test.js.map +1 -0
- package/build/marketThree.d.ts +6 -2
- package/build/marketThree.js +10 -8
- package/build/marketThree.js.map +1 -1
- package/package.json +34 -32
- package/src/client/vaults/index.ts +2 -0
- package/src/client/vaults/types/index.ts +2 -0
- package/src/client/vaults/types/kaminoFarmEntry.ts +32 -0
- package/src/client/vaults/types/kaminoObligationEntry.ts +6 -3
- package/src/client/vaults/types/positionUpdate.ts +62 -0
- package/src/client/vaults/types/proposalAction.ts +0 -3
- package/src/client/vaults/types/reserveFarmMapping.ts +35 -0
- package/src/client/vaults/types/strategyPosition.ts +18 -1
- package/src/exponentVaults/aumCalculator.ts +353 -16
- package/src/exponentVaults/fetcher.ts +257 -0
- package/src/exponentVaults/index.ts +65 -40
- package/src/exponentVaults/kamino-farms.ts +538 -0
- package/src/exponentVaults/loopscale/client.ts +808 -0
- package/src/exponentVaults/loopscale/helpers.ts +172 -0
- package/src/exponentVaults/loopscale/index.ts +57 -0
- package/src/exponentVaults/loopscale/prepared-transactions.ts +435 -0
- package/src/exponentVaults/loopscale/prepared-types.ts +73 -0
- package/src/exponentVaults/loopscale/types.ts +466 -0
- package/src/exponentVaults/policyBuilders.ts +170 -0
- package/src/exponentVaults/pricePathResolver.test.ts +466 -0
- package/src/exponentVaults/pricePathResolver.ts +273 -0
- package/src/exponentVaults/syncTransaction.ts +6 -1
- package/src/exponentVaults/titan-quote.ts +231 -45
- package/src/exponentVaults/vault-instruction-types.ts +493 -0
- package/src/exponentVaults/vault-interaction.kamino-vault.test.ts +149 -0
- package/src/exponentVaults/vault-interaction.ts +2818 -799
- package/src/exponentVaults/vault.ts +474 -63
- package/src/exponentVaults/vaultTransactionBuilder.test.ts +349 -0
- package/src/exponentVaults/vaultTransactionBuilder.ts +581 -433
- package/src/marketThree.ts +14 -6
- 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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
//
|
|
70
|
+
// Vault Instruction Types (re-exported from vault-instruction-types.ts)
|
|
137
71
|
// ============================================================================
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
(function (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
256
|
-
*
|
|
257
|
-
* @
|
|
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(
|
|
276
|
-
const
|
|
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 {
|
|
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
|
|
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
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
|
|
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
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
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
|
|
469
|
-
|
|
470
|
-
|
|
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
|
|
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
|
|
492
|
-
|
|
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
|
|
504
|
-
|
|
505
|
-
|
|
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
|
|
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
|
|
546
|
-
|
|
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
|
-
|
|
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
|
|
607
|
-
|
|
608
|
-
|
|
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
|
|
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
|
|
641
|
-
|
|
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
|
|
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
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1992
|
+
function createKaminoInitUserMetadataInstruction(owner) {
|
|
880
1993
|
const userMetadata = (0, constants_1.getKaminoUserMetadata)(owner, policyBuilders_1.KAMINO_LENDING_PROGRAM_ID);
|
|
881
|
-
|
|
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
|
-
|
|
894
|
-
const lendingMarket = kamino_markets_1.KAMINO_MARKETS[
|
|
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
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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:
|
|
2870
|
+
virtualOffer: true,
|
|
1521
2871
|
expirySeconds: ix.expirySeconds ?? 3600,
|
|
1522
2872
|
mintSy: orderbook.vault.mintSy,
|
|
1523
|
-
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:
|
|
2884
|
+
virtualOffer: true,
|
|
1535
2885
|
expirySeconds: ix.expirySeconds ?? 3600,
|
|
1536
2886
|
mintSy: orderbook.vault.mintSy,
|
|
1537
|
-
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 {
|
|
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
|
|
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,
|