@elmntl/jlpd-sdk 0.1.11 → 0.13.4
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/LICENSE +201 -0
- package/README.md +185 -404
- package/dist/common/ata.d.ts +26 -0
- package/dist/common/ata.js +48 -0
- package/dist/common/buffer.d.ts +15 -0
- package/dist/common/buffer.js +118 -0
- package/dist/common/connection.d.ts +13 -0
- package/dist/common/connection.js +2 -0
- package/dist/common/constants.d.ts +14 -0
- package/dist/common/constants.js +31 -0
- package/dist/common/index.d.ts +6 -0
- package/dist/common/index.js +28 -0
- package/dist/common/strategy-interface.d.ts +59 -0
- package/dist/common/strategy-interface.js +42 -0
- package/dist/elemental-lend/accounts.d.ts +103 -0
- package/dist/elemental-lend/accounts.js +354 -0
- package/dist/elemental-lend/constants.d.ts +25 -0
- package/dist/elemental-lend/constants.js +48 -0
- package/dist/elemental-lend/index.d.ts +8 -0
- package/dist/elemental-lend/index.js +24 -0
- package/dist/elemental-lend/instructions.d.ts +104 -0
- package/dist/elemental-lend/instructions.js +266 -0
- package/dist/elemental-lend/jupiter-lend.d.ts +91 -0
- package/dist/elemental-lend/jupiter-lend.js +189 -0
- package/dist/elemental-lend/kamino-vault.d.ts +173 -0
- package/dist/elemental-lend/kamino-vault.js +483 -0
- package/dist/elemental-lend/pda.d.ts +12 -0
- package/dist/elemental-lend/pda.js +24 -0
- package/dist/elemental-lend/protocol-actions.d.ts +56 -0
- package/dist/elemental-lend/protocol-actions.js +244 -0
- package/dist/elemental-lend/types.d.ts +113 -0
- package/dist/elemental-lend/types.js +2 -0
- package/dist/elemental-lend-v2/accounts.d.ts +14 -0
- package/dist/elemental-lend-v2/accounts.js +136 -0
- package/dist/elemental-lend-v2/adapters.d.ts +22 -0
- package/dist/elemental-lend-v2/adapters.js +50 -0
- package/dist/elemental-lend-v2/constants.d.ts +48 -0
- package/dist/elemental-lend-v2/constants.js +104 -0
- package/dist/elemental-lend-v2/index.d.ts +8 -0
- package/dist/elemental-lend-v2/index.js +24 -0
- package/dist/elemental-lend-v2/instructions.d.ts +96 -0
- package/dist/elemental-lend-v2/instructions.js +190 -0
- package/dist/elemental-lend-v2/lut.d.ts +20 -0
- package/dist/elemental-lend-v2/lut.js +65 -0
- package/dist/elemental-lend-v2/pda.d.ts +9 -0
- package/dist/elemental-lend-v2/pda.js +27 -0
- package/dist/elemental-lend-v2/types.d.ts +105 -0
- package/dist/elemental-lend-v2/types.js +2 -0
- package/dist/elemental-lend-v2/update-aum.d.ts +29 -0
- package/dist/elemental-lend-v2/update-aum.js +82 -0
- package/dist/index.d.ts +5 -3552
- package/dist/index.js +37 -4454
- package/dist/jlpd-strategy/accounts.d.ts +83 -0
- package/dist/jlpd-strategy/accounts.js +216 -0
- package/dist/jlpd-strategy/adapter.d.ts +81 -0
- package/dist/jlpd-strategy/adapter.js +118 -0
- package/dist/jlpd-strategy/base-to-base-swap.d.ts +74 -0
- package/dist/jlpd-strategy/base-to-base-swap.js +205 -0
- package/dist/jlpd-strategy/constants.d.ts +127 -0
- package/dist/jlpd-strategy/constants.js +174 -0
- package/dist/jlpd-strategy/fluid-view.d.ts +199 -0
- package/dist/jlpd-strategy/fluid-view.js +799 -0
- package/dist/jlpd-strategy/hedge-derived.d.ts +135 -0
- package/dist/jlpd-strategy/hedge-derived.js +231 -0
- package/dist/jlpd-strategy/hedge-instructions.d.ts +210 -0
- package/dist/jlpd-strategy/hedge-instructions.js +300 -0
- package/dist/jlpd-strategy/hedge-state.d.ts +88 -0
- package/dist/jlpd-strategy/hedge-state.js +110 -0
- package/dist/jlpd-strategy/index.d.ts +17 -0
- package/dist/jlpd-strategy/index.js +33 -0
- package/dist/jlpd-strategy/instructions.d.ts +159 -0
- package/dist/jlpd-strategy/instructions.js +234 -0
- package/dist/jlpd-strategy/jlp-borrow.d.ts +63 -0
- package/dist/jlpd-strategy/jlp-borrow.js +87 -0
- package/dist/jlpd-strategy/jlp-data.d.ts +166 -0
- package/dist/jlpd-strategy/jlp-data.js +611 -0
- package/dist/jlpd-strategy/jupusd-earn.d.ts +90 -0
- package/dist/jlpd-strategy/jupusd-earn.js +166 -0
- package/dist/jlpd-strategy/live-jlp-price.d.ts +46 -0
- package/dist/jlpd-strategy/live-jlp-price.js +267 -0
- package/dist/jlpd-strategy/pda.d.ts +13 -0
- package/dist/jlpd-strategy/pda.js +28 -0
- package/dist/jlpd-strategy/settle-yield.d.ts +45 -0
- package/dist/jlpd-strategy/settle-yield.js +113 -0
- package/dist/jlpd-strategy/swap-jlp.d.ts +198 -0
- package/dist/jlpd-strategy/swap-jlp.js +586 -0
- package/dist/jlpd-strategy/types.d.ts +101 -0
- package/dist/jlpd-strategy/types.js +29 -0
- package/dist/p-stv-core/accounts.d.ts +152 -0
- package/dist/p-stv-core/accounts.js +407 -0
- package/dist/p-stv-core/constants.d.ts +138 -0
- package/dist/p-stv-core/constants.js +181 -0
- package/dist/p-stv-core/events.d.ts +7 -0
- package/dist/p-stv-core/events.js +246 -0
- package/dist/p-stv-core/index.d.ts +11 -0
- package/dist/p-stv-core/index.js +27 -0
- package/dist/p-stv-core/instructions.d.ts +637 -0
- package/dist/p-stv-core/instructions.js +670 -0
- package/dist/p-stv-core/lut.d.ts +41 -0
- package/dist/p-stv-core/lut.js +81 -0
- package/dist/p-stv-core/pda.d.ts +30 -0
- package/dist/p-stv-core/pda.js +65 -0
- package/dist/p-stv-core/prices.d.ts +20 -0
- package/dist/p-stv-core/prices.js +77 -0
- package/dist/p-stv-core/remaining-accounts.d.ts +107 -0
- package/dist/p-stv-core/remaining-accounts.js +326 -0
- package/dist/p-stv-core/send-tx.d.ts +72 -0
- package/dist/p-stv-core/send-tx.js +290 -0
- package/dist/p-stv-core/sol-wrap.d.ts +34 -0
- package/dist/p-stv-core/sol-wrap.js +74 -0
- package/dist/p-stv-core/types.d.ts +361 -0
- package/dist/p-stv-core/types.js +2 -0
- package/package.json +56 -15
- package/dist/index.d.mts +0 -3552
- package/dist/index.mjs +0 -4363
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kamino kVault account builders for deposit/withdraw.
|
|
3
|
+
*
|
|
4
|
+
* Builds the remaining_accounts arrays needed by elemental-lend's
|
|
5
|
+
* kvault_deposit and kvault_withdraw instructions.
|
|
6
|
+
*
|
|
7
|
+
* For deposit: 11 fixed accounts derived from vault_state PDAs.
|
|
8
|
+
* For withdraw: 21 fixed accounts — first 12 derivable, then 9 KLend-specific
|
|
9
|
+
* accounts read from vault_state and reserve on-chain data.
|
|
10
|
+
*/
|
|
11
|
+
import { AccountMeta, PublicKey, TransactionInstruction } from "@solana/web3.js";
|
|
12
|
+
import { KLEND_PROGRAM } from "./constants";
|
|
13
|
+
import type { SolanaConnection } from "../common/connection";
|
|
14
|
+
export { KLEND_PROGRAM };
|
|
15
|
+
/** Kamino Vault global config (singleton, mainnet) */
|
|
16
|
+
export declare const KAMINO_GLOBAL_CONFIG: PublicKey;
|
|
17
|
+
/** Derive token_vault PDA: ["token_vault", vault_state] under Kamino Vault */
|
|
18
|
+
export declare function findKvaultTokenVault(vaultState: PublicKey): [PublicKey, number];
|
|
19
|
+
/** Derive base_vault_authority PDA: ["authority", vault_state] under Kamino Vault */
|
|
20
|
+
export declare function findKvaultAuthority(vaultState: PublicKey): [PublicKey, number];
|
|
21
|
+
/** Derive shares_mint PDA: ["shares", vault_state] under Kamino Vault */
|
|
22
|
+
export declare function findKvaultSharesMint(vaultState: PublicKey): [PublicKey, number];
|
|
23
|
+
/** Derive event_authority PDA: ["__event_authority"] under Kamino Vault */
|
|
24
|
+
export declare function findKvaultEventAuthority(): [PublicKey, number];
|
|
25
|
+
/** Derive lending_market_authority PDA: ["lma", lending_market] under KLend */
|
|
26
|
+
export declare function findLendingMarketAuthority(lendingMarket: PublicKey): [PublicKey, number];
|
|
27
|
+
/**
|
|
28
|
+
* Read the first VaultAllocation's reserve and ctokenVault from VaultState data.
|
|
29
|
+
*
|
|
30
|
+
* @param vaultStateData - Raw account data (including 8-byte discriminator)
|
|
31
|
+
* @param allocationIndex - Which allocation slot to read (default 0)
|
|
32
|
+
*/
|
|
33
|
+
export declare function readVaultAllocation(vaultStateData: Buffer | Uint8Array, allocationIndex?: number): {
|
|
34
|
+
reserve: PublicKey;
|
|
35
|
+
ctokenVault: PublicKey;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Read KLend-specific accounts from a Reserve account's raw data.
|
|
39
|
+
*
|
|
40
|
+
* @param reserveData - Raw KLend Reserve account data (including 8-byte discriminator)
|
|
41
|
+
*/
|
|
42
|
+
export declare function readReserveAccounts(reserveData: Buffer | Uint8Array): {
|
|
43
|
+
lendingMarket: PublicKey;
|
|
44
|
+
reserveLiquiditySupply: PublicKey;
|
|
45
|
+
reserveCollateralMint: PublicKey;
|
|
46
|
+
reserveCollateralTokenProgram: PublicKey;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Build the 11 remaining_accounts for elemental-lend's kvault_deposit instruction.
|
|
50
|
+
*
|
|
51
|
+
* Account order matches kamino_vault::deposit_accounts:
|
|
52
|
+
* 0. strategy_state PDA (signer, mut)
|
|
53
|
+
* 1. vault_state (mut)
|
|
54
|
+
* 2. token_vault (mut) — PDA ["token_vault", vault_state]
|
|
55
|
+
* 3. token_mint — base token mint
|
|
56
|
+
* 4. base_vault_authority — PDA ["authority", vault_state]
|
|
57
|
+
* 5. shares_mint (mut) — PDA ["shares", vault_state]
|
|
58
|
+
* 6. user_token_ata (mut) — strategy's base ATA
|
|
59
|
+
* 7. user_shares_ata (mut) — strategy's kvToken ATA
|
|
60
|
+
* 8. klend_program
|
|
61
|
+
* 9. token_program — SPL Token
|
|
62
|
+
* 10. shares_token_program — SPL Token
|
|
63
|
+
*
|
|
64
|
+
* After the 11 fixed accounts, callers should append reserve accounts for refresh.
|
|
65
|
+
*/
|
|
66
|
+
export declare function buildKvaultDepositAccounts(vaultState: PublicKey, strategyStatePda: PublicKey, strategyBaseAta: PublicKey, strategySharesAta: PublicKey, baseMint: PublicKey, reserves?: {
|
|
67
|
+
reserve: PublicKey;
|
|
68
|
+
lendingMarket: PublicKey;
|
|
69
|
+
}[]): AccountMeta[];
|
|
70
|
+
/**
|
|
71
|
+
* KLend reserve accounts needed for kvault_withdraw.
|
|
72
|
+
* Read from vault_state and reserve on-chain data using
|
|
73
|
+
* readVaultAllocation() and readReserveAccounts().
|
|
74
|
+
*/
|
|
75
|
+
export interface KvaultWithdrawReserveConfig {
|
|
76
|
+
reserve: PublicKey;
|
|
77
|
+
ctokenVault: PublicKey;
|
|
78
|
+
lendingMarket: PublicKey;
|
|
79
|
+
lendingMarketAuthority: PublicKey;
|
|
80
|
+
reserveLiquiditySupply: PublicKey;
|
|
81
|
+
reserveCollateralMint: PublicKey;
|
|
82
|
+
reserveCollateralTokenProgram: PublicKey;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Build the KvaultWithdrawReserveConfig from raw on-chain data.
|
|
86
|
+
*
|
|
87
|
+
* @param vaultStateData - Raw Kamino VaultState account data
|
|
88
|
+
* @param reserveData - Raw KLend Reserve account data
|
|
89
|
+
* @param allocationIndex - VaultAllocation index (default 0)
|
|
90
|
+
*/
|
|
91
|
+
export declare function buildKvaultWithdrawReserveConfig(vaultStateData: Buffer | Uint8Array, reserveData: Buffer | Uint8Array, allocationIndex?: number): KvaultWithdrawReserveConfig;
|
|
92
|
+
/**
|
|
93
|
+
* Build the 21 remaining_accounts for elemental-lend's kvault_withdraw instruction.
|
|
94
|
+
*
|
|
95
|
+
* Account order matches kamino_vault::withdraw_accounts:
|
|
96
|
+
* WithdrawFromAvailable (0-11):
|
|
97
|
+
* 0. strategy_state PDA (signer, mut)
|
|
98
|
+
* 1. vault_state (mut)
|
|
99
|
+
* 2. global_config
|
|
100
|
+
* 3. token_vault (mut)
|
|
101
|
+
* 4. base_vault_authority
|
|
102
|
+
* 5. user_token_ata (mut) — strategy's base ATA
|
|
103
|
+
* 6. token_mint (mut)
|
|
104
|
+
* 7. user_shares_ata (mut) — strategy's kvToken ATA
|
|
105
|
+
* 8. shares_mint (mut)
|
|
106
|
+
* 9. token_program
|
|
107
|
+
* 10. shares_token_program
|
|
108
|
+
* 11. klend_program
|
|
109
|
+
* WithdrawFromInvested (12-20):
|
|
110
|
+
* 12. vault_state (mut, dup)
|
|
111
|
+
* 13. reserve (mut)
|
|
112
|
+
* 14. ctoken_vault (mut)
|
|
113
|
+
* 15. lending_market
|
|
114
|
+
* 16. lending_market_authority
|
|
115
|
+
* 17. reserve_liquidity_supply (mut)
|
|
116
|
+
* 18. reserve_collateral_mint (mut)
|
|
117
|
+
* 19. reserve_collateral_token_program
|
|
118
|
+
* 20. instruction_sysvar_account
|
|
119
|
+
*
|
|
120
|
+
* After the 21 fixed accounts, callers should append reserve accounts for refresh.
|
|
121
|
+
*/
|
|
122
|
+
export declare function buildKvaultWithdrawAccounts(vaultState: PublicKey, strategyStatePda: PublicKey, strategyBaseAta: PublicKey, strategySharesAta: PublicKey, baseMint: PublicKey, reserveConfig: KvaultWithdrawReserveConfig, globalConfig?: PublicKey): AccountMeta[];
|
|
123
|
+
/** Staleness threshold in seconds (matches elemental-lend KVAULT_STALENESS_THRESHOLD) */
|
|
124
|
+
export declare const KVAULT_STALENESS_THRESHOLD = 120;
|
|
125
|
+
/**
|
|
126
|
+
* Read `last_fee_charge_timestamp` from raw Kamino VaultState account data.
|
|
127
|
+
* Returns undefined if data is too short.
|
|
128
|
+
*/
|
|
129
|
+
export declare function readKvaultLastFeeChargeTs(data: Buffer | Uint8Array): number | undefined;
|
|
130
|
+
/**
|
|
131
|
+
* Check if a Kamino kVault is stale (needs an `invest` call to refresh).
|
|
132
|
+
*
|
|
133
|
+
* @param vaultStateData - Raw account data for the Kamino VaultState
|
|
134
|
+
* @param currentSlotTimestamp - Current cluster timestamp (seconds)
|
|
135
|
+
* @returns true if the vault is stale (last_fee_charge_timestamp is older than threshold)
|
|
136
|
+
*/
|
|
137
|
+
export declare function isKvaultStale(vaultStateData: Buffer | Uint8Array, currentSlotTimestamp: number): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Check if any registered kVault protocol is stale and return the stale vault states.
|
|
140
|
+
*
|
|
141
|
+
* @param connection - Solana connection
|
|
142
|
+
* @param protocols - Protocol pubkeys from the lend strategy state
|
|
143
|
+
* @returns Array of stale kVault state pubkeys
|
|
144
|
+
*/
|
|
145
|
+
export declare function findStaleKvaults(connection: SolanaConnection, protocols: PublicKey[]): Promise<PublicKey[]>;
|
|
146
|
+
/**
|
|
147
|
+
* Build a Kamino `invest` instruction to refresh a stale kVault.
|
|
148
|
+
*
|
|
149
|
+
* `invest` is permissionless — anyone can call it. It refreshes the vault's
|
|
150
|
+
* `last_fee_charge_timestamp` which is required for elemental-lend's update_aum
|
|
151
|
+
* staleness check (120s threshold).
|
|
152
|
+
*
|
|
153
|
+
* @param payer - Transaction signer (pays for any crank fee, usually 0)
|
|
154
|
+
* @param payerTokenAccount - Payer's base token ATA (for crank fee)
|
|
155
|
+
* @param vaultState - Kamino vault_state pubkey
|
|
156
|
+
* @param baseMint - Base token mint (e.g., USDC)
|
|
157
|
+
* @param vaultStateData - Raw vault_state account data (for reading allocation)
|
|
158
|
+
* @param reserveData - Raw KLend reserve account data (for reading lending_market, etc.)
|
|
159
|
+
* @param allocationIndex - Which vault allocation to invest (default 0)
|
|
160
|
+
*/
|
|
161
|
+
export declare function buildKaminoInvestIx(payer: PublicKey, payerTokenAccount: PublicKey, vaultState: PublicKey, baseMint: PublicKey, vaultStateData: Buffer | Uint8Array, reserveData: Buffer | Uint8Array, allocationIndex?: number): TransactionInstruction;
|
|
162
|
+
/**
|
|
163
|
+
* Build Kamino `invest` instructions for all stale kVaults in a lend strategy.
|
|
164
|
+
*
|
|
165
|
+
* Checks each registered protocol for staleness and returns invest instructions
|
|
166
|
+
* for any that are stale.
|
|
167
|
+
*
|
|
168
|
+
* @param connection - Solana connection
|
|
169
|
+
* @param payer - Transaction signer
|
|
170
|
+
* @param baseMint - Base token mint
|
|
171
|
+
* @param protocols - Registered protocol pubkeys from lend strategy state
|
|
172
|
+
*/
|
|
173
|
+
export declare function buildKaminoInvestIxsIfStale(connection: SolanaConnection, payer: PublicKey, baseMint: PublicKey, protocols: PublicKey[]): Promise<TransactionInstruction[]>;
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Kamino kVault account builders for deposit/withdraw.
|
|
4
|
+
*
|
|
5
|
+
* Builds the remaining_accounts arrays needed by elemental-lend's
|
|
6
|
+
* kvault_deposit and kvault_withdraw instructions.
|
|
7
|
+
*
|
|
8
|
+
* For deposit: 11 fixed accounts derived from vault_state PDAs.
|
|
9
|
+
* For withdraw: 21 fixed accounts — first 12 derivable, then 9 KLend-specific
|
|
10
|
+
* accounts read from vault_state and reserve on-chain data.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.KVAULT_STALENESS_THRESHOLD = exports.KAMINO_GLOBAL_CONFIG = exports.KLEND_PROGRAM = void 0;
|
|
14
|
+
exports.findKvaultTokenVault = findKvaultTokenVault;
|
|
15
|
+
exports.findKvaultAuthority = findKvaultAuthority;
|
|
16
|
+
exports.findKvaultSharesMint = findKvaultSharesMint;
|
|
17
|
+
exports.findKvaultEventAuthority = findKvaultEventAuthority;
|
|
18
|
+
exports.findLendingMarketAuthority = findLendingMarketAuthority;
|
|
19
|
+
exports.readVaultAllocation = readVaultAllocation;
|
|
20
|
+
exports.readReserveAccounts = readReserveAccounts;
|
|
21
|
+
exports.buildKvaultDepositAccounts = buildKvaultDepositAccounts;
|
|
22
|
+
exports.buildKvaultWithdrawReserveConfig = buildKvaultWithdrawReserveConfig;
|
|
23
|
+
exports.buildKvaultWithdrawAccounts = buildKvaultWithdrawAccounts;
|
|
24
|
+
exports.readKvaultLastFeeChargeTs = readKvaultLastFeeChargeTs;
|
|
25
|
+
exports.isKvaultStale = isKvaultStale;
|
|
26
|
+
exports.findStaleKvaults = findStaleKvaults;
|
|
27
|
+
exports.buildKaminoInvestIx = buildKaminoInvestIx;
|
|
28
|
+
exports.buildKaminoInvestIxsIfStale = buildKaminoInvestIxsIfStale;
|
|
29
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
30
|
+
const constants_1 = require("./constants");
|
|
31
|
+
Object.defineProperty(exports, "KLEND_PROGRAM", { enumerable: true, get: function () { return constants_1.KLEND_PROGRAM; } });
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Constants
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/** Kamino Vault global config (singleton, mainnet) */
|
|
36
|
+
exports.KAMINO_GLOBAL_CONFIG = new web3_js_1.PublicKey("BKyTcUe6daNG8HbgBix2ugdRHbykG2dK9hPBBqhUyoEX");
|
|
37
|
+
/** SPL Token program */
|
|
38
|
+
const SPL_TOKEN_PROGRAM = new web3_js_1.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
39
|
+
/** Sysvar Instructions */
|
|
40
|
+
const SYSVAR_INSTRUCTIONS = new web3_js_1.PublicKey("Sysvar1nstructions1111111111111111111111111");
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// PDA Derivation Helpers
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
/** Derive token_vault PDA: ["token_vault", vault_state] under Kamino Vault */
|
|
45
|
+
function findKvaultTokenVault(vaultState) {
|
|
46
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token_vault"), vaultState.toBuffer()], constants_1.KAMINO_VAULT_PROGRAM);
|
|
47
|
+
}
|
|
48
|
+
/** Derive base_vault_authority PDA: ["authority", vault_state] under Kamino Vault */
|
|
49
|
+
function findKvaultAuthority(vaultState) {
|
|
50
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("authority"), vaultState.toBuffer()], constants_1.KAMINO_VAULT_PROGRAM);
|
|
51
|
+
}
|
|
52
|
+
/** Derive shares_mint PDA: ["shares", vault_state] under Kamino Vault */
|
|
53
|
+
function findKvaultSharesMint(vaultState) {
|
|
54
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("shares"), vaultState.toBuffer()], constants_1.KAMINO_VAULT_PROGRAM);
|
|
55
|
+
}
|
|
56
|
+
/** Derive event_authority PDA: ["__event_authority"] under Kamino Vault */
|
|
57
|
+
function findKvaultEventAuthority() {
|
|
58
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], constants_1.KAMINO_VAULT_PROGRAM);
|
|
59
|
+
}
|
|
60
|
+
/** Derive lending_market_authority PDA: ["lma", lending_market] under KLend */
|
|
61
|
+
function findLendingMarketAuthority(lendingMarket) {
|
|
62
|
+
return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("lma"), lendingMarket.toBuffer()], constants_1.KLEND_PROGRAM);
|
|
63
|
+
}
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// VaultState / Reserve Data Readers
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
/**
|
|
68
|
+
* Kamino VaultState layout offsets (from account data start, including 8-byte discriminator).
|
|
69
|
+
*
|
|
70
|
+
* VaultAllocation[0] starts at offset 312:
|
|
71
|
+
* [312..344] reserve (Pubkey)
|
|
72
|
+
* [344..376] ctokenVault (Pubkey)
|
|
73
|
+
* Each VaultAllocation is 2160 bytes. Index N starts at 312 + N*2160.
|
|
74
|
+
*/
|
|
75
|
+
const VAULT_ALLOCATION_OFFSET = 312; // 8 disc + 304 struct offset
|
|
76
|
+
const VAULT_ALLOCATION_SIZE = 2160;
|
|
77
|
+
/**
|
|
78
|
+
* KLend Reserve layout offsets (from account data start, including 8-byte discriminator).
|
|
79
|
+
* [32..64] lendingMarket (Pubkey)
|
|
80
|
+
* [160..192] liquidity.supplyVault (Pubkey)
|
|
81
|
+
* [408..440] liquidity.tokenProgram (Pubkey)
|
|
82
|
+
* [2560..2592] collateral.mintPubkey (Pubkey)
|
|
83
|
+
*/
|
|
84
|
+
const RESERVE_LENDING_MARKET_OFFSET = 32;
|
|
85
|
+
const RESERVE_LIQUIDITY_SUPPLY_OFFSET = 160;
|
|
86
|
+
const RESERVE_LIQUIDITY_TOKEN_PROGRAM_OFFSET = 408;
|
|
87
|
+
const RESERVE_COLLATERAL_MINT_OFFSET = 2560;
|
|
88
|
+
/** Read a Pubkey from a Buffer at a given offset */
|
|
89
|
+
function readPubkey(data, offset) {
|
|
90
|
+
return new web3_js_1.PublicKey(data.slice(offset, offset + 32));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Read the first VaultAllocation's reserve and ctokenVault from VaultState data.
|
|
94
|
+
*
|
|
95
|
+
* @param vaultStateData - Raw account data (including 8-byte discriminator)
|
|
96
|
+
* @param allocationIndex - Which allocation slot to read (default 0)
|
|
97
|
+
*/
|
|
98
|
+
function readVaultAllocation(vaultStateData, allocationIndex = 0) {
|
|
99
|
+
const base = VAULT_ALLOCATION_OFFSET + allocationIndex * VAULT_ALLOCATION_SIZE;
|
|
100
|
+
if (vaultStateData.length < base + 64) {
|
|
101
|
+
throw new Error(`VaultState data too short for allocation ${allocationIndex}: need ${base + 64}, got ${vaultStateData.length}`);
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
reserve: readPubkey(vaultStateData, base),
|
|
105
|
+
ctokenVault: readPubkey(vaultStateData, base + 32),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Read KLend-specific accounts from a Reserve account's raw data.
|
|
110
|
+
*
|
|
111
|
+
* @param reserveData - Raw KLend Reserve account data (including 8-byte discriminator)
|
|
112
|
+
*/
|
|
113
|
+
function readReserveAccounts(reserveData) {
|
|
114
|
+
if (reserveData.length < RESERVE_COLLATERAL_MINT_OFFSET + 32) {
|
|
115
|
+
throw new Error(`Reserve data too short: need ${RESERVE_COLLATERAL_MINT_OFFSET + 32}, got ${reserveData.length}`);
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
lendingMarket: readPubkey(reserveData, RESERVE_LENDING_MARKET_OFFSET),
|
|
119
|
+
reserveLiquiditySupply: readPubkey(reserveData, RESERVE_LIQUIDITY_SUPPLY_OFFSET),
|
|
120
|
+
reserveCollateralMint: readPubkey(reserveData, RESERVE_COLLATERAL_MINT_OFFSET),
|
|
121
|
+
reserveCollateralTokenProgram: readPubkey(reserveData, RESERVE_LIQUIDITY_TOKEN_PROGRAM_OFFSET),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Deposit Account Builder (11 fixed accounts)
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
/**
|
|
128
|
+
* Build the 11 remaining_accounts for elemental-lend's kvault_deposit instruction.
|
|
129
|
+
*
|
|
130
|
+
* Account order matches kamino_vault::deposit_accounts:
|
|
131
|
+
* 0. strategy_state PDA (signer, mut)
|
|
132
|
+
* 1. vault_state (mut)
|
|
133
|
+
* 2. token_vault (mut) — PDA ["token_vault", vault_state]
|
|
134
|
+
* 3. token_mint — base token mint
|
|
135
|
+
* 4. base_vault_authority — PDA ["authority", vault_state]
|
|
136
|
+
* 5. shares_mint (mut) — PDA ["shares", vault_state]
|
|
137
|
+
* 6. user_token_ata (mut) — strategy's base ATA
|
|
138
|
+
* 7. user_shares_ata (mut) — strategy's kvToken ATA
|
|
139
|
+
* 8. klend_program
|
|
140
|
+
* 9. token_program — SPL Token
|
|
141
|
+
* 10. shares_token_program — SPL Token
|
|
142
|
+
*
|
|
143
|
+
* After the 11 fixed accounts, callers should append reserve accounts for refresh.
|
|
144
|
+
*/
|
|
145
|
+
function buildKvaultDepositAccounts(vaultState, strategyStatePda, strategyBaseAta, strategySharesAta, baseMint, reserves = []) {
|
|
146
|
+
const [tokenVault] = findKvaultTokenVault(vaultState);
|
|
147
|
+
const [authority] = findKvaultAuthority(vaultState);
|
|
148
|
+
const [sharesMint] = findKvaultSharesMint(vaultState);
|
|
149
|
+
const [eventAuthority] = findKvaultEventAuthority();
|
|
150
|
+
const accounts = [
|
|
151
|
+
{ pubkey: strategyStatePda, isSigner: true, isWritable: true }, // 0
|
|
152
|
+
{ pubkey: vaultState, isSigner: false, isWritable: true }, // 1
|
|
153
|
+
{ pubkey: tokenVault, isSigner: false, isWritable: true }, // 2
|
|
154
|
+
{ pubkey: baseMint, isSigner: false, isWritable: false }, // 3
|
|
155
|
+
{ pubkey: authority, isSigner: false, isWritable: false }, // 4
|
|
156
|
+
{ pubkey: sharesMint, isSigner: false, isWritable: true }, // 5
|
|
157
|
+
{ pubkey: strategyBaseAta, isSigner: false, isWritable: true }, // 6
|
|
158
|
+
{ pubkey: strategySharesAta, isSigner: false, isWritable: true }, // 7
|
|
159
|
+
{ pubkey: constants_1.KLEND_PROGRAM, isSigner: false, isWritable: false }, // 8
|
|
160
|
+
{ pubkey: SPL_TOKEN_PROGRAM, isSigner: false, isWritable: false }, // 9
|
|
161
|
+
{ pubkey: SPL_TOKEN_PROGRAM, isSigner: false, isWritable: false }, // 10
|
|
162
|
+
{ pubkey: eventAuthority, isSigner: false, isWritable: false }, // 11 (Anchor event CPI)
|
|
163
|
+
{ pubkey: constants_1.KAMINO_VAULT_PROGRAM, isSigner: false, isWritable: false }, // 12 (program self-ref)
|
|
164
|
+
];
|
|
165
|
+
// Append reserve accounts for KLend batch refresh (writable), then unique lending markets (readonly).
|
|
166
|
+
// Kamino's cpi_refresh_reserves uses .take(reserves_count) to get just the reserves,
|
|
167
|
+
// then reads lending_market from each reserve's data for the CPI. The lending_market
|
|
168
|
+
// AccountInfo must be in the transaction but after the reserves.
|
|
169
|
+
const seenMarkets = new Set();
|
|
170
|
+
for (const { reserve } of reserves) {
|
|
171
|
+
accounts.push({ pubkey: reserve, isSigner: false, isWritable: true });
|
|
172
|
+
}
|
|
173
|
+
for (const { lendingMarket } of reserves) {
|
|
174
|
+
const key = lendingMarket.toBase58();
|
|
175
|
+
if (!seenMarkets.has(key)) {
|
|
176
|
+
seenMarkets.add(key);
|
|
177
|
+
accounts.push({ pubkey: lendingMarket, isSigner: false, isWritable: false });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return accounts;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Build the KvaultWithdrawReserveConfig from raw on-chain data.
|
|
184
|
+
*
|
|
185
|
+
* @param vaultStateData - Raw Kamino VaultState account data
|
|
186
|
+
* @param reserveData - Raw KLend Reserve account data
|
|
187
|
+
* @param allocationIndex - VaultAllocation index (default 0)
|
|
188
|
+
*/
|
|
189
|
+
function buildKvaultWithdrawReserveConfig(vaultStateData, reserveData, allocationIndex = 0) {
|
|
190
|
+
const alloc = readVaultAllocation(vaultStateData, allocationIndex);
|
|
191
|
+
const res = readReserveAccounts(reserveData);
|
|
192
|
+
const [lendingMarketAuthority] = findLendingMarketAuthority(res.lendingMarket);
|
|
193
|
+
return {
|
|
194
|
+
reserve: alloc.reserve,
|
|
195
|
+
ctokenVault: alloc.ctokenVault,
|
|
196
|
+
lendingMarket: res.lendingMarket,
|
|
197
|
+
lendingMarketAuthority,
|
|
198
|
+
reserveLiquiditySupply: res.reserveLiquiditySupply,
|
|
199
|
+
reserveCollateralMint: res.reserveCollateralMint,
|
|
200
|
+
reserveCollateralTokenProgram: res.reserveCollateralTokenProgram,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Build the 21 remaining_accounts for elemental-lend's kvault_withdraw instruction.
|
|
205
|
+
*
|
|
206
|
+
* Account order matches kamino_vault::withdraw_accounts:
|
|
207
|
+
* WithdrawFromAvailable (0-11):
|
|
208
|
+
* 0. strategy_state PDA (signer, mut)
|
|
209
|
+
* 1. vault_state (mut)
|
|
210
|
+
* 2. global_config
|
|
211
|
+
* 3. token_vault (mut)
|
|
212
|
+
* 4. base_vault_authority
|
|
213
|
+
* 5. user_token_ata (mut) — strategy's base ATA
|
|
214
|
+
* 6. token_mint (mut)
|
|
215
|
+
* 7. user_shares_ata (mut) — strategy's kvToken ATA
|
|
216
|
+
* 8. shares_mint (mut)
|
|
217
|
+
* 9. token_program
|
|
218
|
+
* 10. shares_token_program
|
|
219
|
+
* 11. klend_program
|
|
220
|
+
* WithdrawFromInvested (12-20):
|
|
221
|
+
* 12. vault_state (mut, dup)
|
|
222
|
+
* 13. reserve (mut)
|
|
223
|
+
* 14. ctoken_vault (mut)
|
|
224
|
+
* 15. lending_market
|
|
225
|
+
* 16. lending_market_authority
|
|
226
|
+
* 17. reserve_liquidity_supply (mut)
|
|
227
|
+
* 18. reserve_collateral_mint (mut)
|
|
228
|
+
* 19. reserve_collateral_token_program
|
|
229
|
+
* 20. instruction_sysvar_account
|
|
230
|
+
*
|
|
231
|
+
* After the 21 fixed accounts, callers should append reserve accounts for refresh.
|
|
232
|
+
*/
|
|
233
|
+
function buildKvaultWithdrawAccounts(vaultState, strategyStatePda, strategyBaseAta, strategySharesAta, baseMint, reserveConfig, globalConfig = exports.KAMINO_GLOBAL_CONFIG) {
|
|
234
|
+
const [tokenVault] = findKvaultTokenVault(vaultState);
|
|
235
|
+
const [authority] = findKvaultAuthority(vaultState);
|
|
236
|
+
const [sharesMint] = findKvaultSharesMint(vaultState);
|
|
237
|
+
const [eventAuthority] = findKvaultEventAuthority();
|
|
238
|
+
return [
|
|
239
|
+
// WithdrawFromAvailable (0-11)
|
|
240
|
+
{ pubkey: strategyStatePda, isSigner: true, isWritable: true }, // 0
|
|
241
|
+
{ pubkey: vaultState, isSigner: false, isWritable: true }, // 1
|
|
242
|
+
{ pubkey: globalConfig, isSigner: false, isWritable: false }, // 2
|
|
243
|
+
{ pubkey: tokenVault, isSigner: false, isWritable: true }, // 3
|
|
244
|
+
{ pubkey: authority, isSigner: false, isWritable: false }, // 4
|
|
245
|
+
{ pubkey: strategyBaseAta, isSigner: false, isWritable: true }, // 5
|
|
246
|
+
{ pubkey: baseMint, isSigner: false, isWritable: true }, // 6
|
|
247
|
+
{ pubkey: strategySharesAta, isSigner: false, isWritable: true }, // 7
|
|
248
|
+
{ pubkey: sharesMint, isSigner: false, isWritable: true }, // 8
|
|
249
|
+
{ pubkey: SPL_TOKEN_PROGRAM, isSigner: false, isWritable: false }, // 9
|
|
250
|
+
{ pubkey: SPL_TOKEN_PROGRAM, isSigner: false, isWritable: false }, // 10
|
|
251
|
+
{ pubkey: constants_1.KLEND_PROGRAM, isSigner: false, isWritable: false }, // 11
|
|
252
|
+
// WithdrawFromAvailable event CPI (12-13)
|
|
253
|
+
{ pubkey: eventAuthority, isSigner: false, isWritable: false }, // 12
|
|
254
|
+
{ pubkey: constants_1.KAMINO_VAULT_PROGRAM, isSigner: false, isWritable: false }, // 13
|
|
255
|
+
// WithdrawFromInvested (14-22)
|
|
256
|
+
{ pubkey: vaultState, isSigner: false, isWritable: true }, // 14 (dup)
|
|
257
|
+
{ pubkey: reserveConfig.reserve, isSigner: false, isWritable: true }, // 15
|
|
258
|
+
{ pubkey: reserveConfig.ctokenVault, isSigner: false, isWritable: true }, // 16
|
|
259
|
+
{ pubkey: reserveConfig.lendingMarket, isSigner: false, isWritable: false }, // 17
|
|
260
|
+
{ pubkey: reserveConfig.lendingMarketAuthority, isSigner: false, isWritable: false }, // 18
|
|
261
|
+
{ pubkey: reserveConfig.reserveLiquiditySupply, isSigner: false, isWritable: true }, // 19
|
|
262
|
+
{ pubkey: reserveConfig.reserveCollateralMint, isSigner: false, isWritable: true }, // 20
|
|
263
|
+
{ pubkey: reserveConfig.reserveCollateralTokenProgram, isSigner: false, isWritable: false }, // 21
|
|
264
|
+
{ pubkey: SYSVAR_INSTRUCTIONS, isSigner: false, isWritable: false }, // 22
|
|
265
|
+
// Withdraw event CPI (23-24)
|
|
266
|
+
{ pubkey: eventAuthority, isSigner: false, isWritable: false }, // 23
|
|
267
|
+
{ pubkey: constants_1.KAMINO_VAULT_PROGRAM, isSigner: false, isWritable: false }, // 24
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
// Kamino kVault Staleness Check
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
/** Offset of `last_fee_charge_timestamp` (u64) in Kamino VaultState account data */
|
|
274
|
+
const KVAULT_LAST_FEE_CHARGE_TS_OFFSET = 272; // 8 disc + 264
|
|
275
|
+
/** Minimum data length to read `last_fee_charge_timestamp` */
|
|
276
|
+
const KVAULT_MIN_READ_SIZE = KVAULT_LAST_FEE_CHARGE_TS_OFFSET + 8;
|
|
277
|
+
/** Staleness threshold in seconds (matches elemental-lend KVAULT_STALENESS_THRESHOLD) */
|
|
278
|
+
exports.KVAULT_STALENESS_THRESHOLD = 120;
|
|
279
|
+
/**
|
|
280
|
+
* Read `last_fee_charge_timestamp` from raw Kamino VaultState account data.
|
|
281
|
+
* Returns undefined if data is too short.
|
|
282
|
+
*/
|
|
283
|
+
function readKvaultLastFeeChargeTs(data) {
|
|
284
|
+
if (data.length < KVAULT_MIN_READ_SIZE)
|
|
285
|
+
return undefined;
|
|
286
|
+
const buf = Buffer.from(data);
|
|
287
|
+
// Read as u64 LE — timestamp fits in a regular number
|
|
288
|
+
return Number(buf.readBigUInt64LE(KVAULT_LAST_FEE_CHARGE_TS_OFFSET));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Check if a Kamino kVault is stale (needs an `invest` call to refresh).
|
|
292
|
+
*
|
|
293
|
+
* @param vaultStateData - Raw account data for the Kamino VaultState
|
|
294
|
+
* @param currentSlotTimestamp - Current cluster timestamp (seconds)
|
|
295
|
+
* @returns true if the vault is stale (last_fee_charge_timestamp is older than threshold)
|
|
296
|
+
*/
|
|
297
|
+
function isKvaultStale(vaultStateData, currentSlotTimestamp) {
|
|
298
|
+
const ts = readKvaultLastFeeChargeTs(vaultStateData);
|
|
299
|
+
if (ts === undefined)
|
|
300
|
+
return true; // can't read — assume stale
|
|
301
|
+
return (currentSlotTimestamp - ts) > exports.KVAULT_STALENESS_THRESHOLD;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Check if any registered kVault protocol is stale and return the stale vault states.
|
|
305
|
+
*
|
|
306
|
+
* @param connection - Solana connection
|
|
307
|
+
* @param protocols - Protocol pubkeys from the lend strategy state
|
|
308
|
+
* @returns Array of stale kVault state pubkeys
|
|
309
|
+
*/
|
|
310
|
+
async function findStaleKvaults(connection, protocols) {
|
|
311
|
+
if (protocols.length === 0)
|
|
312
|
+
return [];
|
|
313
|
+
const [accountInfos, slot] = await Promise.all([
|
|
314
|
+
connection.getMultipleAccountsInfo(protocols),
|
|
315
|
+
connection.getSlot("confirmed"),
|
|
316
|
+
]);
|
|
317
|
+
// Approximate current timestamp via the slot's block time. If the RPC has
|
|
318
|
+
// no block time available, fall back to wall-clock — close enough for the
|
|
319
|
+
// 120s staleness check.
|
|
320
|
+
const blockTime = await connection.getBlockTime(slot);
|
|
321
|
+
const currentTs = blockTime ?? Math.floor(Date.now() / 1000);
|
|
322
|
+
const stale = [];
|
|
323
|
+
for (let i = 0; i < protocols.length; i++) {
|
|
324
|
+
const info = accountInfos[i];
|
|
325
|
+
if (!info)
|
|
326
|
+
continue;
|
|
327
|
+
// Only check accounts owned by Kamino Vault program
|
|
328
|
+
if (!info.owner.equals(constants_1.KAMINO_VAULT_PROGRAM))
|
|
329
|
+
continue;
|
|
330
|
+
if (isKvaultStale(info.data, currentTs)) {
|
|
331
|
+
stale.push(protocols[i]);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return stale;
|
|
335
|
+
}
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// Kamino Invest Instruction Builder
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
/** Anchor discriminator: sha256("global:invest")[..8] */
|
|
340
|
+
const IX_INVEST = Buffer.from([13, 245, 180, 103, 254, 182, 121, 4]);
|
|
341
|
+
/** PDA seed for ctoken_vault: ["ctoken_vault", vault_state, reserve] */
|
|
342
|
+
const CTOKEN_VAULT_SEED = Buffer.from("ctoken_vault");
|
|
343
|
+
/** PDA seed for whitelisted_reserves: ["whitelisted_reserves", reserve] */
|
|
344
|
+
const WHITELISTED_RESERVES_SEED = Buffer.from("whitelisted_reserves");
|
|
345
|
+
/**
|
|
346
|
+
* Build a Kamino `invest` instruction to refresh a stale kVault.
|
|
347
|
+
*
|
|
348
|
+
* `invest` is permissionless — anyone can call it. It refreshes the vault's
|
|
349
|
+
* `last_fee_charge_timestamp` which is required for elemental-lend's update_aum
|
|
350
|
+
* staleness check (120s threshold).
|
|
351
|
+
*
|
|
352
|
+
* @param payer - Transaction signer (pays for any crank fee, usually 0)
|
|
353
|
+
* @param payerTokenAccount - Payer's base token ATA (for crank fee)
|
|
354
|
+
* @param vaultState - Kamino vault_state pubkey
|
|
355
|
+
* @param baseMint - Base token mint (e.g., USDC)
|
|
356
|
+
* @param vaultStateData - Raw vault_state account data (for reading allocation)
|
|
357
|
+
* @param reserveData - Raw KLend reserve account data (for reading lending_market, etc.)
|
|
358
|
+
* @param allocationIndex - Which vault allocation to invest (default 0)
|
|
359
|
+
*/
|
|
360
|
+
function buildKaminoInvestIx(payer, payerTokenAccount, vaultState, baseMint, vaultStateData, reserveData, allocationIndex = 0) {
|
|
361
|
+
const alloc = readVaultAllocation(vaultStateData, allocationIndex);
|
|
362
|
+
const res = readReserveAccounts(reserveData);
|
|
363
|
+
const [lendingMarketAuthority] = findLendingMarketAuthority(res.lendingMarket);
|
|
364
|
+
const [tokenVault] = findKvaultTokenVault(vaultState);
|
|
365
|
+
const [authority] = findKvaultAuthority(vaultState);
|
|
366
|
+
// ctoken_vault PDA: ["ctoken_vault", vault_state, reserve] under Kamino
|
|
367
|
+
const [ctokenVault] = web3_js_1.PublicKey.findProgramAddressSync([CTOKEN_VAULT_SEED, vaultState.toBuffer(), alloc.reserve.toBuffer()], constants_1.KAMINO_VAULT_PROGRAM);
|
|
368
|
+
// reserve_whitelist_entry PDA: ["whitelisted_reserves", reserve] under Kamino (optional)
|
|
369
|
+
const [reserveWhitelist] = web3_js_1.PublicKey.findProgramAddressSync([WHITELISTED_RESERVES_SEED, alloc.reserve.toBuffer()], constants_1.KAMINO_VAULT_PROGRAM);
|
|
370
|
+
const keys = [
|
|
371
|
+
{ pubkey: payer, isSigner: true, isWritable: true }, // 0: payer
|
|
372
|
+
{ pubkey: payerTokenAccount, isSigner: false, isWritable: true }, // 1: payer_token_account
|
|
373
|
+
{ pubkey: vaultState, isSigner: false, isWritable: true }, // 2: vault_state
|
|
374
|
+
{ pubkey: tokenVault, isSigner: false, isWritable: true }, // 3: token_vault
|
|
375
|
+
{ pubkey: baseMint, isSigner: false, isWritable: true }, // 4: token_mint
|
|
376
|
+
{ pubkey: authority, isSigner: false, isWritable: true }, // 5: base_vault_authority
|
|
377
|
+
{ pubkey: ctokenVault, isSigner: false, isWritable: true }, // 6: ctoken_vault
|
|
378
|
+
{ pubkey: alloc.reserve, isSigner: false, isWritable: true }, // 7: reserve
|
|
379
|
+
{ pubkey: res.lendingMarket, isSigner: false, isWritable: false }, // 8: lending_market
|
|
380
|
+
{ pubkey: lendingMarketAuthority, isSigner: false, isWritable: false }, // 9: lending_market_authority
|
|
381
|
+
{ pubkey: res.reserveLiquiditySupply, isSigner: false, isWritable: true }, // 10: reserve_liquidity_supply
|
|
382
|
+
{ pubkey: res.reserveCollateralMint, isSigner: false, isWritable: true }, // 11: reserve_collateral_mint
|
|
383
|
+
{ pubkey: reserveWhitelist, isSigner: false, isWritable: false }, // 12: reserve_whitelist_entry (optional)
|
|
384
|
+
{ pubkey: constants_1.KLEND_PROGRAM, isSigner: false, isWritable: false }, // 13: klend_program
|
|
385
|
+
{ pubkey: SPL_TOKEN_PROGRAM, isSigner: false, isWritable: false }, // 14: reserve_collateral_token_program
|
|
386
|
+
{ pubkey: SPL_TOKEN_PROGRAM, isSigner: false, isWritable: false }, // 15: token_program
|
|
387
|
+
{ pubkey: SYSVAR_INSTRUCTIONS, isSigner: false, isWritable: false }, // 16: instruction_sysvar_account
|
|
388
|
+
];
|
|
389
|
+
// Append ALL reserves for batch refresh, then lending_market at the end.
|
|
390
|
+
// cpi_refresh_reserves uses .take(reserves_count) to get just reserves.
|
|
391
|
+
// The lending_market must be in the transaction but AFTER reserves.
|
|
392
|
+
const allReserves = [];
|
|
393
|
+
for (let ai = 0; ai < 10; ai++) {
|
|
394
|
+
try {
|
|
395
|
+
const a = readVaultAllocation(vaultStateData, ai);
|
|
396
|
+
if (a.reserve.equals(web3_js_1.PublicKey.default))
|
|
397
|
+
break;
|
|
398
|
+
allReserves.push(a.reserve);
|
|
399
|
+
}
|
|
400
|
+
catch {
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const r of allReserves) {
|
|
405
|
+
keys.push({ pubkey: r, isSigner: false, isWritable: true });
|
|
406
|
+
}
|
|
407
|
+
// Lending market after reserves (for CPI resolution, not counted as a reserve)
|
|
408
|
+
keys.push({ pubkey: res.lendingMarket, isSigner: false, isWritable: false });
|
|
409
|
+
return new web3_js_1.TransactionInstruction({
|
|
410
|
+
programId: constants_1.KAMINO_VAULT_PROGRAM,
|
|
411
|
+
keys,
|
|
412
|
+
data: IX_INVEST,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Build Kamino `invest` instructions for all stale kVaults in a lend strategy.
|
|
417
|
+
*
|
|
418
|
+
* Checks each registered protocol for staleness and returns invest instructions
|
|
419
|
+
* for any that are stale.
|
|
420
|
+
*
|
|
421
|
+
* @param connection - Solana connection
|
|
422
|
+
* @param payer - Transaction signer
|
|
423
|
+
* @param baseMint - Base token mint
|
|
424
|
+
* @param protocols - Registered protocol pubkeys from lend strategy state
|
|
425
|
+
*/
|
|
426
|
+
async function buildKaminoInvestIxsIfStale(connection, payer, baseMint, protocols) {
|
|
427
|
+
const staleVaults = await findStaleKvaults(connection, protocols);
|
|
428
|
+
if (staleVaults.length === 0)
|
|
429
|
+
return [];
|
|
430
|
+
const TOKEN_PROG = new web3_js_1.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
431
|
+
const ATA_PROG = new web3_js_1.PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
|
432
|
+
// Payer's base ATA (for crank fee — usually 0)
|
|
433
|
+
const [payerAta] = web3_js_1.PublicKey.findProgramAddressSync([payer.toBuffer(), TOKEN_PROG.toBuffer(), baseMint.toBuffer()], ATA_PROG);
|
|
434
|
+
// Batch-fetch all vault states in one round-trip, then derive the reserve
|
|
435
|
+
// pubkeys from the returned buffers, then batch-fetch all reserves. Total:
|
|
436
|
+
// 2 RPC calls regardless of how many vaults are stale (previous loop did
|
|
437
|
+
// 2N sequential calls).
|
|
438
|
+
const vaultInfos = await connection.getMultipleAccountsInfo(staleVaults, "confirmed");
|
|
439
|
+
const reserveKeys = vaultInfos.map((info) => {
|
|
440
|
+
if (!info)
|
|
441
|
+
return null;
|
|
442
|
+
try {
|
|
443
|
+
const alloc = readVaultAllocation(info.data, 0);
|
|
444
|
+
return alloc.reserve.equals(web3_js_1.PublicKey.default) ? null : alloc.reserve;
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
// Reserves to fetch: only the non-null ones, but remember which original
|
|
451
|
+
// index each corresponds to so we can line them back up.
|
|
452
|
+
const reserveFetchList = [];
|
|
453
|
+
const reserveIdx = [];
|
|
454
|
+
for (let i = 0; i < reserveKeys.length; i++) {
|
|
455
|
+
const r = reserveKeys[i];
|
|
456
|
+
if (r) {
|
|
457
|
+
reserveIdx.push(i);
|
|
458
|
+
reserveFetchList.push(r);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
const reserveInfos = reserveFetchList.length > 0
|
|
462
|
+
? await connection.getMultipleAccountsInfo(reserveFetchList, "confirmed")
|
|
463
|
+
: [];
|
|
464
|
+
const ixs = [];
|
|
465
|
+
for (let j = 0; j < reserveFetchList.length; j++) {
|
|
466
|
+
const origIdx = reserveIdx[j];
|
|
467
|
+
const vaultInfo = vaultInfos[origIdx];
|
|
468
|
+
const reserveInfo = reserveInfos[j];
|
|
469
|
+
if (!reserveInfo)
|
|
470
|
+
continue;
|
|
471
|
+
// Validate reserve is owned by KLend (skip if migrated/closed)
|
|
472
|
+
if (!reserveInfo.owner.equals(constants_1.KLEND_PROGRAM))
|
|
473
|
+
continue;
|
|
474
|
+
try {
|
|
475
|
+
ixs.push(buildKaminoInvestIx(payer, payerAta, staleVaults[origIdx], baseMint, vaultInfo.data, reserveInfo.data, 0));
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
// Skip if ix builder can't parse the data
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
return ixs;
|
|
483
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
export declare function findStrategyStatePda(baseMint: PublicKey, programId?: PublicKey): [PublicKey, number];
|
|
3
|
+
export declare function findStvPositionPda(stv: PublicKey, programId?: PublicKey): [PublicKey, number];
|
|
4
|
+
/**
|
|
5
|
+
* Derive the per-strategy_state `ManagerRole` PDA for Elemental Lend.
|
|
6
|
+
* Seeds: `["manager", strategy_state, manager]`.
|
|
7
|
+
*
|
|
8
|
+
* Note the per-program prefix on the name: each Elemental program has its
|
|
9
|
+
* own `ManagerRole` PDA scoped to a different anchor account (`p-stv-core`
|
|
10
|
+
* → STV, `jlpd-strategy` → JlpdConfig).
|
|
11
|
+
*/
|
|
12
|
+
export declare function findLendManagerRolePda(strategyState: PublicKey, manager: PublicKey, programId?: PublicKey): [PublicKey, number];
|