@elmntl/jlpd-sdk 0.1.0
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/README.md +363 -0
- package/dist/index.d.mts +743 -0
- package/dist/index.d.ts +743 -0
- package/dist/index.js +1843 -0
- package/dist/index.mjs +1753 -0
- package/package.json +30 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1753 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import {
|
|
3
|
+
PublicKey as PublicKey12,
|
|
4
|
+
TransactionMessage,
|
|
5
|
+
VersionedTransaction,
|
|
6
|
+
ComputeBudgetProgram,
|
|
7
|
+
SystemProgram as SystemProgram2,
|
|
8
|
+
SYSVAR_RENT_PUBKEY
|
|
9
|
+
} from "@solana/web3.js";
|
|
10
|
+
import { BN as BN5 } from "@coral-xyz/anchor";
|
|
11
|
+
import {
|
|
12
|
+
TOKEN_PROGRAM_ID as TOKEN_PROGRAM_ID2,
|
|
13
|
+
ASSOCIATED_TOKEN_PROGRAM_ID as ASSOCIATED_TOKEN_PROGRAM_ID2,
|
|
14
|
+
getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2
|
|
15
|
+
} from "@solana/spl-token";
|
|
16
|
+
|
|
17
|
+
// src/constants.ts
|
|
18
|
+
import { PublicKey } from "@solana/web3.js";
|
|
19
|
+
import { NATIVE_MINT } from "@solana/spl-token";
|
|
20
|
+
var JLPD_PROGRAM_ID = new PublicKey(
|
|
21
|
+
"ELEM2bAobpZNuysfhyF28XGVjA1aTUwm6rPYyv1JX3oq"
|
|
22
|
+
);
|
|
23
|
+
var JUPITER_LEND_PROGRAM_ID = new PublicKey(
|
|
24
|
+
"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"
|
|
25
|
+
);
|
|
26
|
+
var JUPITER_SWAP_PROGRAM_ID = new PublicKey(
|
|
27
|
+
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"
|
|
28
|
+
);
|
|
29
|
+
var SEED_JLP_VAULT = "jlp_vault";
|
|
30
|
+
var SEED_STV = "stv";
|
|
31
|
+
var FLAG_PAUSED = 1;
|
|
32
|
+
var FLAG_DEPOSITS_DISABLED = 2;
|
|
33
|
+
var FLAG_WITHDRAWALS_DISABLED = 4;
|
|
34
|
+
var FLAG_JLP_DISABLED = 8;
|
|
35
|
+
var FLAG_REBALANCE_DISABLED = 16;
|
|
36
|
+
var PPS_DECIMALS = 1e9;
|
|
37
|
+
var EXCHANGE_RATE_PRECISION = 1e12;
|
|
38
|
+
var MINTS = {
|
|
39
|
+
JLP: new PublicKey("27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4"),
|
|
40
|
+
WSOL: NATIVE_MINT,
|
|
41
|
+
USDC: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
|
|
42
|
+
WBTC: new PublicKey("3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh"),
|
|
43
|
+
WETH: new PublicKey("7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"),
|
|
44
|
+
JupUSD: new PublicKey("JuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD")
|
|
45
|
+
};
|
|
46
|
+
var STV_INDEX = /* @__PURE__ */ ((STV_INDEX2) => {
|
|
47
|
+
STV_INDEX2[STV_INDEX2["BTC"] = 0] = "BTC";
|
|
48
|
+
STV_INDEX2[STV_INDEX2["ETH"] = 1] = "ETH";
|
|
49
|
+
STV_INDEX2[STV_INDEX2["SOL"] = 2] = "SOL";
|
|
50
|
+
STV_INDEX2[STV_INDEX2["USDC"] = 3] = "USDC";
|
|
51
|
+
STV_INDEX2[STV_INDEX2["JupUSD"] = 4] = "JupUSD";
|
|
52
|
+
return STV_INDEX2;
|
|
53
|
+
})(STV_INDEX || {});
|
|
54
|
+
var ORACLES = {
|
|
55
|
+
// Doves oracles (Jupiter Perps oracle system)
|
|
56
|
+
DOVES_BTC_USD: new PublicKey("4HBbPx9QJdjJ7GUe6bsiJjGybvfpDhQMMPXP1UEa7VT5"),
|
|
57
|
+
DOVES_ETH_USD: new PublicKey("5URYohbPy32nxK1t3jAHVNfdWY2xTubHiFvLrE3VhXEp"),
|
|
58
|
+
DOVES_SOL_USD: new PublicKey("39cWjvHrpHNz2SbXv6ME4NPhqBDBd4KsjUYv5JkHEAJU"),
|
|
59
|
+
// Pyth oracle for JLP price
|
|
60
|
+
PYTH_JLP_USD: new PublicKey("2TTGSRSezqFzeLUH8JwRUbtN66XLLaymfYsWRTMjfiMw")
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/accounts/pdas.ts
|
|
64
|
+
import { PublicKey as PublicKey2 } from "@solana/web3.js";
|
|
65
|
+
var SEED_JLP_VAULT_BUFFER = Buffer.from(SEED_JLP_VAULT);
|
|
66
|
+
var SEED_STV_BUFFER = Buffer.from(SEED_STV);
|
|
67
|
+
var pdaCache = /* @__PURE__ */ new Map();
|
|
68
|
+
function deriveVaultPda(programId) {
|
|
69
|
+
const key = `vault:${programId.toBase58()}`;
|
|
70
|
+
let cached = pdaCache.get(key);
|
|
71
|
+
if (!cached) {
|
|
72
|
+
cached = PublicKey2.findProgramAddressSync(
|
|
73
|
+
[SEED_JLP_VAULT_BUFFER],
|
|
74
|
+
programId
|
|
75
|
+
);
|
|
76
|
+
pdaCache.set(key, cached);
|
|
77
|
+
}
|
|
78
|
+
return cached;
|
|
79
|
+
}
|
|
80
|
+
function deriveStvPda(baseMint, programId) {
|
|
81
|
+
const key = `stv:${baseMint.toBase58()}:${programId.toBase58()}`;
|
|
82
|
+
let cached = pdaCache.get(key);
|
|
83
|
+
if (!cached) {
|
|
84
|
+
cached = PublicKey2.findProgramAddressSync(
|
|
85
|
+
[SEED_STV_BUFFER, baseMint.toBuffer()],
|
|
86
|
+
programId
|
|
87
|
+
);
|
|
88
|
+
pdaCache.set(key, cached);
|
|
89
|
+
}
|
|
90
|
+
return cached;
|
|
91
|
+
}
|
|
92
|
+
function clearPdaCache() {
|
|
93
|
+
pdaCache.clear();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/accounts/vault.ts
|
|
97
|
+
import { PublicKey as PublicKey3 } from "@solana/web3.js";
|
|
98
|
+
import { BN } from "@coral-xyz/anchor";
|
|
99
|
+
var JLP_VAULT_DATA_SIZE = 464;
|
|
100
|
+
var JLP_VAULT_ACCOUNT_SIZE = 8 + JLP_VAULT_DATA_SIZE;
|
|
101
|
+
var JLP_VAULT_DISCRIMINATOR = Buffer.from([
|
|
102
|
+
41,
|
|
103
|
+
108,
|
|
104
|
+
189,
|
|
105
|
+
168,
|
|
106
|
+
199,
|
|
107
|
+
152,
|
|
108
|
+
221,
|
|
109
|
+
119
|
|
110
|
+
]);
|
|
111
|
+
function parseVault(data) {
|
|
112
|
+
if (data.length < JLP_VAULT_ACCOUNT_SIZE) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Invalid JLPVault data size: expected ${JLP_VAULT_ACCOUNT_SIZE}, got ${data.length}`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const discriminator = data.subarray(0, 8);
|
|
118
|
+
if (!discriminator.equals(JLP_VAULT_DISCRIMINATOR)) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Invalid JLPVault discriminator: expected ${JLP_VAULT_DISCRIMINATOR.toString("hex")}, got ${discriminator.toString("hex")}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
let offset = 8;
|
|
124
|
+
const admin = new PublicKey3(data.subarray(offset, offset + 32));
|
|
125
|
+
offset += 32;
|
|
126
|
+
const manager = new PublicKey3(data.subarray(offset, offset + 32));
|
|
127
|
+
offset += 32;
|
|
128
|
+
const feeReceiver = new PublicKey3(data.subarray(offset, offset + 32));
|
|
129
|
+
offset += 32;
|
|
130
|
+
const jlpMint = new PublicKey3(data.subarray(offset, offset + 32));
|
|
131
|
+
offset += 32;
|
|
132
|
+
const baseAssetMints = [];
|
|
133
|
+
for (let i = 0; i < 5; i++) {
|
|
134
|
+
baseAssetMints.push(new PublicKey3(data.subarray(offset, offset + 32)));
|
|
135
|
+
offset += 32;
|
|
136
|
+
}
|
|
137
|
+
const jlxAssetMints = [];
|
|
138
|
+
for (let i = 0; i < 5; i++) {
|
|
139
|
+
jlxAssetMints.push(new PublicKey3(data.subarray(offset, offset + 32)));
|
|
140
|
+
offset += 32;
|
|
141
|
+
}
|
|
142
|
+
const oracleStalenessThreshold = new BN(data.subarray(offset, offset + 8), "le");
|
|
143
|
+
offset += 8;
|
|
144
|
+
const flags = data.readUInt16LE(offset);
|
|
145
|
+
offset += 2;
|
|
146
|
+
const jlpSlippageBps = data.readUInt16LE(offset);
|
|
147
|
+
offset += 2;
|
|
148
|
+
const version = data.readUInt8(offset);
|
|
149
|
+
offset += 1;
|
|
150
|
+
const bump = data.readUInt8(offset);
|
|
151
|
+
offset += 1;
|
|
152
|
+
return {
|
|
153
|
+
admin,
|
|
154
|
+
manager,
|
|
155
|
+
feeReceiver,
|
|
156
|
+
jlpMint,
|
|
157
|
+
baseAssetMints,
|
|
158
|
+
jlxAssetMints,
|
|
159
|
+
oracleStalenessThreshold,
|
|
160
|
+
flags,
|
|
161
|
+
jlpSlippageBps,
|
|
162
|
+
version,
|
|
163
|
+
bump
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/accounts/stv.ts
|
|
168
|
+
import { PublicKey as PublicKey4 } from "@solana/web3.js";
|
|
169
|
+
import { BN as BN2 } from "@coral-xyz/anchor";
|
|
170
|
+
var STV_DATA_SIZE = 160;
|
|
171
|
+
var STV_ACCOUNT_SIZE = 8 + STV_DATA_SIZE;
|
|
172
|
+
var STV_DISCRIMINATOR = Buffer.from([
|
|
173
|
+
214,
|
|
174
|
+
2,
|
|
175
|
+
96,
|
|
176
|
+
41,
|
|
177
|
+
85,
|
|
178
|
+
163,
|
|
179
|
+
17,
|
|
180
|
+
230
|
|
181
|
+
]);
|
|
182
|
+
function parseStv(data) {
|
|
183
|
+
if (data.length < STV_ACCOUNT_SIZE) {
|
|
184
|
+
throw new Error(
|
|
185
|
+
`Invalid STV data size: expected ${STV_ACCOUNT_SIZE}, got ${data.length}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
const discriminator = data.subarray(0, 8);
|
|
189
|
+
if (!discriminator.equals(STV_DISCRIMINATOR)) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Invalid STV discriminator: expected ${STV_DISCRIMINATOR.toString("hex")}, got ${discriminator.toString("hex")}`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
let offset = 8;
|
|
195
|
+
const baseMint = new PublicKey4(data.subarray(offset, offset + 32));
|
|
196
|
+
offset += 32;
|
|
197
|
+
const jlMint = new PublicKey4(data.subarray(offset, offset + 32));
|
|
198
|
+
offset += 32;
|
|
199
|
+
const jvMint = new PublicKey4(data.subarray(offset, offset + 32));
|
|
200
|
+
offset += 32;
|
|
201
|
+
const pps = new BN2(data.subarray(offset, offset + 8), "le");
|
|
202
|
+
offset += 8;
|
|
203
|
+
const hwm = new BN2(data.subarray(offset, offset + 8), "le");
|
|
204
|
+
offset += 8;
|
|
205
|
+
const lastMgmtFeeTs = new BN2(data.subarray(offset, offset + 8), "le");
|
|
206
|
+
offset += 8;
|
|
207
|
+
const baseLoaned = new BN2(data.subarray(offset, offset + 8), "le");
|
|
208
|
+
offset += 8;
|
|
209
|
+
const accruedFeesJlx = new BN2(data.subarray(offset, offset + 8), "le");
|
|
210
|
+
offset += 8;
|
|
211
|
+
const maxDeposit = new BN2(data.subarray(offset, offset + 8), "le");
|
|
212
|
+
offset += 8;
|
|
213
|
+
const minDeposit = new BN2(data.subarray(offset, offset + 8), "le");
|
|
214
|
+
offset += 8;
|
|
215
|
+
const mgmtFeeBps = data.readUInt16LE(offset);
|
|
216
|
+
offset += 2;
|
|
217
|
+
const perfFeeBps = data.readUInt16LE(offset);
|
|
218
|
+
offset += 2;
|
|
219
|
+
const flags = data.readUInt16LE(offset);
|
|
220
|
+
offset += 2;
|
|
221
|
+
const version = data.readUInt8(offset);
|
|
222
|
+
offset += 1;
|
|
223
|
+
const bump = data.readUInt8(offset);
|
|
224
|
+
offset += 1;
|
|
225
|
+
return {
|
|
226
|
+
baseMint,
|
|
227
|
+
jlMint,
|
|
228
|
+
jvMint,
|
|
229
|
+
pps,
|
|
230
|
+
hwm,
|
|
231
|
+
lastMgmtFeeTs,
|
|
232
|
+
baseLoaned,
|
|
233
|
+
accruedFeesJlx,
|
|
234
|
+
maxDeposit,
|
|
235
|
+
minDeposit,
|
|
236
|
+
mgmtFeeBps,
|
|
237
|
+
perfFeeBps,
|
|
238
|
+
flags,
|
|
239
|
+
version,
|
|
240
|
+
bump
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/utils/ata.ts
|
|
245
|
+
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
|
|
246
|
+
var ataCache = /* @__PURE__ */ new Map();
|
|
247
|
+
function deriveAta(mint, owner, allowPda = false) {
|
|
248
|
+
const key = `${mint.toBase58()}:${owner.toBase58()}:${allowPda}`;
|
|
249
|
+
let cached = ataCache.get(key);
|
|
250
|
+
if (!cached) {
|
|
251
|
+
cached = getAssociatedTokenAddressSync(mint, owner, allowPda);
|
|
252
|
+
ataCache.set(key, cached);
|
|
253
|
+
}
|
|
254
|
+
return cached;
|
|
255
|
+
}
|
|
256
|
+
function deriveUserAtas(baseMint, jvMint, userWallet) {
|
|
257
|
+
return {
|
|
258
|
+
baseAta: deriveAta(baseMint, userWallet, false),
|
|
259
|
+
jvxAta: deriveAta(jvMint, userWallet, false)
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function deriveStvAtas(baseMint, jlxMint, stvPda) {
|
|
263
|
+
return {
|
|
264
|
+
baseAta: deriveAta(baseMint, stvPda, true),
|
|
265
|
+
jlxAta: deriveAta(jlxMint, stvPda, true)
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function deriveVaultAtas(jlxMint, jlpMint, vaultPda) {
|
|
269
|
+
return {
|
|
270
|
+
stagingAta: deriveAta(jlxMint, vaultPda, true),
|
|
271
|
+
jlpAta: deriveAta(jlpMint, vaultPda, true)
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function clearAtaCache() {
|
|
275
|
+
ataCache.clear();
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/utils/math.ts
|
|
279
|
+
import { BN as BN3 } from "@coral-xyz/anchor";
|
|
280
|
+
var PPS_DECIMALS_BN = new BN3(PPS_DECIMALS);
|
|
281
|
+
var EXCHANGE_RATE_PRECISION_BN = new BN3(EXCHANGE_RATE_PRECISION);
|
|
282
|
+
var EXCHANGE_RATE_PRECISION_BI = BigInt(EXCHANGE_RATE_PRECISION);
|
|
283
|
+
var RETURN_PERCENT_PRECISION = EXCHANGE_RATE_PRECISION_BI * 100n;
|
|
284
|
+
var SECONDS_PER_YEAR = BigInt(365 * 24 * 60 * 60);
|
|
285
|
+
var MAX_REWARDS_RATE = 50n * EXCHANGE_RATE_PRECISION_BI;
|
|
286
|
+
function calculateNav(stvBaseBalance, stvJlxBalance, exchangeRate, baseLoaned) {
|
|
287
|
+
const jlxValue = stvJlxBalance.mul(new BN3(exchangeRate.toString())).div(EXCHANGE_RATE_PRECISION_BN);
|
|
288
|
+
return stvBaseBalance.add(jlxValue).add(baseLoaned);
|
|
289
|
+
}
|
|
290
|
+
function calculatePps(nav, jvSupply) {
|
|
291
|
+
if (jvSupply.isZero()) {
|
|
292
|
+
return new BN3(PPS_DECIMALS);
|
|
293
|
+
}
|
|
294
|
+
return nav.mul(PPS_DECIMALS_BN).div(jvSupply);
|
|
295
|
+
}
|
|
296
|
+
function sharesToBase(shares, pps) {
|
|
297
|
+
if (pps.isZero()) {
|
|
298
|
+
throw new Error("Price per share cannot be zero");
|
|
299
|
+
}
|
|
300
|
+
return shares.mul(pps).div(PPS_DECIMALS_BN);
|
|
301
|
+
}
|
|
302
|
+
function baseToShares(base, pps) {
|
|
303
|
+
if (pps.isZero()) {
|
|
304
|
+
throw new Error("Price per share cannot be zero");
|
|
305
|
+
}
|
|
306
|
+
return base.mul(PPS_DECIMALS_BN).div(pps);
|
|
307
|
+
}
|
|
308
|
+
function jlxToBase(jlxAmount, exchangeRate) {
|
|
309
|
+
if (exchangeRate.isZero()) {
|
|
310
|
+
throw new Error("Exchange rate cannot be zero");
|
|
311
|
+
}
|
|
312
|
+
return jlxAmount.mul(exchangeRate).div(EXCHANGE_RATE_PRECISION_BN);
|
|
313
|
+
}
|
|
314
|
+
function baseToJlx(baseAmount, exchangeRate) {
|
|
315
|
+
if (exchangeRate.isZero()) {
|
|
316
|
+
throw new Error("Exchange rate cannot be zero");
|
|
317
|
+
}
|
|
318
|
+
return baseAmount.mul(EXCHANGE_RATE_PRECISION_BN).div(exchangeRate);
|
|
319
|
+
}
|
|
320
|
+
function calculateExchangeRate(storedRate, rewardsRate, currentTimestamp, lastUpdateTimestamp, fTokenTotalSupply) {
|
|
321
|
+
if (fTokenTotalSupply === 0n) {
|
|
322
|
+
return storedRate;
|
|
323
|
+
}
|
|
324
|
+
const totalAssets = storedRate * fTokenTotalSupply / EXCHANGE_RATE_PRECISION_BI;
|
|
325
|
+
if (totalAssets === 0n || rewardsRate === 0n) {
|
|
326
|
+
return storedRate;
|
|
327
|
+
}
|
|
328
|
+
const timeDiff = currentTimestamp - lastUpdateTimestamp;
|
|
329
|
+
if (timeDiff <= 0n) {
|
|
330
|
+
return storedRate;
|
|
331
|
+
}
|
|
332
|
+
const rate = rewardsRate * RETURN_PERCENT_PRECISION / totalAssets;
|
|
333
|
+
const cappedRate = rate > MAX_REWARDS_RATE ? MAX_REWARDS_RATE : rate;
|
|
334
|
+
const priceIncrease = storedRate * cappedRate * timeDiff / RETURN_PERCENT_PRECISION / SECONDS_PER_YEAR;
|
|
335
|
+
return storedRate + priceIncrease;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/jupiter/pools.ts
|
|
339
|
+
import { PublicKey as PublicKey5 } from "@solana/web3.js";
|
|
340
|
+
import { NATIVE_MINT as NATIVE_MINT2 } from "@solana/spl-token";
|
|
341
|
+
var LENDING_ADMIN = new PublicKey5("5nmGjA4s7ATzpBQXC5RNceRpaJ7pYw2wKsNBWyuSAZV6");
|
|
342
|
+
var LIQUIDITY_PROGRAM_ID = new PublicKey5("jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC");
|
|
343
|
+
var LIQUIDITY_SINGLETON = new PublicKey5("7s1da8DduuBFqGra5bJBjpnvL5E9mGzCuMk1Qkh4or2Z");
|
|
344
|
+
var WSOL_POOL = {
|
|
345
|
+
mint: NATIVE_MINT2,
|
|
346
|
+
name: "SOL",
|
|
347
|
+
lending: new PublicKey5("BeAqbxfrcXmzEYT2Ra62oW2MqkuFDHaCtps47Mzg6Zj3"),
|
|
348
|
+
fTokenMint: new PublicKey5("2uQsyo1fXXQkDtcpXnLofWy88PxcvnfH2L8FPSE62FVU"),
|
|
349
|
+
tokenReservesLiquidity: new PublicKey5("4Y66HtUEqbbbpZdENGtFdVhUMS3tnagffn3M4do59Nfy"),
|
|
350
|
+
supplyPositionOnLiquidity: new PublicKey5("4SkEYxmiRgQ4VYyvh9VB4k39M49BpqazyzDUFDzJhXQm"),
|
|
351
|
+
rewardsRateModel: new PublicKey5("CkeQGDRsgMZcCaU8cZEdC2aFAohia4jLzL36RaLcUDsR"),
|
|
352
|
+
vault: new PublicKey5("BWeWnEbVmYKLmUpcrAXGrvDYxVA7VBS6TEnNc74jTerw"),
|
|
353
|
+
rateModel: new PublicKey5("Acvyi9HBGmqh3Exe1N4PjBVyY8fokq2AdC6fSLqV6KSo"),
|
|
354
|
+
claimAccount: new PublicKey5("6AQGR8zK4KTVZfZ9UZaRzyEL5ynvwVaF5ywVdmtJT24N"),
|
|
355
|
+
decimals: 9,
|
|
356
|
+
stvIndex: 2 /* SOL */,
|
|
357
|
+
hasJupiterLend: true
|
|
358
|
+
};
|
|
359
|
+
var USDC_POOL = {
|
|
360
|
+
mint: MINTS.USDC,
|
|
361
|
+
name: "USDC",
|
|
362
|
+
lending: new PublicKey5("2vVYHYM8VYnvZqQWpTJSj8o8DBf1wM8pVs3bsTgYZiqJ"),
|
|
363
|
+
fTokenMint: new PublicKey5("9BEcn9aPEmhSPbPQeFGjidRiEKki46fVQDyPpSQXPA2D"),
|
|
364
|
+
tokenReservesLiquidity: new PublicKey5("94vK29npVbyRHXH63rRcTiSr26SFhrQTzbpNJuhQEDu"),
|
|
365
|
+
supplyPositionOnLiquidity: new PublicKey5("Hf9gtkM4dpVBahVSzEXSVCAPpKzBsBcns3s8As3z77oF"),
|
|
366
|
+
rewardsRateModel: new PublicKey5("5xSPBiD3TibamAnwHDhZABdB4z4F9dcj5PnbteroBTTd"),
|
|
367
|
+
vault: new PublicKey5("BmkUoKMFYBxNSzWXyUjyMJjMAaVz4d8ZnxwwmhDCUXFB"),
|
|
368
|
+
rateModel: new PublicKey5("5pjzT5dFTsXcwixoab1QDLvZQvpYJxJeBphkyfHGn688"),
|
|
369
|
+
claimAccount: new PublicKey5("HN1r4VfkDn53xQQfeGDYrNuDKFdemAhZsHYRwBrFhsW"),
|
|
370
|
+
decimals: 6,
|
|
371
|
+
stvIndex: 3 /* USDC */,
|
|
372
|
+
hasJupiterLend: true
|
|
373
|
+
};
|
|
374
|
+
var JUPUSD_POOL = {
|
|
375
|
+
mint: MINTS.JupUSD,
|
|
376
|
+
name: "JupUSD",
|
|
377
|
+
lending: new PublicKey5("papYEgeG5uPE4niUWZhihUUzVVotJn1mAWbYo2UBSHi"),
|
|
378
|
+
fTokenMint: new PublicKey5("7GxATsNMnaC88vdwd2t3mwrFuQwwGvmYPrUQ4D6FotXk"),
|
|
379
|
+
tokenReservesLiquidity: new PublicKey5("2tQE8jVR5ezDw3PDa21BNzfyQ14Ug5cTf6n3swJNjkod"),
|
|
380
|
+
supplyPositionOnLiquidity: new PublicKey5("DXFoJruECdEch2KpzLQ2cSpxoBSsyg4bpYPnHYofsbD4"),
|
|
381
|
+
rewardsRateModel: new PublicKey5("E3U32h49TL9Qof3NeLja9qJxTrGYpY1o1NQPtrSLJjcc"),
|
|
382
|
+
vault: new PublicKey5("9kGqd5zsQGaFfFPdUuEgbRM4V7x72Jdt7WTS4uRouAQ7"),
|
|
383
|
+
rateModel: new PublicKey5("2hT44GA9r5PiqsbbmqN5CuF7ymtquoEdokRncAs9CVej"),
|
|
384
|
+
claimAccount: new PublicKey5("6q9vTzAsTMEPUCwuhEdJSJdpRNXnubwKZbi2go1B8nvg"),
|
|
385
|
+
decimals: 6,
|
|
386
|
+
stvIndex: 4 /* JupUSD */,
|
|
387
|
+
hasJupiterLend: true
|
|
388
|
+
};
|
|
389
|
+
var WBTC_POOL = {
|
|
390
|
+
mint: MINTS.WBTC,
|
|
391
|
+
name: "BTC",
|
|
392
|
+
lending: PublicKey5.default,
|
|
393
|
+
fTokenMint: MINTS.WBTC,
|
|
394
|
+
// jlBTC = WBTC (base-only)
|
|
395
|
+
tokenReservesLiquidity: PublicKey5.default,
|
|
396
|
+
supplyPositionOnLiquidity: PublicKey5.default,
|
|
397
|
+
rewardsRateModel: PublicKey5.default,
|
|
398
|
+
vault: PublicKey5.default,
|
|
399
|
+
rateModel: PublicKey5.default,
|
|
400
|
+
claimAccount: PublicKey5.default,
|
|
401
|
+
decimals: 8,
|
|
402
|
+
stvIndex: 0 /* BTC */,
|
|
403
|
+
hasJupiterLend: false
|
|
404
|
+
};
|
|
405
|
+
var WETH_POOL = {
|
|
406
|
+
mint: MINTS.WETH,
|
|
407
|
+
name: "ETH",
|
|
408
|
+
lending: PublicKey5.default,
|
|
409
|
+
fTokenMint: MINTS.WETH,
|
|
410
|
+
// jlETH = WETH (base-only)
|
|
411
|
+
tokenReservesLiquidity: PublicKey5.default,
|
|
412
|
+
supplyPositionOnLiquidity: PublicKey5.default,
|
|
413
|
+
rewardsRateModel: PublicKey5.default,
|
|
414
|
+
vault: PublicKey5.default,
|
|
415
|
+
rateModel: PublicKey5.default,
|
|
416
|
+
claimAccount: PublicKey5.default,
|
|
417
|
+
decimals: 8,
|
|
418
|
+
stvIndex: 1 /* ETH */,
|
|
419
|
+
hasJupiterLend: false
|
|
420
|
+
};
|
|
421
|
+
var POOLS = {
|
|
422
|
+
SOL: WSOL_POOL,
|
|
423
|
+
USDC: USDC_POOL,
|
|
424
|
+
JupUSD: JUPUSD_POOL,
|
|
425
|
+
BTC: WBTC_POOL,
|
|
426
|
+
ETH: WETH_POOL
|
|
427
|
+
};
|
|
428
|
+
var POOLS_BY_MINT = new Map(
|
|
429
|
+
Object.values(POOLS).map((p) => [p.mint.toBase58(), p])
|
|
430
|
+
);
|
|
431
|
+
function getPoolByName(name) {
|
|
432
|
+
return POOLS[name];
|
|
433
|
+
}
|
|
434
|
+
function getPoolByMint(mint) {
|
|
435
|
+
return POOLS_BY_MINT.get(mint.toBase58());
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/jupiter/lend.ts
|
|
439
|
+
import { PublicKey as PublicKey6, SystemProgram } from "@solana/web3.js";
|
|
440
|
+
import { TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
|
441
|
+
var JUPITER_LEND_PROGRAM_ID2 = new PublicKey6(
|
|
442
|
+
"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"
|
|
443
|
+
);
|
|
444
|
+
function buildExchangeRateAccounts(pool) {
|
|
445
|
+
if (!pool.hasJupiterLend) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
lending: pool.lending,
|
|
450
|
+
rewardsRateModel: pool.rewardsRateModel
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function buildJupEarnDepositAccounts(pool, stvPda, stvBaseAta, stvJlxAta) {
|
|
454
|
+
if (!pool.hasJupiterLend) {
|
|
455
|
+
throw new Error(`Pool ${pool.name} does not support Jupiter Lend`);
|
|
456
|
+
}
|
|
457
|
+
return [
|
|
458
|
+
{ pubkey: stvPda, isSigner: false, isWritable: true },
|
|
459
|
+
{ pubkey: stvBaseAta, isSigner: false, isWritable: true },
|
|
460
|
+
{ pubkey: stvJlxAta, isSigner: false, isWritable: true },
|
|
461
|
+
{ pubkey: pool.mint, isSigner: false, isWritable: false },
|
|
462
|
+
{ pubkey: LENDING_ADMIN, isSigner: false, isWritable: false },
|
|
463
|
+
{ pubkey: pool.lending, isSigner: false, isWritable: true },
|
|
464
|
+
{ pubkey: pool.fTokenMint, isSigner: false, isWritable: true },
|
|
465
|
+
{ pubkey: pool.tokenReservesLiquidity, isSigner: false, isWritable: true },
|
|
466
|
+
{ pubkey: pool.supplyPositionOnLiquidity, isSigner: false, isWritable: true },
|
|
467
|
+
{ pubkey: pool.rateModel, isSigner: false, isWritable: false },
|
|
468
|
+
{ pubkey: pool.vault, isSigner: false, isWritable: true },
|
|
469
|
+
{ pubkey: LIQUIDITY_SINGLETON, isSigner: false, isWritable: true },
|
|
470
|
+
{ pubkey: LIQUIDITY_PROGRAM_ID, isSigner: false, isWritable: true },
|
|
471
|
+
{ pubkey: pool.rewardsRateModel, isSigner: false, isWritable: false },
|
|
472
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
473
|
+
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
474
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
475
|
+
{ pubkey: JUPITER_LEND_PROGRAM_ID2, isSigner: false, isWritable: false }
|
|
476
|
+
];
|
|
477
|
+
}
|
|
478
|
+
function buildJupEarnWithdrawAccounts(pool, stvPda, stvJlxAta, stvBaseAta) {
|
|
479
|
+
if (!pool.hasJupiterLend) {
|
|
480
|
+
throw new Error(`Pool ${pool.name} does not support Jupiter Lend`);
|
|
481
|
+
}
|
|
482
|
+
return [
|
|
483
|
+
{ pubkey: stvPda, isSigner: false, isWritable: true },
|
|
484
|
+
{ pubkey: stvJlxAta, isSigner: false, isWritable: true },
|
|
485
|
+
{ pubkey: stvBaseAta, isSigner: false, isWritable: true },
|
|
486
|
+
{ pubkey: LENDING_ADMIN, isSigner: false, isWritable: false },
|
|
487
|
+
{ pubkey: pool.lending, isSigner: false, isWritable: true },
|
|
488
|
+
{ pubkey: pool.mint, isSigner: false, isWritable: false },
|
|
489
|
+
{ pubkey: pool.fTokenMint, isSigner: false, isWritable: true },
|
|
490
|
+
{ pubkey: pool.tokenReservesLiquidity, isSigner: false, isWritable: true },
|
|
491
|
+
{ pubkey: pool.supplyPositionOnLiquidity, isSigner: false, isWritable: true },
|
|
492
|
+
{ pubkey: pool.rateModel, isSigner: false, isWritable: false },
|
|
493
|
+
{ pubkey: pool.vault, isSigner: false, isWritable: true },
|
|
494
|
+
{ pubkey: pool.claimAccount, isSigner: false, isWritable: true },
|
|
495
|
+
{ pubkey: LIQUIDITY_SINGLETON, isSigner: false, isWritable: true },
|
|
496
|
+
{ pubkey: LIQUIDITY_PROGRAM_ID, isSigner: false, isWritable: true },
|
|
497
|
+
{ pubkey: pool.rewardsRateModel, isSigner: false, isWritable: false },
|
|
498
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
499
|
+
{ pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
500
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
501
|
+
{ pubkey: JUPITER_LEND_PROGRAM_ID2, isSigner: false, isWritable: false }
|
|
502
|
+
];
|
|
503
|
+
}
|
|
504
|
+
function parseLendingAccount(data) {
|
|
505
|
+
return {
|
|
506
|
+
mint: new PublicKey6(data.slice(8, 40)),
|
|
507
|
+
fTokenMint: new PublicKey6(data.slice(40, 72)),
|
|
508
|
+
lendingId: data.readUInt16LE(72),
|
|
509
|
+
decimals: data[74],
|
|
510
|
+
rewardsRateModel: new PublicKey6(data.slice(75, 107)),
|
|
511
|
+
liquidityExchangePrice: data.readBigUInt64LE(107),
|
|
512
|
+
tokenExchangePrice: data.readBigUInt64LE(115),
|
|
513
|
+
lastUpdateTimestamp: data.readBigUInt64LE(123),
|
|
514
|
+
tokenReservesLiquidity: new PublicKey6(data.slice(131, 163)),
|
|
515
|
+
supplyPositionOnLiquidity: new PublicKey6(data.slice(163, 195)),
|
|
516
|
+
bump: data[195]
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function parseRewardsRateModel(data) {
|
|
520
|
+
return {
|
|
521
|
+
mint: new PublicKey6(data.slice(8, 40)),
|
|
522
|
+
startTvl: data.readBigUInt64LE(40),
|
|
523
|
+
duration: data.readBigUInt64LE(48),
|
|
524
|
+
startTime: data.readBigUInt64LE(56),
|
|
525
|
+
yearlyReward: data.readBigUInt64LE(64),
|
|
526
|
+
nextDuration: data.readBigUInt64LE(72),
|
|
527
|
+
nextRewardAmount: data.readBigUInt64LE(80),
|
|
528
|
+
bump: data[88]
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/jupiter/swap.ts
|
|
533
|
+
import {
|
|
534
|
+
PublicKey as PublicKey7,
|
|
535
|
+
AddressLookupTableAccount
|
|
536
|
+
} from "@solana/web3.js";
|
|
537
|
+
var JUPITER_API_BASE = "https://lite-api.jup.ag/swap/v1";
|
|
538
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
539
|
+
var MAX_RETRIES = 3;
|
|
540
|
+
var RETRY_DELAY_MS = 1e3;
|
|
541
|
+
function sleep(ms) {
|
|
542
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
543
|
+
}
|
|
544
|
+
async function fetchWithRetry(url, options = {}, timeoutMs = DEFAULT_TIMEOUT_MS, retries = MAX_RETRIES) {
|
|
545
|
+
let lastError = null;
|
|
546
|
+
for (let attempt = 0; attempt < retries; attempt++) {
|
|
547
|
+
try {
|
|
548
|
+
const controller = new AbortController();
|
|
549
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
550
|
+
const response = await fetch(url, {
|
|
551
|
+
...options,
|
|
552
|
+
signal: controller.signal
|
|
553
|
+
});
|
|
554
|
+
clearTimeout(timeoutId);
|
|
555
|
+
if (response.ok) {
|
|
556
|
+
return response;
|
|
557
|
+
}
|
|
558
|
+
if (response.status >= 400 && response.status < 500) {
|
|
559
|
+
const errorText = await response.text();
|
|
560
|
+
throw new Error(`Jupiter API error (${response.status}): ${errorText}`);
|
|
561
|
+
}
|
|
562
|
+
lastError = new Error(`Jupiter API returned ${response.status}`);
|
|
563
|
+
} catch (error) {
|
|
564
|
+
if (error instanceof Error) {
|
|
565
|
+
if (error.name === "AbortError") {
|
|
566
|
+
lastError = new Error(`Request timed out after ${timeoutMs}ms`);
|
|
567
|
+
} else {
|
|
568
|
+
lastError = error;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (attempt < retries - 1) {
|
|
573
|
+
await sleep(RETRY_DELAY_MS * (attempt + 1));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
throw lastError ?? new Error("Unknown error during fetch");
|
|
577
|
+
}
|
|
578
|
+
async function getJupiterQuote(inputMint, outputMint, amount, slippageBps = 50, maxAccounts = 40) {
|
|
579
|
+
const params = new URLSearchParams({
|
|
580
|
+
inputMint: inputMint.toBase58(),
|
|
581
|
+
outputMint: outputMint.toBase58(),
|
|
582
|
+
amount: amount.toString(),
|
|
583
|
+
slippageBps: slippageBps.toString(),
|
|
584
|
+
maxAccounts: maxAccounts.toString()
|
|
585
|
+
});
|
|
586
|
+
const url = `${JUPITER_API_BASE}/quote?${params.toString()}`;
|
|
587
|
+
const response = await fetchWithRetry(url);
|
|
588
|
+
const data = await response.json();
|
|
589
|
+
if (data.error) {
|
|
590
|
+
throw new Error(`Jupiter quote error: ${data.error}`);
|
|
591
|
+
}
|
|
592
|
+
return data;
|
|
593
|
+
}
|
|
594
|
+
async function getJupiterSwapInstructions(quote, userPubkey) {
|
|
595
|
+
const url = `${JUPITER_API_BASE}/swap-instructions`;
|
|
596
|
+
const body = {
|
|
597
|
+
quoteResponse: quote,
|
|
598
|
+
userPublicKey: userPubkey.toBase58(),
|
|
599
|
+
wrapAndUnwrapSol: true,
|
|
600
|
+
useSharedAccounts: true,
|
|
601
|
+
dynamicComputeUnitLimit: true,
|
|
602
|
+
skipUserAccountsRpcCalls: false
|
|
603
|
+
};
|
|
604
|
+
const response = await fetchWithRetry(url, {
|
|
605
|
+
method: "POST",
|
|
606
|
+
headers: { "Content-Type": "application/json" },
|
|
607
|
+
body: JSON.stringify(body)
|
|
608
|
+
});
|
|
609
|
+
const data = await response.json();
|
|
610
|
+
if (data.error) {
|
|
611
|
+
throw new Error(`Jupiter swap instructions error: ${data.error}`);
|
|
612
|
+
}
|
|
613
|
+
return data;
|
|
614
|
+
}
|
|
615
|
+
function parseSwapRemainingAccounts(swapInstruction) {
|
|
616
|
+
return swapInstruction.accounts.map((acc) => ({
|
|
617
|
+
pubkey: new PublicKey7(acc.pubkey),
|
|
618
|
+
isSigner: acc.isSigner,
|
|
619
|
+
isWritable: acc.isWritable
|
|
620
|
+
}));
|
|
621
|
+
}
|
|
622
|
+
async function fetchAddressLookupTables(connection, addresses) {
|
|
623
|
+
if (addresses.length === 0) {
|
|
624
|
+
return [];
|
|
625
|
+
}
|
|
626
|
+
const pubkeys = addresses.map((addr) => new PublicKey7(addr));
|
|
627
|
+
const accountInfos = await connection.getMultipleAccountsInfo(pubkeys);
|
|
628
|
+
const tables = [];
|
|
629
|
+
for (let i = 0; i < accountInfos.length; i++) {
|
|
630
|
+
const info = accountInfos[i];
|
|
631
|
+
if (info === null) {
|
|
632
|
+
console.warn(`ALT not found: ${addresses[i]}`);
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
const table = new AddressLookupTableAccount({
|
|
636
|
+
key: pubkeys[i],
|
|
637
|
+
state: AddressLookupTableAccount.deserialize(info.data)
|
|
638
|
+
});
|
|
639
|
+
tables.push(table);
|
|
640
|
+
}
|
|
641
|
+
return tables;
|
|
642
|
+
}
|
|
643
|
+
async function buildSwapData(connection, inputMint, outputMint, amount, userPubkey, slippageBps = 50, maxAccounts = 40) {
|
|
644
|
+
const quote = await getJupiterQuote(
|
|
645
|
+
inputMint,
|
|
646
|
+
outputMint,
|
|
647
|
+
amount,
|
|
648
|
+
slippageBps,
|
|
649
|
+
maxAccounts
|
|
650
|
+
);
|
|
651
|
+
const instructions = await getJupiterSwapInstructions(quote, userPubkey);
|
|
652
|
+
const remainingAccounts = parseSwapRemainingAccounts(instructions.swapInstruction);
|
|
653
|
+
const addressLookupTables = await fetchAddressLookupTables(
|
|
654
|
+
connection,
|
|
655
|
+
instructions.addressLookupTableAddresses
|
|
656
|
+
);
|
|
657
|
+
return {
|
|
658
|
+
quote,
|
|
659
|
+
instructions,
|
|
660
|
+
remainingAccounts,
|
|
661
|
+
addressLookupTables
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/jupiter/price.ts
|
|
666
|
+
import { BN as BN4 } from "@coral-xyz/anchor";
|
|
667
|
+
var DEFAULT_API_KEY = "9edc77aa-0be8-41cf-9264-907603c44150";
|
|
668
|
+
async function fetchJupiterPriceUsd(mint, apiKey = DEFAULT_API_KEY) {
|
|
669
|
+
const url = `https://api.jup.ag/price/v3?ids=${mint}`;
|
|
670
|
+
try {
|
|
671
|
+
const response = await fetch(url, {
|
|
672
|
+
headers: {
|
|
673
|
+
"x-api-key": apiKey
|
|
674
|
+
}
|
|
675
|
+
});
|
|
676
|
+
if (!response.ok) {
|
|
677
|
+
console.warn(`Jupiter Price API error: ${response.status}`);
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
const data = await response.json();
|
|
681
|
+
const priceData = data[mint];
|
|
682
|
+
if (!priceData?.usdPrice) {
|
|
683
|
+
console.warn(`No price data for ${mint}`);
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
return priceData.usdPrice;
|
|
687
|
+
} catch (error) {
|
|
688
|
+
console.warn(`Failed to fetch Jupiter price: ${error.message}`);
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
async function fetchJlpRate(jlpMint, fallbackPrice = 4.6, apiKey = DEFAULT_API_KEY) {
|
|
693
|
+
const priceUsd = await fetchJupiterPriceUsd(jlpMint, apiKey);
|
|
694
|
+
if (priceUsd === null) {
|
|
695
|
+
console.warn(`Using fallback JLP price: $${fallbackPrice}`);
|
|
696
|
+
return new BN4(Math.round(fallbackPrice * 1e8));
|
|
697
|
+
}
|
|
698
|
+
console.log(`JLP price: $${priceUsd.toFixed(4)} -> ${Math.round(priceUsd * 1e8)} (8 decimals)`);
|
|
699
|
+
return new BN4(Math.round(priceUsd * 1e8));
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// src/instructions/admin.ts
|
|
703
|
+
import { TransactionInstruction } from "@solana/web3.js";
|
|
704
|
+
var INIT_OR_UPDATE_VAULT_DISCRIMINATOR = Buffer.from([
|
|
705
|
+
91,
|
|
706
|
+
101,
|
|
707
|
+
110,
|
|
708
|
+
100,
|
|
709
|
+
105,
|
|
710
|
+
110,
|
|
711
|
+
103,
|
|
712
|
+
93
|
|
713
|
+
]);
|
|
714
|
+
var INITIALIZE_STV_DISCRIMINATOR = Buffer.from([
|
|
715
|
+
105,
|
|
716
|
+
110,
|
|
717
|
+
105,
|
|
718
|
+
116,
|
|
719
|
+
95,
|
|
720
|
+
115,
|
|
721
|
+
116,
|
|
722
|
+
118
|
|
723
|
+
]);
|
|
724
|
+
var UPDATE_STV_DISCRIMINATOR = Buffer.from([
|
|
725
|
+
117,
|
|
726
|
+
112,
|
|
727
|
+
100,
|
|
728
|
+
95,
|
|
729
|
+
115,
|
|
730
|
+
116,
|
|
731
|
+
118,
|
|
732
|
+
0
|
|
733
|
+
]);
|
|
734
|
+
function serializeOptionPubkey(value) {
|
|
735
|
+
if (value === null) {
|
|
736
|
+
return Buffer.from([0]);
|
|
737
|
+
}
|
|
738
|
+
return Buffer.concat([Buffer.from([1]), value.toBuffer()]);
|
|
739
|
+
}
|
|
740
|
+
function serializeOptionU64(value) {
|
|
741
|
+
if (value === null) {
|
|
742
|
+
return Buffer.from([0]);
|
|
743
|
+
}
|
|
744
|
+
const buf = Buffer.alloc(9);
|
|
745
|
+
buf.writeUInt8(1, 0);
|
|
746
|
+
buf.writeBigUInt64LE(BigInt(value.toString()), 1);
|
|
747
|
+
return buf;
|
|
748
|
+
}
|
|
749
|
+
function serializeU16(value) {
|
|
750
|
+
const buf = Buffer.alloc(2);
|
|
751
|
+
buf.writeUInt16LE(value, 0);
|
|
752
|
+
return buf;
|
|
753
|
+
}
|
|
754
|
+
function serializeU64(value) {
|
|
755
|
+
const buf = Buffer.alloc(8);
|
|
756
|
+
buf.writeBigUInt64LE(BigInt(value.toString()), 0);
|
|
757
|
+
return buf;
|
|
758
|
+
}
|
|
759
|
+
function createInitOrUpdateVaultInstruction(params, accounts, programId = JLPD_PROGRAM_ID) {
|
|
760
|
+
const data = Buffer.concat([
|
|
761
|
+
INIT_OR_UPDATE_VAULT_DISCRIMINATOR,
|
|
762
|
+
serializeOptionPubkey(params.jlpMint),
|
|
763
|
+
// baseAssetMints - Option<[Pubkey; 5]>
|
|
764
|
+
params.baseAssetMints ? Buffer.concat([Buffer.from([1]), ...params.baseAssetMints.map((p) => p.toBuffer())]) : Buffer.from([0]),
|
|
765
|
+
// jlxAssetMints - Option<[Pubkey; 5]>
|
|
766
|
+
params.jlxAssetMints ? Buffer.concat([Buffer.from([1]), ...params.jlxAssetMints.map((p) => p.toBuffer())]) : Buffer.from([0]),
|
|
767
|
+
serializeOptionPubkey(params.newAdmin),
|
|
768
|
+
serializeOptionPubkey(params.manager ?? null),
|
|
769
|
+
serializeOptionPubkey(params.feeReceiver ?? null),
|
|
770
|
+
params.flags !== void 0 ? Buffer.concat([Buffer.from([1]), serializeU16(params.flags)]) : Buffer.from([0]),
|
|
771
|
+
params.jlpSlippageBps !== void 0 ? Buffer.concat([Buffer.from([1]), serializeU16(params.jlpSlippageBps)]) : Buffer.from([0]),
|
|
772
|
+
serializeOptionU64(params.oracleStalenessThreshold)
|
|
773
|
+
]);
|
|
774
|
+
const keys = [
|
|
775
|
+
{ pubkey: accounts.admin, isSigner: true, isWritable: true },
|
|
776
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: true },
|
|
777
|
+
{ pubkey: accounts.systemProgram, isSigner: false, isWritable: false }
|
|
778
|
+
];
|
|
779
|
+
return new TransactionInstruction({ keys, programId, data });
|
|
780
|
+
}
|
|
781
|
+
function createInitializeStvInstruction(params, accounts, programId = JLPD_PROGRAM_ID) {
|
|
782
|
+
const data = Buffer.concat([
|
|
783
|
+
INITIALIZE_STV_DISCRIMINATOR,
|
|
784
|
+
serializeU16(params.mgmtFeeBps),
|
|
785
|
+
serializeU16(params.perfFeeBps),
|
|
786
|
+
serializeU64(params.maxDeposit),
|
|
787
|
+
serializeU64(params.minDeposit)
|
|
788
|
+
]);
|
|
789
|
+
const keys = [
|
|
790
|
+
{ pubkey: accounts.admin, isSigner: true, isWritable: true },
|
|
791
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
792
|
+
{ pubkey: accounts.stv, isSigner: false, isWritable: true },
|
|
793
|
+
{ pubkey: accounts.baseMint, isSigner: false, isWritable: false },
|
|
794
|
+
{ pubkey: accounts.jlMint, isSigner: false, isWritable: false },
|
|
795
|
+
{ pubkey: accounts.jvMint, isSigner: true, isWritable: true },
|
|
796
|
+
{ pubkey: accounts.stvJlxAta, isSigner: false, isWritable: true },
|
|
797
|
+
{ pubkey: accounts.systemProgram, isSigner: false, isWritable: false },
|
|
798
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
799
|
+
{ pubkey: accounts.associatedTokenProgram, isSigner: false, isWritable: false },
|
|
800
|
+
{ pubkey: accounts.rent, isSigner: false, isWritable: false }
|
|
801
|
+
];
|
|
802
|
+
return new TransactionInstruction({ keys, programId, data });
|
|
803
|
+
}
|
|
804
|
+
function createUpdateStvInstruction(params, accounts, programId = JLPD_PROGRAM_ID) {
|
|
805
|
+
const data = Buffer.concat([
|
|
806
|
+
UPDATE_STV_DISCRIMINATOR,
|
|
807
|
+
params.mgmtFeeBps !== void 0 ? Buffer.concat([Buffer.from([1]), serializeU16(params.mgmtFeeBps)]) : Buffer.from([0]),
|
|
808
|
+
params.perfFeeBps !== void 0 ? Buffer.concat([Buffer.from([1]), serializeU16(params.perfFeeBps)]) : Buffer.from([0]),
|
|
809
|
+
params.flags !== void 0 ? Buffer.concat([Buffer.from([1]), serializeU16(params.flags)]) : Buffer.from([0]),
|
|
810
|
+
params.maxDeposit !== void 0 ? Buffer.concat([Buffer.from([1]), serializeU64(params.maxDeposit)]) : Buffer.from([0]),
|
|
811
|
+
params.minDeposit !== void 0 ? Buffer.concat([Buffer.from([1]), serializeU64(params.minDeposit)]) : Buffer.from([0])
|
|
812
|
+
]);
|
|
813
|
+
const keys = [
|
|
814
|
+
{ pubkey: accounts.admin, isSigner: true, isWritable: false },
|
|
815
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
816
|
+
{ pubkey: accounts.stv, isSigner: false, isWritable: true }
|
|
817
|
+
];
|
|
818
|
+
return new TransactionInstruction({ keys, programId, data });
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// src/instructions/user.ts
|
|
822
|
+
import { TransactionInstruction as TransactionInstruction2 } from "@solana/web3.js";
|
|
823
|
+
var DEPOSIT_DISCRIMINATOR = Buffer.from([
|
|
824
|
+
248,
|
|
825
|
+
198,
|
|
826
|
+
158,
|
|
827
|
+
145,
|
|
828
|
+
225,
|
|
829
|
+
117,
|
|
830
|
+
135,
|
|
831
|
+
200
|
|
832
|
+
]);
|
|
833
|
+
var WITHDRAW_DISCRIMINATOR = Buffer.from([
|
|
834
|
+
183,
|
|
835
|
+
18,
|
|
836
|
+
70,
|
|
837
|
+
156,
|
|
838
|
+
148,
|
|
839
|
+
109,
|
|
840
|
+
161,
|
|
841
|
+
34
|
|
842
|
+
]);
|
|
843
|
+
function serializeU642(value) {
|
|
844
|
+
const buf = Buffer.alloc(8);
|
|
845
|
+
buf.writeBigUInt64LE(BigInt(value.toString()), 0);
|
|
846
|
+
return buf;
|
|
847
|
+
}
|
|
848
|
+
function createDepositInstruction(amount, accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
849
|
+
const data = Buffer.concat([DEPOSIT_DISCRIMINATOR, serializeU642(amount)]);
|
|
850
|
+
const keys = [
|
|
851
|
+
{ pubkey: accounts.user, isSigner: true, isWritable: true },
|
|
852
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
853
|
+
{ pubkey: accounts.stv, isSigner: false, isWritable: true },
|
|
854
|
+
{ pubkey: accounts.baseMint, isSigner: false, isWritable: false },
|
|
855
|
+
{ pubkey: accounts.jlMint, isSigner: false, isWritable: false },
|
|
856
|
+
{ pubkey: accounts.jvMint, isSigner: false, isWritable: true },
|
|
857
|
+
{ pubkey: accounts.userBaseAta, isSigner: false, isWritable: true },
|
|
858
|
+
{ pubkey: accounts.userJvxAta, isSigner: false, isWritable: true },
|
|
859
|
+
{ pubkey: accounts.stvBaseAta, isSigner: false, isWritable: true },
|
|
860
|
+
{ pubkey: accounts.stvJlxAta, isSigner: false, isWritable: false },
|
|
861
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
862
|
+
...remainingAccounts
|
|
863
|
+
];
|
|
864
|
+
return new TransactionInstruction2({ keys, programId, data });
|
|
865
|
+
}
|
|
866
|
+
function createWithdrawInstruction(sharesToBurn, accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
867
|
+
const data = Buffer.concat([WITHDRAW_DISCRIMINATOR, serializeU642(sharesToBurn)]);
|
|
868
|
+
const keys = [
|
|
869
|
+
{ pubkey: accounts.user, isSigner: true, isWritable: true },
|
|
870
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
871
|
+
{ pubkey: accounts.stv, isSigner: false, isWritable: true },
|
|
872
|
+
{ pubkey: accounts.baseMint, isSigner: false, isWritable: false },
|
|
873
|
+
{ pubkey: accounts.jlMint, isSigner: false, isWritable: false },
|
|
874
|
+
{ pubkey: accounts.jvMint, isSigner: false, isWritable: true },
|
|
875
|
+
{ pubkey: accounts.userBaseAta, isSigner: false, isWritable: true },
|
|
876
|
+
{ pubkey: accounts.userJvxAta, isSigner: false, isWritable: true },
|
|
877
|
+
{ pubkey: accounts.stvBaseAta, isSigner: false, isWritable: true },
|
|
878
|
+
{ pubkey: accounts.stvJlxAta, isSigner: false, isWritable: false },
|
|
879
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
880
|
+
...remainingAccounts
|
|
881
|
+
];
|
|
882
|
+
return new TransactionInstruction2({ keys, programId, data });
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// src/instructions/manager.ts
|
|
886
|
+
import { TransactionInstruction as TransactionInstruction3 } from "@solana/web3.js";
|
|
887
|
+
var JUP_EARN_DISCRIMINATOR = Buffer.from([
|
|
888
|
+
161,
|
|
889
|
+
178,
|
|
890
|
+
195,
|
|
891
|
+
212,
|
|
892
|
+
229,
|
|
893
|
+
246,
|
|
894
|
+
7,
|
|
895
|
+
24
|
|
896
|
+
]);
|
|
897
|
+
var MOVE_STV_DISCRIMINATOR = Buffer.from([
|
|
898
|
+
41,
|
|
899
|
+
58,
|
|
900
|
+
75,
|
|
901
|
+
92,
|
|
902
|
+
109,
|
|
903
|
+
126,
|
|
904
|
+
143,
|
|
905
|
+
144
|
|
906
|
+
]);
|
|
907
|
+
var SWAP_JLX_JLX_DISCRIMINATOR = Buffer.from([
|
|
908
|
+
65,
|
|
909
|
+
82,
|
|
910
|
+
99,
|
|
911
|
+
116,
|
|
912
|
+
133,
|
|
913
|
+
150,
|
|
914
|
+
167,
|
|
915
|
+
184
|
|
916
|
+
]);
|
|
917
|
+
var SWAP_JLX_JLP_DISCRIMINATOR = Buffer.from([
|
|
918
|
+
201,
|
|
919
|
+
218,
|
|
920
|
+
235,
|
|
921
|
+
252,
|
|
922
|
+
13,
|
|
923
|
+
30,
|
|
924
|
+
47,
|
|
925
|
+
48
|
|
926
|
+
]);
|
|
927
|
+
var SETTLE_YIELD_DISCRIMINATOR = Buffer.from([
|
|
928
|
+
81,
|
|
929
|
+
98,
|
|
930
|
+
115,
|
|
931
|
+
132,
|
|
932
|
+
149,
|
|
933
|
+
166,
|
|
934
|
+
183,
|
|
935
|
+
200
|
|
936
|
+
]);
|
|
937
|
+
function serializeU8(value) {
|
|
938
|
+
const buf = Buffer.alloc(1);
|
|
939
|
+
buf.writeUInt8(value, 0);
|
|
940
|
+
return buf;
|
|
941
|
+
}
|
|
942
|
+
function serializeU643(value) {
|
|
943
|
+
const buf = Buffer.alloc(8);
|
|
944
|
+
buf.writeBigUInt64LE(BigInt(value.toString()), 0);
|
|
945
|
+
return buf;
|
|
946
|
+
}
|
|
947
|
+
function createJupEarnInstruction(params, accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
948
|
+
const directionByte = params.direction === "Deposit" ? 0 : 1;
|
|
949
|
+
const data = Buffer.concat([
|
|
950
|
+
JUP_EARN_DISCRIMINATOR,
|
|
951
|
+
serializeU8(directionByte),
|
|
952
|
+
serializeU643(params.amount)
|
|
953
|
+
]);
|
|
954
|
+
const keys = [
|
|
955
|
+
{ pubkey: accounts.manager, isSigner: true, isWritable: false },
|
|
956
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
957
|
+
{ pubkey: accounts.stv, isSigner: false, isWritable: false },
|
|
958
|
+
{ pubkey: accounts.baseMint, isSigner: false, isWritable: false },
|
|
959
|
+
{ pubkey: accounts.jlMint, isSigner: false, isWritable: true },
|
|
960
|
+
{ pubkey: accounts.stvBaseAta, isSigner: false, isWritable: true },
|
|
961
|
+
{ pubkey: accounts.stvJlxAta, isSigner: false, isWritable: true },
|
|
962
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
963
|
+
...remainingAccounts
|
|
964
|
+
];
|
|
965
|
+
return new TransactionInstruction3({ keys, programId, data });
|
|
966
|
+
}
|
|
967
|
+
function createMoveJlxInstruction(params, accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
968
|
+
const directionByte = params.direction === "ToVault" ? 0 : 1;
|
|
969
|
+
const data = Buffer.concat([
|
|
970
|
+
MOVE_STV_DISCRIMINATOR,
|
|
971
|
+
serializeU643(params.amountJlx),
|
|
972
|
+
serializeU8(directionByte)
|
|
973
|
+
]);
|
|
974
|
+
const keys = [
|
|
975
|
+
{ pubkey: accounts.manager, isSigner: true, isWritable: false },
|
|
976
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
977
|
+
{ pubkey: accounts.stv, isSigner: false, isWritable: true },
|
|
978
|
+
{ pubkey: accounts.baseMint, isSigner: false, isWritable: false },
|
|
979
|
+
{ pubkey: accounts.jlMint, isSigner: false, isWritable: false },
|
|
980
|
+
{ pubkey: accounts.stvJlxAta, isSigner: false, isWritable: true },
|
|
981
|
+
{ pubkey: accounts.vaultStagingAta, isSigner: false, isWritable: true },
|
|
982
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
983
|
+
...remainingAccounts
|
|
984
|
+
];
|
|
985
|
+
return new TransactionInstruction3({ keys, programId, data });
|
|
986
|
+
}
|
|
987
|
+
function createSwapJlxJlxInstruction(params, accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
988
|
+
const lenBuf = Buffer.alloc(4);
|
|
989
|
+
lenBuf.writeUInt32LE(params.jupiterData.length, 0);
|
|
990
|
+
const data = Buffer.concat([
|
|
991
|
+
SWAP_JLX_JLX_DISCRIMINATOR,
|
|
992
|
+
serializeU8(params.fromIndex),
|
|
993
|
+
serializeU8(params.toIndex),
|
|
994
|
+
serializeU643(params.amountIn),
|
|
995
|
+
serializeU643(params.minOut),
|
|
996
|
+
lenBuf,
|
|
997
|
+
params.jupiterData
|
|
998
|
+
]);
|
|
999
|
+
const keys = [
|
|
1000
|
+
{ pubkey: accounts.manager, isSigner: true, isWritable: false },
|
|
1001
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
1002
|
+
{ pubkey: accounts.fromJlxMint, isSigner: false, isWritable: false },
|
|
1003
|
+
{ pubkey: accounts.toJlxMint, isSigner: false, isWritable: false },
|
|
1004
|
+
{ pubkey: accounts.fromJlxAta, isSigner: false, isWritable: true },
|
|
1005
|
+
{ pubkey: accounts.toJlxAta, isSigner: false, isWritable: true },
|
|
1006
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
1007
|
+
{ pubkey: accounts.jupiterProgram, isSigner: false, isWritable: false },
|
|
1008
|
+
...remainingAccounts
|
|
1009
|
+
];
|
|
1010
|
+
return new TransactionInstruction3({ keys, programId, data });
|
|
1011
|
+
}
|
|
1012
|
+
function createSwapJlxJlpInstruction(params, accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
1013
|
+
const directionByte = params.direction === "JlxToJlp" ? 0 : 1;
|
|
1014
|
+
const lenBuf = Buffer.alloc(4);
|
|
1015
|
+
lenBuf.writeUInt32LE(params.jupiterData.length, 0);
|
|
1016
|
+
const data = Buffer.concat([
|
|
1017
|
+
SWAP_JLX_JLP_DISCRIMINATOR,
|
|
1018
|
+
serializeU8(params.stvIndex),
|
|
1019
|
+
serializeU8(directionByte),
|
|
1020
|
+
serializeU643(params.amountIn),
|
|
1021
|
+
serializeU643(params.expectedAmountOut),
|
|
1022
|
+
lenBuf,
|
|
1023
|
+
params.jupiterData
|
|
1024
|
+
]);
|
|
1025
|
+
const keys = [
|
|
1026
|
+
{ pubkey: accounts.manager, isSigner: true, isWritable: false },
|
|
1027
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
1028
|
+
{ pubkey: accounts.vaultJlxAta, isSigner: false, isWritable: true },
|
|
1029
|
+
{ pubkey: accounts.vaultJlpAta, isSigner: false, isWritable: true },
|
|
1030
|
+
{ pubkey: accounts.jlpMint, isSigner: false, isWritable: false },
|
|
1031
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
1032
|
+
{ pubkey: accounts.jupiterProgram, isSigner: false, isWritable: false },
|
|
1033
|
+
...remainingAccounts
|
|
1034
|
+
];
|
|
1035
|
+
return new TransactionInstruction3({ keys, programId, data });
|
|
1036
|
+
}
|
|
1037
|
+
function createSettleYieldInstruction(params, accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
1038
|
+
const data = Buffer.concat([
|
|
1039
|
+
SETTLE_YIELD_DISCRIMINATOR,
|
|
1040
|
+
serializeU643(params.jlpRate)
|
|
1041
|
+
]);
|
|
1042
|
+
const keys = [
|
|
1043
|
+
{ pubkey: accounts.manager, isSigner: true, isWritable: false },
|
|
1044
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: true },
|
|
1045
|
+
{ pubkey: accounts.vaultJlpAta, isSigner: false, isWritable: false },
|
|
1046
|
+
{ pubkey: accounts.jlpMint, isSigner: false, isWritable: false },
|
|
1047
|
+
{ pubkey: accounts.stvBtc, isSigner: false, isWritable: true },
|
|
1048
|
+
{ pubkey: accounts.stvEth, isSigner: false, isWritable: true },
|
|
1049
|
+
{ pubkey: accounts.stvSol, isSigner: false, isWritable: true },
|
|
1050
|
+
{ pubkey: accounts.stvUsdc, isSigner: false, isWritable: true },
|
|
1051
|
+
{ pubkey: accounts.stvJupusd, isSigner: false, isWritable: true },
|
|
1052
|
+
{ pubkey: accounts.stagingBtc, isSigner: false, isWritable: false },
|
|
1053
|
+
{ pubkey: accounts.stagingEth, isSigner: false, isWritable: false },
|
|
1054
|
+
{ pubkey: accounts.stagingSol, isSigner: false, isWritable: false },
|
|
1055
|
+
{ pubkey: accounts.stagingUsdc, isSigner: false, isWritable: false },
|
|
1056
|
+
{ pubkey: accounts.stagingJupusd, isSigner: false, isWritable: false },
|
|
1057
|
+
{ pubkey: accounts.oracleBtc, isSigner: false, isWritable: false },
|
|
1058
|
+
{ pubkey: accounts.oracleEth, isSigner: false, isWritable: false },
|
|
1059
|
+
{ pubkey: accounts.oracleSol, isSigner: false, isWritable: false },
|
|
1060
|
+
{ pubkey: accounts.oracleJlp, isSigner: false, isWritable: false },
|
|
1061
|
+
...remainingAccounts
|
|
1062
|
+
];
|
|
1063
|
+
return new TransactionInstruction3({ keys, programId, data });
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// src/instructions/fees.ts
|
|
1067
|
+
import { TransactionInstruction as TransactionInstruction4 } from "@solana/web3.js";
|
|
1068
|
+
var CLAIM_FEES_DISCRIMINATOR = Buffer.from([
|
|
1069
|
+
209,
|
|
1070
|
+
226,
|
|
1071
|
+
243,
|
|
1072
|
+
4,
|
|
1073
|
+
21,
|
|
1074
|
+
38,
|
|
1075
|
+
55,
|
|
1076
|
+
72
|
|
1077
|
+
]);
|
|
1078
|
+
function createClaimFeesInstruction(accounts, remainingAccounts = [], programId = JLPD_PROGRAM_ID) {
|
|
1079
|
+
const data = CLAIM_FEES_DISCRIMINATOR;
|
|
1080
|
+
const keys = [
|
|
1081
|
+
{ pubkey: accounts.manager, isSigner: true, isWritable: false },
|
|
1082
|
+
{ pubkey: accounts.jlpVault, isSigner: false, isWritable: false },
|
|
1083
|
+
{ pubkey: accounts.stv, isSigner: false, isWritable: true },
|
|
1084
|
+
{ pubkey: accounts.baseMint, isSigner: false, isWritable: false },
|
|
1085
|
+
{ pubkey: accounts.jlMint, isSigner: false, isWritable: false },
|
|
1086
|
+
{ pubkey: accounts.jvMint, isSigner: false, isWritable: false },
|
|
1087
|
+
{ pubkey: accounts.stvBaseAta, isSigner: false, isWritable: false },
|
|
1088
|
+
{ pubkey: accounts.stvJlxAta, isSigner: false, isWritable: true },
|
|
1089
|
+
{ pubkey: accounts.feeReceiverJlxAta, isSigner: false, isWritable: true },
|
|
1090
|
+
{ pubkey: accounts.tokenProgram, isSigner: false, isWritable: false },
|
|
1091
|
+
...remainingAccounts
|
|
1092
|
+
];
|
|
1093
|
+
return new TransactionInstruction4({ keys, programId, data });
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// src/client.ts
|
|
1097
|
+
var JlpdClientError = class extends Error {
|
|
1098
|
+
constructor(message) {
|
|
1099
|
+
super(message);
|
|
1100
|
+
this.name = "JlpdClientError";
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
var JlpdClient = class _JlpdClient {
|
|
1104
|
+
constructor(connection, programId = JLPD_PROGRAM_ID) {
|
|
1105
|
+
// Cache for PoolContext instances (avoids re-deriving PDAs/ATAs)
|
|
1106
|
+
this.poolContextCache = /* @__PURE__ */ new Map();
|
|
1107
|
+
this.connection = connection;
|
|
1108
|
+
this.programId = programId;
|
|
1109
|
+
const [vaultPda, vaultBump] = deriveVaultPda(programId);
|
|
1110
|
+
this.vaultPda = vaultPda;
|
|
1111
|
+
this.vaultBump = vaultBump;
|
|
1112
|
+
}
|
|
1113
|
+
static mainnet(connection) {
|
|
1114
|
+
return new _JlpdClient(connection, JLPD_PROGRAM_ID);
|
|
1115
|
+
}
|
|
1116
|
+
static devnet(connection) {
|
|
1117
|
+
const devnetProgramId = new PublicKey12(
|
|
1118
|
+
"AcVcAihjB9Z3Lg3ZiboWxni2sTzXUrrsF3unGX4ntn6p"
|
|
1119
|
+
);
|
|
1120
|
+
return new _JlpdClient(connection, devnetProgramId);
|
|
1121
|
+
}
|
|
1122
|
+
// Fetch vault account
|
|
1123
|
+
async fetchVault() {
|
|
1124
|
+
const accountInfo = await this.connection.getAccountInfo(this.vaultPda);
|
|
1125
|
+
if (!accountInfo) return null;
|
|
1126
|
+
return parseVault(accountInfo.data);
|
|
1127
|
+
}
|
|
1128
|
+
// Fetch STV by base mint
|
|
1129
|
+
async fetchStv(baseMint) {
|
|
1130
|
+
const [stvPda] = deriveStvPda(baseMint, this.programId);
|
|
1131
|
+
const accountInfo = await this.connection.getAccountInfo(stvPda);
|
|
1132
|
+
if (!accountInfo) return null;
|
|
1133
|
+
return parseStv(accountInfo.data);
|
|
1134
|
+
}
|
|
1135
|
+
// Fetch exchange rate for a pool
|
|
1136
|
+
async fetchExchangeRate(poolName) {
|
|
1137
|
+
const pool = getPoolByName(poolName);
|
|
1138
|
+
if (!pool) throw new JlpdClientError(`Unknown pool: ${poolName}`);
|
|
1139
|
+
if (!pool.hasJupiterLend) {
|
|
1140
|
+
return {
|
|
1141
|
+
rate: 1,
|
|
1142
|
+
rawRate: BigInt(EXCHANGE_RATE_PRECISION),
|
|
1143
|
+
storedRate: 1,
|
|
1144
|
+
staleness: 0,
|
|
1145
|
+
isFresh: true
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
const accounts = await this.connection.getMultipleAccountsInfo([
|
|
1149
|
+
pool.lending,
|
|
1150
|
+
pool.rewardsRateModel,
|
|
1151
|
+
pool.fTokenMint
|
|
1152
|
+
]);
|
|
1153
|
+
const [lendingInfo, rewardsInfo, mintInfo] = accounts;
|
|
1154
|
+
if (!lendingInfo || !rewardsInfo || !mintInfo) {
|
|
1155
|
+
throw new JlpdClientError("Failed to fetch Jupiter Lend accounts");
|
|
1156
|
+
}
|
|
1157
|
+
const lendingData = parseLendingAccount(lendingInfo.data);
|
|
1158
|
+
const rewardsData = parseRewardsRateModel(rewardsInfo.data);
|
|
1159
|
+
const slot = await this.connection.getSlot();
|
|
1160
|
+
const timestamp = await this.connection.getBlockTime(slot);
|
|
1161
|
+
if (!timestamp) throw new JlpdClientError("Failed to get block time");
|
|
1162
|
+
const supply = mintInfo.data.readBigUInt64LE(36);
|
|
1163
|
+
const rawRate = calculateExchangeRate(
|
|
1164
|
+
lendingData.tokenExchangePrice,
|
|
1165
|
+
rewardsData.yearlyReward,
|
|
1166
|
+
BigInt(timestamp),
|
|
1167
|
+
lendingData.lastUpdateTimestamp,
|
|
1168
|
+
supply
|
|
1169
|
+
);
|
|
1170
|
+
const rate = Number(rawRate) / EXCHANGE_RATE_PRECISION;
|
|
1171
|
+
const storedRate = Number(lendingData.tokenExchangePrice) / EXCHANGE_RATE_PRECISION;
|
|
1172
|
+
const staleness = timestamp - Number(lendingData.lastUpdateTimestamp);
|
|
1173
|
+
return {
|
|
1174
|
+
rate,
|
|
1175
|
+
rawRate,
|
|
1176
|
+
storedRate,
|
|
1177
|
+
staleness,
|
|
1178
|
+
isFresh: staleness < 300
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
// Get pool context for pool-specific operations (cached)
|
|
1182
|
+
pool(poolNameOrMint) {
|
|
1183
|
+
const key = typeof poolNameOrMint === "string" ? poolNameOrMint : poolNameOrMint.toBase58();
|
|
1184
|
+
let ctx = this.poolContextCache.get(key);
|
|
1185
|
+
if (!ctx) {
|
|
1186
|
+
const pool = typeof poolNameOrMint === "string" ? getPoolByName(poolNameOrMint) : getPoolByMint(poolNameOrMint);
|
|
1187
|
+
if (!pool) throw new JlpdClientError(`Unknown pool: ${key}`);
|
|
1188
|
+
ctx = new PoolContext(this, pool);
|
|
1189
|
+
this.poolContextCache.set(key, ctx);
|
|
1190
|
+
if (typeof poolNameOrMint === "string") {
|
|
1191
|
+
this.poolContextCache.set(pool.mint.toBase58(), ctx);
|
|
1192
|
+
} else {
|
|
1193
|
+
this.poolContextCache.set(pool.name, ctx);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
return ctx;
|
|
1197
|
+
}
|
|
1198
|
+
// Get swap context for swap operations
|
|
1199
|
+
swap() {
|
|
1200
|
+
return new SwapContext(this);
|
|
1201
|
+
}
|
|
1202
|
+
// Get admin context for admin operations
|
|
1203
|
+
admin() {
|
|
1204
|
+
return new AdminContext(this);
|
|
1205
|
+
}
|
|
1206
|
+
// Build a versioned transaction
|
|
1207
|
+
async buildTransaction(payer, ixs, alts = []) {
|
|
1208
|
+
const { blockhash } = await this.connection.getLatestBlockhash("confirmed");
|
|
1209
|
+
const message = new TransactionMessage({
|
|
1210
|
+
payerKey: payer,
|
|
1211
|
+
recentBlockhash: blockhash,
|
|
1212
|
+
instructions: ixs
|
|
1213
|
+
}).compileToV0Message(alts);
|
|
1214
|
+
return new VersionedTransaction(message);
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
var PoolContext = class {
|
|
1218
|
+
constructor(client, pool) {
|
|
1219
|
+
this.client = client;
|
|
1220
|
+
this.pool = pool;
|
|
1221
|
+
const [pda, bump] = deriveStvPda(pool.mint, client.programId);
|
|
1222
|
+
this.stvPda = pda;
|
|
1223
|
+
this.stvBump = bump;
|
|
1224
|
+
const vaultAtas = deriveVaultAtas(pool.fTokenMint, MINTS.JLP, client.vaultPda);
|
|
1225
|
+
this.vaultStagingAta = vaultAtas.stagingAta;
|
|
1226
|
+
this.vaultJlpAta = vaultAtas.jlpAta;
|
|
1227
|
+
const stvAtas = deriveStvAtas(pool.mint, pool.fTokenMint, this.stvPda);
|
|
1228
|
+
this.stvBaseAta = stvAtas.baseAta;
|
|
1229
|
+
this.stvJlxAta = stvAtas.jlxAta;
|
|
1230
|
+
}
|
|
1231
|
+
// Deposit base asset and receive jvX shares
|
|
1232
|
+
async deposit(params) {
|
|
1233
|
+
if (params.amount.isZero() || params.amount.isNeg()) {
|
|
1234
|
+
throw new JlpdClientError("Deposit amount must be positive");
|
|
1235
|
+
}
|
|
1236
|
+
const [stv, { blockhash }] = await Promise.all([
|
|
1237
|
+
this.client.fetchStv(this.pool.mint),
|
|
1238
|
+
this.client.connection.getLatestBlockhash("confirmed")
|
|
1239
|
+
]);
|
|
1240
|
+
if (!stv) throw new JlpdClientError("STV not found");
|
|
1241
|
+
const userAtas = deriveUserAtas(this.pool.mint, stv.jvMint, params.user);
|
|
1242
|
+
const erAccounts = this.pool.hasJupiterLend ? buildExchangeRateAccounts(this.pool) : null;
|
|
1243
|
+
const ix = createDepositInstruction(
|
|
1244
|
+
params.amount,
|
|
1245
|
+
{
|
|
1246
|
+
user: params.user,
|
|
1247
|
+
jlpVault: this.client.vaultPda,
|
|
1248
|
+
stv: this.stvPda,
|
|
1249
|
+
baseMint: this.pool.mint,
|
|
1250
|
+
jlMint: this.pool.fTokenMint,
|
|
1251
|
+
jvMint: stv.jvMint,
|
|
1252
|
+
userBaseAta: userAtas.baseAta,
|
|
1253
|
+
userJvxAta: userAtas.jvxAta,
|
|
1254
|
+
stvBaseAta: this.stvBaseAta,
|
|
1255
|
+
stvJlxAta: this.stvJlxAta,
|
|
1256
|
+
tokenProgram: TOKEN_PROGRAM_ID2
|
|
1257
|
+
},
|
|
1258
|
+
erAccounts ? [
|
|
1259
|
+
{ pubkey: erAccounts.lending, isSigner: false, isWritable: false },
|
|
1260
|
+
{ pubkey: erAccounts.rewardsRateModel, isSigner: false, isWritable: false }
|
|
1261
|
+
] : [],
|
|
1262
|
+
this.client.programId
|
|
1263
|
+
);
|
|
1264
|
+
const message = new TransactionMessage({
|
|
1265
|
+
payerKey: params.user,
|
|
1266
|
+
recentBlockhash: blockhash,
|
|
1267
|
+
instructions: [ix]
|
|
1268
|
+
}).compileToV0Message();
|
|
1269
|
+
return new VersionedTransaction(message);
|
|
1270
|
+
}
|
|
1271
|
+
// Burn jvX shares and withdraw base asset
|
|
1272
|
+
async withdraw(params) {
|
|
1273
|
+
if (params.shares.isZero() || params.shares.isNeg()) {
|
|
1274
|
+
throw new JlpdClientError("Withdraw shares must be positive");
|
|
1275
|
+
}
|
|
1276
|
+
const [stv, { blockhash }] = await Promise.all([
|
|
1277
|
+
this.client.fetchStv(this.pool.mint),
|
|
1278
|
+
this.client.connection.getLatestBlockhash("confirmed")
|
|
1279
|
+
]);
|
|
1280
|
+
if (!stv) throw new JlpdClientError("STV not found");
|
|
1281
|
+
const userAtas = deriveUserAtas(this.pool.mint, stv.jvMint, params.user);
|
|
1282
|
+
const erAccounts = this.pool.hasJupiterLend ? buildExchangeRateAccounts(this.pool) : null;
|
|
1283
|
+
const ix = createWithdrawInstruction(
|
|
1284
|
+
params.shares,
|
|
1285
|
+
{
|
|
1286
|
+
user: params.user,
|
|
1287
|
+
jlpVault: this.client.vaultPda,
|
|
1288
|
+
stv: this.stvPda,
|
|
1289
|
+
baseMint: this.pool.mint,
|
|
1290
|
+
jlMint: this.pool.fTokenMint,
|
|
1291
|
+
jvMint: stv.jvMint,
|
|
1292
|
+
userBaseAta: userAtas.baseAta,
|
|
1293
|
+
userJvxAta: userAtas.jvxAta,
|
|
1294
|
+
stvBaseAta: this.stvBaseAta,
|
|
1295
|
+
stvJlxAta: this.stvJlxAta,
|
|
1296
|
+
tokenProgram: TOKEN_PROGRAM_ID2
|
|
1297
|
+
},
|
|
1298
|
+
erAccounts ? [
|
|
1299
|
+
{ pubkey: erAccounts.lending, isSigner: false, isWritable: false },
|
|
1300
|
+
{ pubkey: erAccounts.rewardsRateModel, isSigner: false, isWritable: false }
|
|
1301
|
+
] : [],
|
|
1302
|
+
this.client.programId
|
|
1303
|
+
);
|
|
1304
|
+
const message = new TransactionMessage({
|
|
1305
|
+
payerKey: params.user,
|
|
1306
|
+
recentBlockhash: blockhash,
|
|
1307
|
+
instructions: [ix]
|
|
1308
|
+
}).compileToV0Message();
|
|
1309
|
+
return new VersionedTransaction(message);
|
|
1310
|
+
}
|
|
1311
|
+
// Jupiter Earn deposit/withdraw
|
|
1312
|
+
async jupEarn(params) {
|
|
1313
|
+
if (!this.pool.hasJupiterLend) {
|
|
1314
|
+
throw new JlpdClientError(`Pool ${this.pool.name} does not support Jupiter Earn`);
|
|
1315
|
+
}
|
|
1316
|
+
const remainingAccounts = params.direction === "Deposit" ? buildJupEarnDepositAccounts(this.pool, this.stvPda, this.stvBaseAta, this.stvJlxAta) : buildJupEarnWithdrawAccounts(this.pool, this.stvPda, this.stvJlxAta, this.stvBaseAta);
|
|
1317
|
+
const ix = createJupEarnInstruction(
|
|
1318
|
+
{ direction: params.direction, amount: params.amount },
|
|
1319
|
+
{
|
|
1320
|
+
manager: params.manager,
|
|
1321
|
+
jlpVault: this.client.vaultPda,
|
|
1322
|
+
stv: this.stvPda,
|
|
1323
|
+
baseMint: this.pool.mint,
|
|
1324
|
+
jlMint: this.pool.fTokenMint,
|
|
1325
|
+
stvBaseAta: this.stvBaseAta,
|
|
1326
|
+
stvJlxAta: this.stvJlxAta,
|
|
1327
|
+
tokenProgram: TOKEN_PROGRAM_ID2
|
|
1328
|
+
},
|
|
1329
|
+
remainingAccounts,
|
|
1330
|
+
this.client.programId
|
|
1331
|
+
);
|
|
1332
|
+
return this.client.buildTransaction(params.manager, [ix]);
|
|
1333
|
+
}
|
|
1334
|
+
// Move jlX between STV and vault
|
|
1335
|
+
async moveJlx(params) {
|
|
1336
|
+
const erAccounts = this.pool.hasJupiterLend ? buildExchangeRateAccounts(this.pool) : null;
|
|
1337
|
+
const ix = createMoveJlxInstruction(
|
|
1338
|
+
{ amountJlx: params.jlxAmount, direction: params.direction },
|
|
1339
|
+
{
|
|
1340
|
+
manager: params.manager,
|
|
1341
|
+
jlpVault: this.client.vaultPda,
|
|
1342
|
+
stv: this.stvPda,
|
|
1343
|
+
baseMint: this.pool.mint,
|
|
1344
|
+
jlMint: this.pool.fTokenMint,
|
|
1345
|
+
stvJlxAta: this.stvJlxAta,
|
|
1346
|
+
vaultStagingAta: this.vaultStagingAta,
|
|
1347
|
+
tokenProgram: TOKEN_PROGRAM_ID2
|
|
1348
|
+
},
|
|
1349
|
+
erAccounts ? [
|
|
1350
|
+
{ pubkey: erAccounts.lending, isSigner: false, isWritable: false },
|
|
1351
|
+
{ pubkey: erAccounts.rewardsRateModel, isSigner: false, isWritable: false }
|
|
1352
|
+
] : [],
|
|
1353
|
+
this.client.programId
|
|
1354
|
+
);
|
|
1355
|
+
return this.client.buildTransaction(params.manager, [ix]);
|
|
1356
|
+
}
|
|
1357
|
+
// Claim fees
|
|
1358
|
+
async claimFees(params) {
|
|
1359
|
+
const [vault, stv] = await Promise.all([
|
|
1360
|
+
this.client.fetchVault(),
|
|
1361
|
+
this.client.fetchStv(this.pool.mint)
|
|
1362
|
+
]);
|
|
1363
|
+
if (!vault) throw new JlpdClientError("Vault not found");
|
|
1364
|
+
if (!stv) throw new JlpdClientError("STV not found");
|
|
1365
|
+
const feeReceiverJlxAta = getAssociatedTokenAddressSync2(
|
|
1366
|
+
this.pool.fTokenMint,
|
|
1367
|
+
vault.feeReceiver,
|
|
1368
|
+
true
|
|
1369
|
+
);
|
|
1370
|
+
const erAccounts = this.pool.hasJupiterLend ? buildExchangeRateAccounts(this.pool) : null;
|
|
1371
|
+
const ix = createClaimFeesInstruction(
|
|
1372
|
+
{
|
|
1373
|
+
manager: params.manager,
|
|
1374
|
+
jlpVault: this.client.vaultPda,
|
|
1375
|
+
stv: this.stvPda,
|
|
1376
|
+
baseMint: this.pool.mint,
|
|
1377
|
+
jlMint: this.pool.fTokenMint,
|
|
1378
|
+
jvMint: stv.jvMint,
|
|
1379
|
+
stvBaseAta: this.stvBaseAta,
|
|
1380
|
+
stvJlxAta: this.stvJlxAta,
|
|
1381
|
+
feeReceiverJlxAta,
|
|
1382
|
+
tokenProgram: TOKEN_PROGRAM_ID2
|
|
1383
|
+
},
|
|
1384
|
+
erAccounts ? [
|
|
1385
|
+
{ pubkey: erAccounts.lending, isSigner: false, isWritable: false },
|
|
1386
|
+
{ pubkey: erAccounts.rewardsRateModel, isSigner: false, isWritable: false }
|
|
1387
|
+
] : [],
|
|
1388
|
+
this.client.programId
|
|
1389
|
+
);
|
|
1390
|
+
return this.client.buildTransaction(params.manager, [ix]);
|
|
1391
|
+
}
|
|
1392
|
+
// Update STV parameters (admin only)
|
|
1393
|
+
async updateStv(params) {
|
|
1394
|
+
const ix = createUpdateStvInstruction(
|
|
1395
|
+
{
|
|
1396
|
+
mgmtFeeBps: params.mgmtFeeBps,
|
|
1397
|
+
perfFeeBps: params.perfFeeBps,
|
|
1398
|
+
flags: params.flags,
|
|
1399
|
+
maxDeposit: params.maxDeposit,
|
|
1400
|
+
minDeposit: params.minDeposit
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
admin: params.admin,
|
|
1404
|
+
jlpVault: this.client.vaultPda,
|
|
1405
|
+
stv: this.stvPda
|
|
1406
|
+
},
|
|
1407
|
+
this.client.programId
|
|
1408
|
+
);
|
|
1409
|
+
return this.client.buildTransaction(params.admin, [ix]);
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
var SwapContext = class {
|
|
1413
|
+
constructor(client) {
|
|
1414
|
+
this.client = client;
|
|
1415
|
+
}
|
|
1416
|
+
// Get a quote for jlX <-> JLP swap
|
|
1417
|
+
async quoteJlxJlp(params) {
|
|
1418
|
+
const poolConfig = getPoolByName(params.pool);
|
|
1419
|
+
if (!poolConfig) throw new JlpdClientError(`Unknown pool: ${params.pool}`);
|
|
1420
|
+
const inputMint = params.direction === "JlxToJlp" ? poolConfig.fTokenMint : MINTS.JLP;
|
|
1421
|
+
const outputMint = params.direction === "JlxToJlp" ? MINTS.JLP : poolConfig.fTokenMint;
|
|
1422
|
+
const quote = await getJupiterQuote(
|
|
1423
|
+
inputMint,
|
|
1424
|
+
outputMint,
|
|
1425
|
+
BigInt(params.amountIn.toString()),
|
|
1426
|
+
params.slippageBps ?? 30
|
|
1427
|
+
);
|
|
1428
|
+
const swapIxs = await getJupiterSwapInstructions(quote, this.client.vaultPda);
|
|
1429
|
+
const remainingAccounts = parseSwapRemainingAccounts(swapIxs.swapInstruction);
|
|
1430
|
+
const alts = await fetchAddressLookupTables(
|
|
1431
|
+
this.client.connection,
|
|
1432
|
+
swapIxs.addressLookupTableAddresses
|
|
1433
|
+
);
|
|
1434
|
+
return {
|
|
1435
|
+
inputMint,
|
|
1436
|
+
outputMint,
|
|
1437
|
+
inAmount: new BN5(quote.inAmount),
|
|
1438
|
+
outAmount: new BN5(quote.outAmount),
|
|
1439
|
+
minOutAmount: new BN5(quote.otherAmountThreshold),
|
|
1440
|
+
priceImpactPct: parseFloat(quote.priceImpactPct),
|
|
1441
|
+
route: quote.routePlan.map((r) => r.swapInfo.label || "unknown"),
|
|
1442
|
+
jupiterData: Buffer.from(swapIxs.swapInstruction.data, "base64"),
|
|
1443
|
+
remainingAccounts,
|
|
1444
|
+
addressLookupTables: alts,
|
|
1445
|
+
computeUnitLimit: swapIxs.computeUnitLimit ?? 6e5
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
// Build jlX <-> JLP swap transaction
|
|
1449
|
+
async swapJlxJlp(params) {
|
|
1450
|
+
const poolConfig = getPoolByName(params.pool);
|
|
1451
|
+
if (!poolConfig) throw new JlpdClientError(`Unknown pool: ${params.pool}`);
|
|
1452
|
+
const poolCtx = this.client.pool(params.pool);
|
|
1453
|
+
const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
|
|
1454
|
+
units: params.quote.computeUnitLimit
|
|
1455
|
+
});
|
|
1456
|
+
const jupiterProgram = params.quote.remainingAccounts[0]?.pubkey ?? PublicKey12.default;
|
|
1457
|
+
const swapIx = createSwapJlxJlpInstruction(
|
|
1458
|
+
{
|
|
1459
|
+
stvIndex: poolConfig.stvIndex,
|
|
1460
|
+
direction: params.direction,
|
|
1461
|
+
amountIn: params.amountIn,
|
|
1462
|
+
expectedAmountOut: params.expectedOut,
|
|
1463
|
+
jupiterData: params.quote.jupiterData
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
manager: params.manager,
|
|
1467
|
+
jlpVault: this.client.vaultPda,
|
|
1468
|
+
vaultJlxAta: poolCtx.vaultStagingAta,
|
|
1469
|
+
vaultJlpAta: poolCtx.vaultJlpAta,
|
|
1470
|
+
jlpMint: MINTS.JLP,
|
|
1471
|
+
tokenProgram: TOKEN_PROGRAM_ID2,
|
|
1472
|
+
jupiterProgram
|
|
1473
|
+
},
|
|
1474
|
+
params.quote.remainingAccounts,
|
|
1475
|
+
this.client.programId
|
|
1476
|
+
);
|
|
1477
|
+
const { blockhash } = await this.client.connection.getLatestBlockhash("confirmed");
|
|
1478
|
+
const message = new TransactionMessage({
|
|
1479
|
+
payerKey: params.manager,
|
|
1480
|
+
recentBlockhash: blockhash,
|
|
1481
|
+
instructions: [computeIx, swapIx]
|
|
1482
|
+
}).compileToV0Message(params.quote.addressLookupTables);
|
|
1483
|
+
return new VersionedTransaction(message);
|
|
1484
|
+
}
|
|
1485
|
+
// Get a quote for jlX <-> jlX swap
|
|
1486
|
+
async quoteJlxJlx(params) {
|
|
1487
|
+
const fromPoolConfig = getPoolByName(params.fromPool);
|
|
1488
|
+
const toPoolConfig = getPoolByName(params.toPool);
|
|
1489
|
+
if (!fromPoolConfig) throw new JlpdClientError(`Unknown pool: ${params.fromPool}`);
|
|
1490
|
+
if (!toPoolConfig) throw new JlpdClientError(`Unknown pool: ${params.toPool}`);
|
|
1491
|
+
const inputMint = fromPoolConfig.fTokenMint;
|
|
1492
|
+
const outputMint = toPoolConfig.fTokenMint;
|
|
1493
|
+
const quote = await getJupiterQuote(
|
|
1494
|
+
inputMint,
|
|
1495
|
+
outputMint,
|
|
1496
|
+
BigInt(params.amountIn.toString()),
|
|
1497
|
+
params.slippageBps ?? 30
|
|
1498
|
+
);
|
|
1499
|
+
const swapIxs = await getJupiterSwapInstructions(quote, this.client.vaultPda);
|
|
1500
|
+
const remainingAccounts = parseSwapRemainingAccounts(swapIxs.swapInstruction);
|
|
1501
|
+
const alts = await fetchAddressLookupTables(
|
|
1502
|
+
this.client.connection,
|
|
1503
|
+
swapIxs.addressLookupTableAddresses
|
|
1504
|
+
);
|
|
1505
|
+
return {
|
|
1506
|
+
inputMint,
|
|
1507
|
+
outputMint,
|
|
1508
|
+
inAmount: new BN5(quote.inAmount),
|
|
1509
|
+
outAmount: new BN5(quote.outAmount),
|
|
1510
|
+
minOutAmount: new BN5(quote.otherAmountThreshold),
|
|
1511
|
+
priceImpactPct: parseFloat(quote.priceImpactPct),
|
|
1512
|
+
route: quote.routePlan.map((r) => r.swapInfo.label || "unknown"),
|
|
1513
|
+
jupiterData: Buffer.from(swapIxs.swapInstruction.data, "base64"),
|
|
1514
|
+
remainingAccounts,
|
|
1515
|
+
addressLookupTables: alts,
|
|
1516
|
+
computeUnitLimit: swapIxs.computeUnitLimit ?? 6e5
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
// Build jlX <-> jlX swap transaction
|
|
1520
|
+
async swapJlxJlx(params) {
|
|
1521
|
+
const fromPoolConfig = getPoolByName(params.fromPool);
|
|
1522
|
+
const toPoolConfig = getPoolByName(params.toPool);
|
|
1523
|
+
if (!fromPoolConfig) throw new JlpdClientError(`Unknown pool: ${params.fromPool}`);
|
|
1524
|
+
if (!toPoolConfig) throw new JlpdClientError(`Unknown pool: ${params.toPool}`);
|
|
1525
|
+
const fromPoolCtx = this.client.pool(params.fromPool);
|
|
1526
|
+
const toPoolCtx = this.client.pool(params.toPool);
|
|
1527
|
+
const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
|
|
1528
|
+
units: params.quote.computeUnitLimit
|
|
1529
|
+
});
|
|
1530
|
+
const jupiterProgram = params.quote.remainingAccounts[0]?.pubkey ?? PublicKey12.default;
|
|
1531
|
+
const swapIx = createSwapJlxJlxInstruction(
|
|
1532
|
+
{
|
|
1533
|
+
fromIndex: fromPoolConfig.stvIndex,
|
|
1534
|
+
toIndex: toPoolConfig.stvIndex,
|
|
1535
|
+
amountIn: params.amountIn,
|
|
1536
|
+
minOut: params.minOut,
|
|
1537
|
+
jupiterData: params.quote.jupiterData
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
manager: params.manager,
|
|
1541
|
+
jlpVault: this.client.vaultPda,
|
|
1542
|
+
fromJlxMint: fromPoolConfig.fTokenMint,
|
|
1543
|
+
toJlxMint: toPoolConfig.fTokenMint,
|
|
1544
|
+
fromJlxAta: fromPoolCtx.vaultStagingAta,
|
|
1545
|
+
toJlxAta: toPoolCtx.vaultStagingAta,
|
|
1546
|
+
tokenProgram: TOKEN_PROGRAM_ID2,
|
|
1547
|
+
jupiterProgram
|
|
1548
|
+
},
|
|
1549
|
+
params.quote.remainingAccounts,
|
|
1550
|
+
this.client.programId
|
|
1551
|
+
);
|
|
1552
|
+
const { blockhash } = await this.client.connection.getLatestBlockhash("confirmed");
|
|
1553
|
+
const message = new TransactionMessage({
|
|
1554
|
+
payerKey: params.manager,
|
|
1555
|
+
recentBlockhash: blockhash,
|
|
1556
|
+
instructions: [computeIx, swapIx]
|
|
1557
|
+
}).compileToV0Message(params.quote.addressLookupTables);
|
|
1558
|
+
return new VersionedTransaction(message);
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
var AdminContext = class {
|
|
1562
|
+
constructor(client) {
|
|
1563
|
+
this.client = client;
|
|
1564
|
+
}
|
|
1565
|
+
// Initialize or update vault
|
|
1566
|
+
async initOrUpdateVault(params) {
|
|
1567
|
+
const ix = createInitOrUpdateVaultInstruction(
|
|
1568
|
+
{
|
|
1569
|
+
jlpMint: params.jlpMint ?? null,
|
|
1570
|
+
baseAssetMints: params.baseAssetMints ?? null,
|
|
1571
|
+
jlxAssetMints: params.jlxAssetMints ?? null,
|
|
1572
|
+
newAdmin: params.newAdmin ?? null,
|
|
1573
|
+
manager: params.manager,
|
|
1574
|
+
feeReceiver: params.feeReceiver,
|
|
1575
|
+
flags: params.flags,
|
|
1576
|
+
jlpSlippageBps: params.jlpSlippageBps,
|
|
1577
|
+
oracleStalenessThreshold: params.oracleStalenessThreshold ?? null
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
admin: params.admin,
|
|
1581
|
+
jlpVault: this.client.vaultPda,
|
|
1582
|
+
systemProgram: SystemProgram2.programId
|
|
1583
|
+
},
|
|
1584
|
+
this.client.programId
|
|
1585
|
+
);
|
|
1586
|
+
return this.client.buildTransaction(params.admin, [ix]);
|
|
1587
|
+
}
|
|
1588
|
+
// Initialize STV
|
|
1589
|
+
async initializeStv(params, jvMintKeypair) {
|
|
1590
|
+
const pool = getPoolByName(params.poolName);
|
|
1591
|
+
if (!pool) throw new JlpdClientError(`Unknown pool: ${params.poolName}`);
|
|
1592
|
+
const [stvPda] = deriveStvPda(pool.mint, this.client.programId);
|
|
1593
|
+
const stvJlxAta = getAssociatedTokenAddressSync2(pool.fTokenMint, stvPda, true);
|
|
1594
|
+
const ix = createInitializeStvInstruction(
|
|
1595
|
+
{
|
|
1596
|
+
mgmtFeeBps: params.mgmtFeeBps,
|
|
1597
|
+
perfFeeBps: params.perfFeeBps,
|
|
1598
|
+
maxDeposit: params.maxDeposit,
|
|
1599
|
+
minDeposit: params.minDeposit
|
|
1600
|
+
},
|
|
1601
|
+
{
|
|
1602
|
+
admin: params.admin,
|
|
1603
|
+
jlpVault: this.client.vaultPda,
|
|
1604
|
+
stv: stvPda,
|
|
1605
|
+
baseMint: pool.mint,
|
|
1606
|
+
jlMint: pool.fTokenMint,
|
|
1607
|
+
jvMint: jvMintKeypair,
|
|
1608
|
+
stvJlxAta,
|
|
1609
|
+
systemProgram: SystemProgram2.programId,
|
|
1610
|
+
tokenProgram: TOKEN_PROGRAM_ID2,
|
|
1611
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID2,
|
|
1612
|
+
rent: SYSVAR_RENT_PUBKEY
|
|
1613
|
+
},
|
|
1614
|
+
this.client.programId
|
|
1615
|
+
);
|
|
1616
|
+
return this.client.buildTransaction(params.admin, [ix]);
|
|
1617
|
+
}
|
|
1618
|
+
// Settle yield across all STVs
|
|
1619
|
+
async settleYield(params) {
|
|
1620
|
+
const poolNames = ["BTC", "ETH", "SOL", "USDC", "JupUSD"];
|
|
1621
|
+
const pools = poolNames.map((name) => {
|
|
1622
|
+
const pool = getPoolByName(name);
|
|
1623
|
+
if (!pool) throw new JlpdClientError(`Unknown pool: ${name}`);
|
|
1624
|
+
return pool;
|
|
1625
|
+
});
|
|
1626
|
+
const stvPdas = pools.map((p) => deriveStvPda(p.mint, this.client.programId)[0]);
|
|
1627
|
+
const stagingAtas = pools.map(
|
|
1628
|
+
(p) => getAssociatedTokenAddressSync2(p.fTokenMint, this.client.vaultPda, true)
|
|
1629
|
+
);
|
|
1630
|
+
const lendingAccounts = pools.map((p) => ({
|
|
1631
|
+
pubkey: p.hasJupiterLend ? p.lending : PublicKey12.default,
|
|
1632
|
+
isSigner: false,
|
|
1633
|
+
isWritable: false
|
|
1634
|
+
}));
|
|
1635
|
+
const rewardsAccounts = pools.map((p) => ({
|
|
1636
|
+
pubkey: p.hasJupiterLend ? p.rewardsRateModel : PublicKey12.default,
|
|
1637
|
+
isSigner: false,
|
|
1638
|
+
isWritable: false
|
|
1639
|
+
}));
|
|
1640
|
+
const mintAccounts = pools.map((p) => ({
|
|
1641
|
+
pubkey: p.fTokenMint,
|
|
1642
|
+
isSigner: false,
|
|
1643
|
+
isWritable: false
|
|
1644
|
+
}));
|
|
1645
|
+
const remainingAccounts = [...lendingAccounts, ...rewardsAccounts, ...mintAccounts];
|
|
1646
|
+
const ix = createSettleYieldInstruction(
|
|
1647
|
+
{ jlpRate: params.jlpRate },
|
|
1648
|
+
{
|
|
1649
|
+
manager: params.manager,
|
|
1650
|
+
jlpVault: this.client.vaultPda,
|
|
1651
|
+
vaultJlpAta: getAssociatedTokenAddressSync2(MINTS.JLP, this.client.vaultPda, true),
|
|
1652
|
+
jlpMint: MINTS.JLP,
|
|
1653
|
+
stvBtc: stvPdas[0],
|
|
1654
|
+
stvEth: stvPdas[1],
|
|
1655
|
+
stvSol: stvPdas[2],
|
|
1656
|
+
stvUsdc: stvPdas[3],
|
|
1657
|
+
stvJupusd: stvPdas[4],
|
|
1658
|
+
stagingBtc: stagingAtas[0],
|
|
1659
|
+
stagingEth: stagingAtas[1],
|
|
1660
|
+
stagingSol: stagingAtas[2],
|
|
1661
|
+
stagingUsdc: stagingAtas[3],
|
|
1662
|
+
stagingJupusd: stagingAtas[4],
|
|
1663
|
+
oracleBtc: ORACLES.DOVES_BTC_USD,
|
|
1664
|
+
oracleEth: ORACLES.DOVES_ETH_USD,
|
|
1665
|
+
oracleSol: ORACLES.DOVES_SOL_USD,
|
|
1666
|
+
oracleJlp: ORACLES.PYTH_JLP_USD
|
|
1667
|
+
},
|
|
1668
|
+
remainingAccounts,
|
|
1669
|
+
this.client.programId
|
|
1670
|
+
);
|
|
1671
|
+
return this.client.buildTransaction(params.manager, [ix]);
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
export {
|
|
1675
|
+
AdminContext,
|
|
1676
|
+
EXCHANGE_RATE_PRECISION,
|
|
1677
|
+
FLAG_DEPOSITS_DISABLED,
|
|
1678
|
+
FLAG_JLP_DISABLED,
|
|
1679
|
+
FLAG_PAUSED,
|
|
1680
|
+
FLAG_REBALANCE_DISABLED,
|
|
1681
|
+
FLAG_WITHDRAWALS_DISABLED,
|
|
1682
|
+
JLPD_PROGRAM_ID,
|
|
1683
|
+
JLP_VAULT_ACCOUNT_SIZE,
|
|
1684
|
+
JLP_VAULT_DATA_SIZE,
|
|
1685
|
+
JLP_VAULT_DISCRIMINATOR,
|
|
1686
|
+
JUPITER_LEND_PROGRAM_ID,
|
|
1687
|
+
JUPITER_SWAP_PROGRAM_ID,
|
|
1688
|
+
JUPUSD_POOL,
|
|
1689
|
+
JUPITER_LEND_PROGRAM_ID2 as JUP_LEND_PROGRAM_ID,
|
|
1690
|
+
JlpdClient,
|
|
1691
|
+
JlpdClientError,
|
|
1692
|
+
LENDING_ADMIN,
|
|
1693
|
+
LIQUIDITY_PROGRAM_ID,
|
|
1694
|
+
LIQUIDITY_SINGLETON,
|
|
1695
|
+
MINTS,
|
|
1696
|
+
ORACLES,
|
|
1697
|
+
POOLS,
|
|
1698
|
+
PPS_DECIMALS,
|
|
1699
|
+
PoolContext,
|
|
1700
|
+
SEED_JLP_VAULT,
|
|
1701
|
+
SEED_STV,
|
|
1702
|
+
STV_ACCOUNT_SIZE,
|
|
1703
|
+
STV_DATA_SIZE,
|
|
1704
|
+
STV_DISCRIMINATOR,
|
|
1705
|
+
STV_INDEX,
|
|
1706
|
+
SwapContext,
|
|
1707
|
+
USDC_POOL,
|
|
1708
|
+
WBTC_POOL,
|
|
1709
|
+
WETH_POOL,
|
|
1710
|
+
WSOL_POOL,
|
|
1711
|
+
baseToJlx,
|
|
1712
|
+
baseToShares,
|
|
1713
|
+
buildExchangeRateAccounts,
|
|
1714
|
+
buildJupEarnDepositAccounts,
|
|
1715
|
+
buildJupEarnWithdrawAccounts,
|
|
1716
|
+
buildSwapData,
|
|
1717
|
+
calculateExchangeRate,
|
|
1718
|
+
calculateNav,
|
|
1719
|
+
calculatePps,
|
|
1720
|
+
clearAtaCache,
|
|
1721
|
+
clearPdaCache,
|
|
1722
|
+
createClaimFeesInstruction,
|
|
1723
|
+
createDepositInstruction,
|
|
1724
|
+
createInitOrUpdateVaultInstruction,
|
|
1725
|
+
createInitializeStvInstruction,
|
|
1726
|
+
createJupEarnInstruction,
|
|
1727
|
+
createMoveJlxInstruction,
|
|
1728
|
+
createSettleYieldInstruction,
|
|
1729
|
+
createSwapJlxJlpInstruction,
|
|
1730
|
+
createSwapJlxJlxInstruction,
|
|
1731
|
+
createUpdateStvInstruction,
|
|
1732
|
+
createWithdrawInstruction,
|
|
1733
|
+
deriveAta,
|
|
1734
|
+
deriveStvAtas,
|
|
1735
|
+
deriveStvPda,
|
|
1736
|
+
deriveUserAtas,
|
|
1737
|
+
deriveVaultAtas,
|
|
1738
|
+
deriveVaultPda,
|
|
1739
|
+
fetchAddressLookupTables,
|
|
1740
|
+
fetchJlpRate,
|
|
1741
|
+
fetchJupiterPriceUsd,
|
|
1742
|
+
getJupiterQuote,
|
|
1743
|
+
getJupiterSwapInstructions,
|
|
1744
|
+
getPoolByMint,
|
|
1745
|
+
getPoolByName,
|
|
1746
|
+
jlxToBase,
|
|
1747
|
+
parseLendingAccount,
|
|
1748
|
+
parseRewardsRateModel,
|
|
1749
|
+
parseStv,
|
|
1750
|
+
parseSwapRemainingAccounts,
|
|
1751
|
+
parseVault,
|
|
1752
|
+
sharesToBase
|
|
1753
|
+
};
|