@perena/vault-sdk 1.0.44 → 1.0.46

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.
@@ -0,0 +1,757 @@
1
+ // src/services/nestWithdrawalService.ts
2
+ import { address as address4 } from "@solana/kit";
3
+ import {
4
+ ASSOCIATED_TOKEN_PROGRAM_ID as ASSOCIATED_TOKEN_PROGRAM_ID2,
5
+ getAssociatedTokenAddressSync as getAssociatedTokenAddressSync2,
6
+ TOKEN_PROGRAM_ID
7
+ } from "@solana/spl-token";
8
+ import {
9
+ ComputeBudgetProgram,
10
+ PublicKey as PublicKey3,
11
+ TransactionMessage as TransactionMessage2,
12
+ VersionedTransaction as VersionedTransaction2
13
+ } from "@solana/web3.js";
14
+
15
+ // ../common/src/utils/accounts/ata.ts
16
+ import {
17
+ ASSOCIATED_TOKEN_PROGRAM_ID,
18
+ getAssociatedTokenAddressSync
19
+ } from "@solana/spl-token";
20
+
21
+ // ../common/src/utils/compat.ts
22
+ import {
23
+ AccountRole,
24
+ address
25
+ } from "@solana/kit";
26
+ import {
27
+ PublicKey,
28
+ TransactionInstruction
29
+ } from "@solana/web3.js";
30
+ function toKitInstruction(ix) {
31
+ return {
32
+ programAddress: ix.programId.toBase58(),
33
+ data: new Uint8Array(ix.data),
34
+ accounts: ix.keys.map((key) => ({
35
+ address: key.pubkey.toBase58(),
36
+ role: key.isWritable ? key.isSigner ? AccountRole.WRITABLE_SIGNER : AccountRole.WRITABLE : key.isSigner ? AccountRole.READONLY_SIGNER : AccountRole.READONLY
37
+ }))
38
+ };
39
+ }
40
+
41
+ // ../common/src/utils/accounts/roles.ts
42
+ import {
43
+ AccountRole as AccountRole2,
44
+ upgradeRoleToSigner,
45
+ upgradeRoleToWritable
46
+ } from "@solana/kit";
47
+
48
+ // ../common/src/utils/cpi/refs.ts
49
+ import {
50
+ upgradeRoleToSigner as upgradeRoleToSigner2,
51
+ upgradeRoleToWritable as upgradeRoleToWritable2
52
+ } from "@solana/kit";
53
+
54
+ // ../common/src/utils/numbers.ts
55
+ function toUiAmount(amount, decimals) {
56
+ const divisor = BigInt(10 ** decimals);
57
+ const wholePart = amount / divisor;
58
+ const remainder = amount % divisor;
59
+ return Number(wholePart) + Number(remainder) / 10 ** decimals;
60
+ }
61
+ function fromUiAmount(amount, decimals) {
62
+ return BigInt(Math.round(amount * 10 ** decimals));
63
+ }
64
+
65
+ // ../common/src/utils/transaction.ts
66
+ import {
67
+ PublicKey as PublicKey2,
68
+ TransactionMessage,
69
+ VersionedTransaction
70
+ } from "@solana/web3.js";
71
+
72
+ // ../common/src/services/mints/constants.ts
73
+ import { address as address2 } from "@solana/kit";
74
+ var SOL_MINT = address2("So11111111111111111111111111111111111111112");
75
+ var WBTC_MINT = address2(
76
+ "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh"
77
+ );
78
+ var CBBTC_MINT = address2(
79
+ "cbbtcf3aa214zXHbiAZQwf4122FBYbraNdFqgw4iMij"
80
+ );
81
+ var WETH_MINT = address2(
82
+ "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs"
83
+ );
84
+ var JLP_MINT = address2("27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4");
85
+ var SPYX_MINT = address2("XsoCS1TfEyfFhfvj8EtZ528L3CaKBDBRqRapnBbDF2W");
86
+ var QQQX_MINT = address2("Xs8S1uUs1zvS2p7iwtsG3b6fkhpvmwz4GYU3gWAmWHZ");
87
+ var USDC_MINT = address2(
88
+ "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
89
+ );
90
+ var USDT_MINT = address2(
91
+ "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
92
+ );
93
+ var USDS_MINT = address2("USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA");
94
+ var USDG_MINT = address2(
95
+ "2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH"
96
+ );
97
+ var PYUSD_MINT = address2(
98
+ "2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo"
99
+ );
100
+ var CASH_MINT = address2(
101
+ "CASHx9KJUStyftLFWGvEVf59SGeG9sh5FfcnZMVPCASH"
102
+ );
103
+ var USD1_MINT = address2("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");
104
+ var JITOSOL_MINT = address2(
105
+ "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn"
106
+ );
107
+ var JUPSOL_MINT = address2(
108
+ "jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v"
109
+ );
110
+ var USD_STAR_MINT = address2(
111
+ "star9agSpjiFe3M49B3RniVU4CMBBEK3Qnaqn3RGiFM"
112
+ );
113
+ var USD_STAR_JUNIOR_MINT = address2(
114
+ "3AJYCjrhKitmBd8tRUurPA5dGHCAQh6A8L9DkuSC37Rm"
115
+ );
116
+ var PST_MINT = address2("59obFNBzyTBGowrkif5uK7ojS58vsuWz3ZCvg6tfZAGw");
117
+ var PRIME_MINT = address2(
118
+ "3b8X44fLF9ooXaUm3hhSgjpmVs6rZZ3pPoGnGahc3Uu7"
119
+ );
120
+ var NEST_RWA_SHARE_MINT = address2(
121
+ "VyXKJnVhkSkB6KAozSZCiLQbuB1k1r6Nu4etNYVo5LJ"
122
+ );
123
+
124
+ // ../common/src/services/mints/defaults.ts
125
+ var DEFAULT_MINTS = [
126
+ {
127
+ address: SOL_MINT,
128
+ symbol: "SOL",
129
+ name: "Solana",
130
+ decimals: 9,
131
+ assetType: "Standard"
132
+ },
133
+ {
134
+ address: USDC_MINT,
135
+ symbol: "USDC",
136
+ name: "USD Coin",
137
+ decimals: 6,
138
+ assetType: "Stablecoin"
139
+ },
140
+ {
141
+ address: USDT_MINT,
142
+ symbol: "USDT",
143
+ name: "Tether USD",
144
+ decimals: 6,
145
+ assetType: "Stablecoin"
146
+ },
147
+ {
148
+ address: USDS_MINT,
149
+ symbol: "USDS",
150
+ decimals: 6,
151
+ assetType: "Stablecoin"
152
+ },
153
+ {
154
+ address: USDG_MINT,
155
+ symbol: "USDG",
156
+ name: "Global Dollar",
157
+ decimals: 6,
158
+ assetType: "Stablecoin",
159
+ isToken2022: true
160
+ },
161
+ {
162
+ address: PYUSD_MINT,
163
+ symbol: "PYUSD",
164
+ name: "PayPal USD",
165
+ decimals: 6,
166
+ assetType: "Stablecoin",
167
+ isToken2022: true
168
+ },
169
+ {
170
+ address: CASH_MINT,
171
+ symbol: "CASH",
172
+ decimals: 6,
173
+ assetType: "Stablecoin",
174
+ isToken2022: true
175
+ },
176
+ {
177
+ address: USD_STAR_MINT,
178
+ symbol: "USD*",
179
+ name: "USD Star",
180
+ decimals: 6,
181
+ assetType: "YBA"
182
+ },
183
+ {
184
+ address: USD_STAR_JUNIOR_MINT,
185
+ symbol: "USD*-J",
186
+ name: "USD Star Junior",
187
+ decimals: 6,
188
+ assetType: "YBA"
189
+ },
190
+ {
191
+ address: PST_MINT,
192
+ symbol: "PST",
193
+ name: "PayFi Strategy Token",
194
+ decimals: 6,
195
+ assetType: "YBA"
196
+ },
197
+ {
198
+ address: PRIME_MINT,
199
+ symbol: "PRIME",
200
+ name: "Hastra PRIME",
201
+ decimals: 6,
202
+ assetType: "YBA"
203
+ },
204
+ {
205
+ address: NEST_RWA_SHARE_MINT,
206
+ symbol: "nPERENA",
207
+ name: "Nest Vault",
208
+ decimals: 6,
209
+ assetType: "YBA"
210
+ }
211
+ ];
212
+
213
+ // ../common/src/services/mints/registry.ts
214
+ import { TOKEN_PROGRAM_ADDRESS } from "@solana-program/token";
215
+ import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022";
216
+ var MintRegistry = class {
217
+ constructor(options = {}) {
218
+ this.byAddress = /* @__PURE__ */ new Map();
219
+ this.bySymbol = /* @__PURE__ */ new Map();
220
+ const { includeDefaults = true, custom = [] } = options;
221
+ if (includeDefaults) {
222
+ for (const config of DEFAULT_MINTS) {
223
+ this.register(config);
224
+ }
225
+ }
226
+ for (const config of custom) {
227
+ this.register(config);
228
+ }
229
+ }
230
+ register(config) {
231
+ const mintInfo = {
232
+ ...config,
233
+ tokenProgram: config.isToken2022 ? TOKEN_2022_PROGRAM_ADDRESS : TOKEN_PROGRAM_ADDRESS,
234
+ toUiAmount: (amount) => toUiAmount(amount, config.decimals),
235
+ fromUiAmount: (amount) => fromUiAmount(amount, config.decimals)
236
+ };
237
+ this.byAddress.set(config.address, mintInfo);
238
+ this.bySymbol.set(config.symbol.toUpperCase(), mintInfo);
239
+ }
240
+ get(mint) {
241
+ const key = mint.toString();
242
+ const bySymbol = this.bySymbol.get(key.toUpperCase());
243
+ if (bySymbol) {
244
+ return bySymbol;
245
+ }
246
+ return this.byAddress.get(key);
247
+ }
248
+ all() {
249
+ return Array.from(this.byAddress.values());
250
+ }
251
+ standardAssets() {
252
+ return this.filterAssetsByType("Standard");
253
+ }
254
+ stablecoins() {
255
+ return this.filterAssetsByType("Stablecoin");
256
+ }
257
+ lsts() {
258
+ return this.filterAssetsByType("LST");
259
+ }
260
+ yieldBearingAssets() {
261
+ return this.filterAssetsByType("YBA");
262
+ }
263
+ filterAssetsByType(assetType) {
264
+ return Array.from(this.byAddress.values()).filter(
265
+ (mint) => mint.assetType === assetType
266
+ );
267
+ }
268
+ symbols() {
269
+ return Array.from(this.bySymbol.keys());
270
+ }
271
+ addresses() {
272
+ return this.all().map((mint) => mint.address);
273
+ }
274
+ };
275
+ var mints = new MintRegistry();
276
+
277
+ // ../common/src/services/baseAccountClient.ts
278
+ import { fetchMaybeToken } from "@solana-program/token";
279
+ var DEFAULT_SPL_ATA_TTL_MS = 6e4 * 10;
280
+
281
+ // ../nest/src/priceApi.ts
282
+ var NEST_API_BASE_URL = "https://api.nest.credit/v1";
283
+
284
+ // ../nest/src/redemptionApi.ts
285
+ var NestApiError = class extends Error {
286
+ constructor(status, message) {
287
+ super(`Nest API (${status}): ${message}`);
288
+ this.status = status;
289
+ this.name = "NestApiError";
290
+ }
291
+ };
292
+ function assertNestShareAmount(amount) {
293
+ if (typeof amount !== "bigint" || amount <= 0n || amount > 0xffffffffffffffffn) {
294
+ throw new Error(
295
+ "Nest share amount must be a positive u64 bigint in base units"
296
+ );
297
+ }
298
+ }
299
+ async function request(path, options, body) {
300
+ const response = await (options.fetchFn ?? fetch)(
301
+ `${(options.baseUrl ?? NEST_API_BASE_URL).replace(/\/$/, "")}${path}`,
302
+ {
303
+ method: body ? "POST" : "GET",
304
+ headers: body ? { "Content-Type": "application/json" } : void 0,
305
+ body: body ? JSON.stringify(body) : void 0,
306
+ signal: options.signal
307
+ }
308
+ );
309
+ const payload = await response.json().catch(() => null);
310
+ if (!response.ok) {
311
+ throw new NestApiError(
312
+ response.status,
313
+ typeof payload?.error === "string" ? payload.error : response.statusText
314
+ );
315
+ }
316
+ if (!payload || typeof payload !== "object" || !payload.data) {
317
+ throw new Error("Nest API returned an invalid response");
318
+ }
319
+ return payload.data;
320
+ }
321
+ async function fetchNestRedemptionTransaction(args, options = {}) {
322
+ assertNestShareAmount(args.rawAmountNestToken);
323
+ const data = await request(
324
+ "/solana/nest/async-redeem/request/build-tx",
325
+ options,
326
+ {
327
+ ...args,
328
+ rawAmountNestToken: args.rawAmountNestToken.toString(),
329
+ // Squads must sign both the token authority and LayerZero fee payer at execution.
330
+ feePayer: args.owner
331
+ }
332
+ );
333
+ if (typeof data.txBase64 !== "string" || !data.txBase64.length) {
334
+ throw new Error("Nest API returned no transaction");
335
+ }
336
+ return data.txBase64;
337
+ }
338
+ async function fetchNestRedemptionQuote(args, options = {}) {
339
+ assertNestShareAmount(args.rawAmountNestToken);
340
+ const data = await request("/solana/nest/async-redeem/quote", options, {
341
+ ...args,
342
+ rawAmountNestToken: args.rawAmountNestToken.toString()
343
+ });
344
+ if (data.nestVaultSlug !== args.nestVaultSlug || data.redemptionAsset !== "USDC" || data.shareAmount !== args.rawAmountNestToken.toString() || !Number.isInteger(data.shareDecimals) || data.shareDecimals < 0 || data.shareDecimals > 18 || data.redemptionDecimals !== 6 || typeof data.redemptionAmount !== "string" || typeof data.feeAmount !== "string" || !/^\d+$/.test(data.redemptionAmount) || !/^\d+$/.test(data.feeAmount)) {
345
+ throw new Error("Nest API returned an invalid redemption quote");
346
+ }
347
+ return data;
348
+ }
349
+ async function fetchNestRedemptionStatus(signature, options = {}) {
350
+ if (!/^[1-9A-HJ-NP-Za-km-z]{64,88}$/.test(signature)) {
351
+ throw new Error("Enter a Solana withdrawal execution signature");
352
+ }
353
+ let data;
354
+ try {
355
+ data = await request(
356
+ `/solana/redeem-status/${encodeURIComponent(signature)}`,
357
+ options
358
+ );
359
+ } catch (error) {
360
+ if (error instanceof NestApiError && error.status === 404) return null;
361
+ throw error;
362
+ }
363
+ if (data.solanaBurnSignature !== signature || typeof data.solanaWallet !== "string" || typeof data.overallStatus !== "string" || !["solanaBurn", "plumeProcessing", "cctpAttestation", "solanaClaim"].every(
364
+ (key) => typeof data.steps?.[key]?.status === "string"
365
+ )) {
366
+ throw new Error("Nest API returned an invalid redemption status");
367
+ }
368
+ return data;
369
+ }
370
+
371
+ // src/services/consensusOracle/sources/nestPriceSource.ts
372
+ import { address as address3 } from "@solana/kit";
373
+ var NEST_VAULT_SLUG = "nest-perena-vault";
374
+ var NEST_RWA_SHARE_MINT2 = address3(
375
+ "VyXKJnVhkSkB6KAozSZCiLQbuB1k1r6Nu4etNYVo5LJ"
376
+ );
377
+
378
+ // src/services/nestWithdrawalService.ts
379
+ var NestOftProgram = new PublicKey3(
380
+ "ChEfPd3RzLeYiRwp1K9evimmaFSd6DV1S4Mv5q5Aj1th"
381
+ );
382
+ var NestOftStore = new PublicKey3(
383
+ "k8mJj8Gyw2gFAqut21AFVUUZhxb4RVDMnb4PrSYiDhV"
384
+ );
385
+ var UsdcMint = new PublicKey3("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
386
+ var NestPerenaComposer = Buffer.from(
387
+ "000000000000000000000000908dcb5531691c2124c54e30bb645cf11647090d",
388
+ "hex"
389
+ );
390
+ async function buildNestWithdrawalRequest(args) {
391
+ const txBase64 = await fetchNestRedemptionTransaction(
392
+ {
393
+ owner: args.owner.toBase58(),
394
+ nestVaultSlug: NEST_VAULT_SLUG,
395
+ rawAmountNestToken: args.rawAmountNestToken
396
+ },
397
+ args.apiOptions
398
+ );
399
+ return decodeNestWithdrawalRequest({ ...args, txBase64 });
400
+ }
401
+ async function decodeNestWithdrawalRequest(args) {
402
+ const transaction = VersionedTransaction2.deserialize(
403
+ Buffer.from(args.txBase64, "base64")
404
+ );
405
+ const message = transaction.message;
406
+ if (!message.staticAccountKeys[0].equals(args.owner) || message.header.numRequiredSignatures !== 1) {
407
+ throw new Error(
408
+ "Nest withdrawal must use the Squads vault as its only signer and fee payer"
409
+ );
410
+ }
411
+ const tables = await Promise.all(
412
+ message.addressTableLookups.map(async (lookup) => {
413
+ const { value } = await args.connection.getAddressLookupTable(
414
+ lookup.accountKey
415
+ );
416
+ if (!value)
417
+ throw new Error(
418
+ `Nest lookup table not found: ${lookup.accountKey.toBase58()}`
419
+ );
420
+ return value;
421
+ })
422
+ );
423
+ const decoded = TransactionMessage2.decompile(message, {
424
+ addressLookupTableAccounts: tables
425
+ });
426
+ const nestMint = new PublicKey3(NEST_RWA_SHARE_MINT2);
427
+ const sourceAta = getAssociatedTokenAddressSync2(nestMint, args.owner, true);
428
+ const usdcAta = getAssociatedTokenAddressSync2(UsdcMint, args.owner, true);
429
+ const instructions2 = decoded.instructions.filter(
430
+ (ix) => !ix.programId.equals(ComputeBudgetProgram.programId)
431
+ );
432
+ let sends = 0;
433
+ for (const ix of instructions2) {
434
+ if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID2)) {
435
+ if (ix.data.length !== 1 || ix.data[0] !== 1 || !ix.keys[0]?.pubkey.equals(args.owner) || !ix.keys[1]?.pubkey.equals(usdcAta) || !ix.keys[2]?.pubkey.equals(args.owner) || !ix.keys[3]?.pubkey.equals(UsdcMint) || !ix.keys[5]?.pubkey.equals(TOKEN_PROGRAM_ID)) {
436
+ throw new Error("Unexpected token account creation in Nest withdrawal");
437
+ }
438
+ continue;
439
+ }
440
+ if (!ix.programId.equals(NestOftProgram) || ix.data.length < 52 || !ix.data.subarray(0, 8).equals(Buffer.from([102, 251, 20, 187, 65, 75, 12, 69])) || !ix.data.subarray(12, 44).equals(NestPerenaComposer) || ix.data.readBigUInt64LE(44) !== args.rawAmountNestToken || ![args.owner, nestMint, sourceAta, NestOftStore].every(
441
+ (key) => ix.keys.some((meta) => meta.pubkey.equals(key))
442
+ )) {
443
+ throw new Error(
444
+ "Unexpected instruction or share amount in Nest withdrawal"
445
+ );
446
+ }
447
+ sends += 1;
448
+ }
449
+ if (sends !== 1)
450
+ throw new Error("Nest withdrawal must contain exactly one OFT send");
451
+ return {
452
+ instructions: instructions2.map(toKitInstruction),
453
+ lookupTables: tables.map((table) => address4(table.key.toBase58()))
454
+ };
455
+ }
456
+
457
+ // src/utils/squads.ts
458
+ import * as squads from "@sqds/multisig";
459
+ import {
460
+ PublicKey as PublicKey4,
461
+ Transaction,
462
+ TransactionMessage as TransactionMessage3,
463
+ VersionedTransaction as VersionedTransaction3
464
+ } from "@solana/web3.js";
465
+ var SQUADS_SIMULATION_COMMITMENT = "processed";
466
+ var SquadsProposalExecutionSimulationError = class extends Error {
467
+ constructor(transactionError, logs, unitsConsumed) {
468
+ const programLogs = logs.length > 0 ? `
469
+ ${logs.join("\n")}` : "";
470
+ super(
471
+ `Squads proposal execution simulation failed: ${JSON.stringify(
472
+ transactionError
473
+ )}${programLogs}`
474
+ );
475
+ this.name = "SquadsProposalExecutionSimulationError";
476
+ this.transactionError = transactionError;
477
+ this.logs = [...logs];
478
+ this.unitsConsumed = unitsConsumed;
479
+ }
480
+ };
481
+ function validateVaultIndex(vaultIndex) {
482
+ if (!Number.isSafeInteger(vaultIndex) || vaultIndex < 0) {
483
+ throw new Error("Squads vaultIndex must be a non-negative safe integer");
484
+ }
485
+ }
486
+ function resolveSquadsWalletRoute(route) {
487
+ validateVaultIndex(route.vaultIndex);
488
+ const wallet = new PublicKey4(route.wallet);
489
+ const multisigPda = new PublicKey4(route.multisigPda);
490
+ const [vaultPda] = squads.getVaultPda({
491
+ multisigPda,
492
+ index: route.vaultIndex
493
+ });
494
+ return { ...route, wallet, multisigPda, vaultPda };
495
+ }
496
+ function findSquadsWalletRoute(wallet, routes) {
497
+ const walletKey = typeof wallet === "string" ? new PublicKey4(wallet) : wallet;
498
+ const route = routes.find(
499
+ (candidate) => candidate.wallet === walletKey.toBase58()
500
+ );
501
+ return route ? resolveSquadsWalletRoute(route) : void 0;
502
+ }
503
+ function validateSquadsWalletRoutes(routes) {
504
+ const wallets = /* @__PURE__ */ new Set();
505
+ for (const route of routes) {
506
+ const resolved = resolveSquadsWalletRoute(route);
507
+ const wallet = resolved.wallet.toBase58();
508
+ if (wallets.has(wallet)) {
509
+ throw new Error(`Duplicate Squads route for wallet ${wallet}`);
510
+ }
511
+ wallets.add(wallet);
512
+ }
513
+ }
514
+ function vaultAuthorityForWallet(wallet, routes) {
515
+ const walletKey = typeof wallet === "string" ? new PublicKey4(wallet) : wallet;
516
+ return findSquadsWalletRoute(walletKey, routes)?.vaultPda ?? walletKey;
517
+ }
518
+ async function simulateSquadsProposalExecution(args) {
519
+ const { connection, feePayer, instructions: instructions2 } = args;
520
+ const recentBlockhash = args.recentBlockhash ?? (await connection.getLatestBlockhash(SQUADS_SIMULATION_COMMITMENT)).blockhash;
521
+ const message = new TransactionMessage3({
522
+ payerKey: feePayer,
523
+ recentBlockhash,
524
+ instructions: [...instructions2]
525
+ }).compileToV0Message(args.addressLookupTableAccounts);
526
+ const transaction = new VersionedTransaction3(message);
527
+ const { value } = await connection.simulateTransaction(transaction, {
528
+ sigVerify: false,
529
+ commitment: SQUADS_SIMULATION_COMMITMENT,
530
+ innerInstructions: true
531
+ });
532
+ if (value.err) {
533
+ throw new SquadsProposalExecutionSimulationError(
534
+ value.err,
535
+ value.logs ?? [],
536
+ value.unitsConsumed
537
+ );
538
+ }
539
+ return value;
540
+ }
541
+ async function prepareVaultTransaction(args) {
542
+ const { connection, proposer, instructions: instructions2 } = args;
543
+ const route = findSquadsWalletRoute(proposer, args.squadsRoutes ?? []);
544
+ if (!route) {
545
+ return {
546
+ kind: "direct",
547
+ transaction: new Transaction().add(...instructions2),
548
+ authority: proposer
549
+ };
550
+ }
551
+ const multisig = await squads.accounts.Multisig.fromAccountAddress(
552
+ connection,
553
+ route.multisigPda
554
+ );
555
+ const member = multisig.members.find(
556
+ (candidate) => candidate.key.equals(proposer)
557
+ );
558
+ if (!member) {
559
+ throw new Error(
560
+ `Wallet ${proposer.toBase58()} is not a member of Squads multisig ${route.multisigPda.toBase58()}`
561
+ );
562
+ }
563
+ if (!squads.types.Permissions.has(
564
+ member.permissions,
565
+ squads.types.Permission.Initiate
566
+ )) {
567
+ throw new Error(
568
+ `Wallet ${proposer.toBase58()} cannot initiate proposals for Squads multisig ${route.multisigPda.toBase58()}`
569
+ );
570
+ }
571
+ const transactionIndex = BigInt(multisig.transactionIndex.toString()) + 1n;
572
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
573
+ if (!args.skipSimulation) {
574
+ await simulateSquadsProposalExecution({
575
+ connection,
576
+ feePayer: proposer,
577
+ instructions: instructions2,
578
+ recentBlockhash: blockhash,
579
+ addressLookupTableAccounts: args.addressLookupTableAccounts
580
+ });
581
+ }
582
+ const transactionMessage = new TransactionMessage3({
583
+ payerKey: route.vaultPda,
584
+ recentBlockhash: blockhash,
585
+ instructions: [...instructions2]
586
+ });
587
+ const createVaultTransactionIx = squads.instructions.vaultTransactionCreate({
588
+ multisigPda: route.multisigPda,
589
+ transactionIndex,
590
+ creator: proposer,
591
+ vaultIndex: route.vaultIndex,
592
+ ephemeralSigners: 0,
593
+ transactionMessage,
594
+ addressLookupTableAccounts: args.addressLookupTableAccounts,
595
+ memo: route.memo
596
+ });
597
+ const createProposalIx = squads.instructions.proposalCreate({
598
+ multisigPda: route.multisigPda,
599
+ transactionIndex,
600
+ creator: proposer
601
+ });
602
+ const [proposalPda] = squads.getProposalPda({
603
+ multisigPda: route.multisigPda,
604
+ transactionIndex
605
+ });
606
+ const [vaultTransactionPda] = squads.getTransactionPda({
607
+ multisigPda: route.multisigPda,
608
+ index: transactionIndex
609
+ });
610
+ return {
611
+ kind: "squads",
612
+ transaction: new Transaction().add(
613
+ createVaultTransactionIx,
614
+ createProposalIx
615
+ ),
616
+ authority: route.vaultPda,
617
+ multisigPda: route.multisigPda,
618
+ vaultIndex: route.vaultIndex,
619
+ transactionIndex,
620
+ proposalPda,
621
+ vaultTransactionPda
622
+ };
623
+ }
624
+
625
+ // src/utils/squadsBuffer.ts
626
+ import * as squads2 from "@sqds/multisig";
627
+ import {
628
+ PublicKey as PublicKey5,
629
+ SystemProgram,
630
+ Transaction as Transaction2
631
+ } from "@solana/web3.js";
632
+ var PacketSize = 1232;
633
+ var BufferChunkSize = 700;
634
+ var MaxBufferSize = 4e3;
635
+ async function prepareSquadsProposalUpload(args) {
636
+ const { connection, proposer, prepared } = args;
637
+ const transaction = prepared.transaction;
638
+ const sizeProbe = new Transaction2({
639
+ feePayer: proposer,
640
+ recentBlockhash: PublicKey5.default.toBase58()
641
+ }).add(...transaction.instructions);
642
+ let fits = false;
643
+ try {
644
+ fits = sizeProbe.serialize({
645
+ requireAllSignatures: false,
646
+ verifySignatures: false
647
+ }).length <= PacketSize;
648
+ } catch {
649
+ }
650
+ if (fits) return { setupTransactions: [], proposalTransaction: transaction };
651
+ const [decoded] = squads2.generated.vaultTransactionCreateStruct.deserialize(
652
+ transaction.instructions[0].data
653
+ );
654
+ const bytes = decoded.args.transactionMessage;
655
+ if (bytes.length > MaxBufferSize)
656
+ throw new Error("Squads proposal exceeds the 4,000-byte message limit");
657
+ let bufferPda;
658
+ let bufferIndex = 0;
659
+ for (; bufferIndex <= 255; bufferIndex++) {
660
+ const [candidate] = PublicKey5.findProgramAddressSync(
661
+ [
662
+ Buffer.from("multisig"),
663
+ prepared.multisigPda.toBuffer(),
664
+ Buffer.from("transaction_buffer"),
665
+ proposer.toBuffer(),
666
+ Buffer.from([bufferIndex])
667
+ ],
668
+ squads2.PROGRAM_ID
669
+ );
670
+ if (!await connection.getAccountInfo(candidate, "confirmed")) {
671
+ bufferPda = candidate;
672
+ break;
673
+ }
674
+ }
675
+ if (!bufferPda)
676
+ throw new Error(
677
+ "All Squads upload buffers are occupied; close an old upload first"
678
+ );
679
+ const finalBufferHash = Array.from(
680
+ new Uint8Array(await crypto.subtle.digest("SHA-256", new Uint8Array(bytes)))
681
+ );
682
+ const accounts2 = {
683
+ multisig: prepared.multisigPda,
684
+ transactionBuffer: bufferPda,
685
+ creator: proposer
686
+ };
687
+ const setupTransactions = [
688
+ new Transaction2().add(
689
+ squads2.generated.createTransactionBufferCreateInstruction(
690
+ {
691
+ ...accounts2,
692
+ rentPayer: proposer
693
+ },
694
+ {
695
+ args: {
696
+ bufferIndex,
697
+ vaultIndex: prepared.vaultIndex,
698
+ finalBufferHash,
699
+ finalBufferSize: bytes.length,
700
+ buffer: bytes.slice(0, BufferChunkSize)
701
+ }
702
+ }
703
+ )
704
+ )
705
+ ];
706
+ for (let offset = BufferChunkSize; offset < bytes.length; offset += BufferChunkSize) {
707
+ setupTransactions.push(
708
+ new Transaction2().add(
709
+ squads2.generated.createTransactionBufferExtendInstruction(accounts2, {
710
+ args: { buffer: bytes.slice(offset, offset + BufferChunkSize) }
711
+ })
712
+ )
713
+ );
714
+ }
715
+ const finalize = squads2.generated.createVaultTransactionCreateFromBufferInstruction(
716
+ {
717
+ vaultTransactionCreateItemMultisig: prepared.multisigPda,
718
+ vaultTransactionCreateItemTransaction: prepared.vaultTransactionPda,
719
+ vaultTransactionCreateItemCreator: proposer,
720
+ vaultTransactionCreateItemRentPayer: proposer,
721
+ vaultTransactionCreateItemSystemProgram: SystemProgram.programId,
722
+ transactionBuffer: bufferPda,
723
+ creator: proposer
724
+ },
725
+ { args: { ...decoded.args, transactionMessage: new Uint8Array(6) } }
726
+ );
727
+ const closeBufferInstruction = squads2.generated.createTransactionBufferCloseInstruction(accounts2);
728
+ closeBufferInstruction.keys.find(
729
+ (key) => key.pubkey.equals(proposer)
730
+ ).isWritable = true;
731
+ return {
732
+ setupTransactions,
733
+ proposalTransaction: new Transaction2().add(
734
+ finalize,
735
+ ...transaction.instructions.slice(1)
736
+ ),
737
+ bufferPda,
738
+ closeBufferInstruction
739
+ };
740
+ }
741
+ export {
742
+ NEST_RWA_SHARE_MINT2 as NEST_RWA_SHARE_MINT,
743
+ NEST_VAULT_SLUG,
744
+ NestApiError,
745
+ SquadsProposalExecutionSimulationError,
746
+ buildNestWithdrawalRequest,
747
+ decodeNestWithdrawalRequest,
748
+ fetchNestRedemptionQuote,
749
+ fetchNestRedemptionStatus,
750
+ findSquadsWalletRoute,
751
+ prepareSquadsProposalUpload,
752
+ prepareVaultTransaction,
753
+ resolveSquadsWalletRoute,
754
+ simulateSquadsProposalExecution,
755
+ validateSquadsWalletRoutes,
756
+ vaultAuthorityForWallet
757
+ };