@wireio/stake 0.7.3 → 0.9.1

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.
@@ -1,4 +1,4 @@
1
- import { AnchorProvider, BN, Program } from '@coral-xyz/anchor';
1
+ import { AnchorProvider, BN, Program, Wallet } from '@coral-xyz/anchor';
2
2
  import {
3
3
  SystemProgram,
4
4
  Transaction,
@@ -8,12 +8,19 @@ import {
8
8
  SYSVAR_CLOCK_PUBKEY,
9
9
  SYSVAR_RENT_PUBKEY,
10
10
  SYSVAR_STAKE_HISTORY_PUBKEY,
11
+ Connection,
12
+ PublicKey,
13
+ TransactionMessage,
14
+ LAMPORTS_PER_SOL,
15
+ Signer,
11
16
  } from '@solana/web3.js';
17
+
12
18
  import {
13
19
  TOKEN_2022_PROGRAM_ID,
14
20
  ASSOCIATED_TOKEN_PROGRAM_ID,
15
21
  getAssociatedTokenAddressSync,
16
22
  } from '@solana/spl-token';
23
+ import * as multisig from "@sqds/multisig";
17
24
 
18
25
  import { SolanaProgramService } from '../program';
19
26
  import type { LiqsolCore } from '../../../assets/solana/types/liqsol_core';
@@ -47,6 +54,10 @@ import { GlobalAccount, WalletLike } from '../types';
47
54
  export class DepositClient {
48
55
  private program: Program<LiqsolCore>;
49
56
 
57
+ get connection() {
58
+ return this.provider.connection;
59
+ }
60
+
50
61
  get wallet(): WalletLike {
51
62
  return this.provider.wallet;
52
63
  }
@@ -63,7 +74,7 @@ export class DepositClient {
63
74
  async buildDepositTx(
64
75
  amount: bigint,
65
76
  user = this.wallet.publicKey,
66
- ): Promise<Transaction> {
77
+ ): Promise<TransactionInstruction> {
67
78
  if (!user) {
68
79
  throw new Error(
69
80
  'DepositClient.buildDepositTx: wallet not connected',
@@ -95,7 +106,7 @@ export class DepositClient {
95
106
  const userAta = getAssociatedTokenAddressSync(
96
107
  liqsolMint,
97
108
  user,
98
- false,
109
+ true,
99
110
  TOKEN_2022_PROGRAM_ID,
100
111
  );
101
112
 
@@ -121,7 +132,7 @@ export class DepositClient {
121
132
  // -------------------------------------------------------------
122
133
  // BUILD IX (MUST MATCH IDL)
123
134
  // -------------------------------------------------------------
124
- const ix: TransactionInstruction = await this.program.methods
135
+ return await this.program.methods
125
136
  .deposit(new BN(amount.toString()), seed)
126
137
  .accounts({
127
138
  user,
@@ -151,10 +162,130 @@ export class DepositClient {
151
162
  globalConfig
152
163
  })
153
164
  .instruction();
165
+ }
154
166
 
155
- return new Transaction().add(ix);
167
+
168
+
169
+ async buildSquadsDepositProposalTx(params: {
170
+ connection: Connection;
171
+ multisigPda: PublicKey;
172
+ amountLamports: bigint;
173
+ wallet: WalletLikeSigner; // your WalletLikeSigner type
174
+ vaultIndex?: number;
175
+ }): Promise<{
176
+ tx: Transaction;
177
+ transactionIndex: bigint;
178
+ vaultPda: PublicKey;
179
+ }> {
180
+ const { connection, multisigPda, amountLamports, wallet } = params;
181
+ const vaultIndex = params.vaultIndex ?? 0;
182
+
183
+ if (!wallet?.publicKey) throw new Error("wallet.publicKey missing");
184
+ if (!amountLamports || amountLamports <= BigInt(0)) throw new Error("amountLamports must be > 0");
185
+
186
+ // 1) vault PDA
187
+ const [vaultPda] = multisig.getVaultPda({ multisigPda, index: vaultIndex });
188
+
189
+ // 2) build deposit ix where `user` = vaultPda (off-curve OK)
190
+ const depositBuilt = await this.buildDepositIxForUser(amountLamports, vaultPda);
191
+
192
+ // 3) compute next transactionIndex (best-effort; see note below)
193
+ const ms = await multisig.accounts.Multisig.fromAccountAddress(connection, multisigPda);
194
+ const current = BigInt((ms as any).transactionIndex?.toString?.() ?? 0);
195
+ const transactionIndex = current + BigInt(1);
196
+
197
+ // 4) inner message uses vault as payer
198
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
199
+ const message = new TransactionMessage({
200
+ payerKey: vaultPda,
201
+ recentBlockhash: blockhash,
202
+ instructions: [depositBuilt.ix],
203
+ });
204
+
205
+ // 5) squads instructions (no rpc.*)
206
+ const createVaultTxIx = await multisig.instructions.vaultTransactionCreate({
207
+ multisigPda,
208
+ transactionIndex,
209
+ creator: wallet.publicKey,
210
+ vaultIndex,
211
+ ephemeralSigners: 0,
212
+ transactionMessage: message,
213
+ });
214
+
215
+ // const createProposalIx = await multisig.instructions.proposalCreate({
216
+ // multisigPda,
217
+ // transactionIndex,
218
+ // creator: wallet.publicKey,
219
+ // });
220
+ console.log('createVaultTxIx', createVaultTxIx);
221
+
222
+
223
+ // 6) return a normal Transaction for your pipeline
224
+ const tx = new Transaction().add(createVaultTxIx);
225
+
226
+ return { tx, transactionIndex, vaultPda };
227
+ }
228
+
229
+ async buildSquadsDepositProposalTx2(params: {
230
+ connection: Connection;
231
+ multisigPda: PublicKey;
232
+ amountLamports: bigint;
233
+ wallet: WalletLikeSigner; // your WalletLikeSigner type
234
+ vaultIndex?: number;
235
+ }): Promise<{
236
+ tx: Transaction;
237
+ transactionIndex: bigint;
238
+ vaultPda: PublicKey;
239
+ }> {
240
+ const { connection, multisigPda, amountLamports, wallet } = params;
241
+ const vaultIndex = params.vaultIndex ?? 0;
242
+
243
+ if (!wallet?.publicKey) throw new Error("wallet.publicKey missing");
244
+ if (!amountLamports || amountLamports <= BigInt(0)) throw new Error("amountLamports must be > 0");
245
+
246
+ // 1) vault PDA
247
+ const [vaultPda] = multisig.getVaultPda({ multisigPda, index: vaultIndex });
248
+
249
+ // 2) build deposit ix where `user` = vaultPda (off-curve OK)
250
+ const depositBuilt = await this.buildDepositIxForUser(amountLamports, vaultPda);
251
+
252
+ // 3) compute next transactionIndex (best-effort; see note below)
253
+ const ms = await multisig.accounts.Multisig.fromAccountAddress(connection, multisigPda);
254
+ const current = BigInt(ms.transactionIndex?.toString?.() ?? 0);
255
+ const transactionIndex = current + BigInt(1);
256
+
257
+ // 4) inner message uses vault as payer
258
+ const { blockhash } = await connection.getLatestBlockhash("confirmed");
259
+ const message = new TransactionMessage({
260
+ payerKey: vaultPda,
261
+ recentBlockhash: blockhash,
262
+ instructions: [depositBuilt.ix],
263
+ });
264
+
265
+ // 5) squads instructions (no rpc.*)
266
+ // const createVaultTxIx = await multisig.instructions.vaultTransactionCreate({
267
+ // multisigPda,
268
+ // transactionIndex,
269
+ // creator: wallet.publicKey,
270
+ // vaultIndex,
271
+ // ephemeralSigners: 0,
272
+ // transactionMessage: message,
273
+ // });
274
+
275
+ const createProposalIx = await multisig.instructions.proposalCreate({
276
+ multisigPda,
277
+ transactionIndex,
278
+ creator: wallet.publicKey,
279
+ });
280
+
281
+ // 6) return a normal Transaction for your pipeline
282
+ const tx = new Transaction().add(createProposalIx);
283
+
284
+ return { tx, transactionIndex, vaultPda };
156
285
  }
157
286
 
287
+
288
+
158
289
  /**
159
290
  * Build a withdraw-request transaction:
160
291
  * liqSOL -> SOL via liqsol_core::requestWithdraw.
@@ -187,7 +318,7 @@ export class DepositClient {
187
318
  const userAta = getAssociatedTokenAddressSync(
188
319
  liqsolMint,
189
320
  user,
190
- false,
321
+ true,
191
322
  TOKEN_2022_PROGRAM_ID,
192
323
  );
193
324
 
@@ -208,7 +339,7 @@ export class DepositClient {
208
339
  // -------------------------------------------------------------
209
340
  // Need nextReceiptId from withdraw global state
210
341
  // -------------------------------------------------------------
211
- const globalAcct : GlobalAccount = await this.program.account.global.fetch(global);
342
+ const globalAcct: GlobalAccount = await this.program.account.global.fetch(global);
212
343
 
213
344
  const rawId = globalAcct.nextReceiptId;
214
345
  let receiptId: bigint;
@@ -241,7 +372,7 @@ export class DepositClient {
241
372
  const nftAta = getAssociatedTokenAddressSync(
242
373
  nftMint,
243
374
  owner,
244
- false,
375
+ true,
245
376
  TOKEN_2022_PROGRAM_ID,
246
377
  );
247
378
 
@@ -289,4 +420,124 @@ export class DepositClient {
289
420
 
290
421
  return new Transaction().add(ix);
291
422
  }
292
- }
423
+
424
+
425
+
426
+
427
+
428
+ /**
429
+ * Internal helper: build the liqsol_core::deposit instruction for a specific `user`.
430
+ * This is the exact same account wiring as buildDepositTx(), but returns the IX
431
+ * plus useful derived addresses/seed for Squads flows.
432
+ */
433
+ private async buildDepositIxForUser(
434
+ amount: bigint,
435
+ user: PublicKey,
436
+ ): Promise<{
437
+ ix: TransactionInstruction;
438
+ seed: number;
439
+ userAta: PublicKey;
440
+ ephemeralStake: PublicKey;
441
+ }> {
442
+ if (!user) {
443
+ throw new Error("buildDepositIxForUser: user is required");
444
+ }
445
+ if (!amount || amount <= BigInt(0)) {
446
+ throw new Error("buildDepositIxForUser: amount must be > 0");
447
+ }
448
+
449
+ // -------------------------------------------------------------
450
+ // PDAs
451
+ // -------------------------------------------------------------
452
+ const depositAuthority = deriveDepositAuthorityPda();
453
+ const liqsolMint = deriveLiqsolMintPda();
454
+ const liqsolMintAuthority = deriveLiqsolMintAuthorityPda();
455
+ const reservePool = deriveReservePoolPda();
456
+ const vault = deriveVaultPda();
457
+ const controllerState = deriveStakeControllerStatePda();
458
+ const payoutState = derivePayoutStatePda();
459
+ const bucketAuthority = deriveBucketAuthorityPda();
460
+ const payRateHistory = derivePayRateHistoryPda();
461
+ const globalConfig = deriveGlobalConfigPda();
462
+
463
+ // -------------------------------------------------------------
464
+ // Token-2022 ATAs
465
+ // -------------------------------------------------------------
466
+ const userAta = getAssociatedTokenAddressSync(
467
+ liqsolMint,
468
+ user,
469
+ true,
470
+ TOKEN_2022_PROGRAM_ID,
471
+ );
472
+
473
+ // -------------------------------------------------------------
474
+ // Distribution state + user_record (KEYED BY TOKEN ACCOUNT)
475
+ // -------------------------------------------------------------
476
+ const distributionState = deriveDistributionStatePda();
477
+ const userRecord = deriveUserRecordPda(userAta);
478
+
479
+ const bucketTokenAccount = getAssociatedTokenAddressSync(
480
+ liqsolMint,
481
+ bucketAuthority,
482
+ true,
483
+ TOKEN_2022_PROGRAM_ID,
484
+ );
485
+
486
+ // -------------------------------------------------------------
487
+ // Ephemeral stake
488
+ // -------------------------------------------------------------
489
+ const seed = Math.floor(Math.random() * 2 ** 32);
490
+ const ephemeralStake = await deriveEphemeralStakeAddress(user, seed);
491
+
492
+ // -------------------------------------------------------------
493
+ // BUILD IX (MUST MATCH IDL)
494
+ // -------------------------------------------------------------
495
+ const ix: TransactionInstruction = await this.program.methods
496
+ .deposit(new BN(amount.toString()), seed)
497
+ .accounts({
498
+ user,
499
+ depositAuthority,
500
+ systemProgram: SystemProgram.programId,
501
+ tokenProgram: TOKEN_2022_PROGRAM_ID,
502
+ associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
503
+ liqsolProgram: PROGRAM_IDS.LIQSOL_TOKEN,
504
+ stakeProgram: StakeProgram.programId,
505
+ liqsolMint,
506
+ userAta,
507
+ liqsolMintAuthority,
508
+ reservePool,
509
+ vault,
510
+ ephemeralStake,
511
+ controllerState,
512
+ payoutState,
513
+ bucketAuthority,
514
+ bucketTokenAccount,
515
+ userRecord,
516
+ distributionState,
517
+ payRateHistory,
518
+ instructionsSysvar: SYSVAR_INSTRUCTIONS_PUBKEY,
519
+ clock: SYSVAR_CLOCK_PUBKEY,
520
+ stakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY,
521
+ rent: SYSVAR_RENT_PUBKEY,
522
+ globalConfig,
523
+ })
524
+ .instruction();
525
+
526
+ return { ix, seed, userAta, ephemeralStake };
527
+ }
528
+
529
+ }
530
+
531
+ // A “wallet-adapter-like” shape (AnchorProvider.wallet matches this)
532
+ type WalletLikeSigner = {
533
+ publicKey: PublicKey;
534
+ signTransaction: (tx: Transaction) => Promise<Transaction>;
535
+ };
536
+
537
+ function isKeypairSigner(x: any): x is { publicKey: PublicKey; secretKey: Uint8Array } {
538
+ return !!x && x.publicKey instanceof PublicKey && x.secretKey instanceof Uint8Array;
539
+ }
540
+
541
+ function isWalletLikeSigner(x: any): x is WalletLikeSigner {
542
+ return !!x && x.publicKey instanceof PublicKey && typeof x.signTransaction === "function";
543
+ }
@@ -106,7 +106,7 @@ export class DistributionClient {
106
106
  const ata = getAssociatedTokenAddressSync(
107
107
  liqsolMint,
108
108
  ownerOrAta,
109
- false,
109
+ true,
110
110
  TOKEN_2022_PROGRAM_ID,
111
111
  );
112
112
  const pdaFromWallet = deriveUserRecordPda(ata);
@@ -277,7 +277,7 @@ export class OutpostClient {
277
277
  * if remainder > 0 → shares + 1
278
278
  */
279
279
  static tokensToShares(amount: BN, currentIndex: BN): BN {
280
- const numerator = amount.mul(INDEX_SCALE);
280
+ const numerator = amount.mul(new BN(INDEX_SCALE));
281
281
  const shares = numerator.div(currentIndex);
282
282
  const remainder = numerator.mod(currentIndex);
283
283
  return remainder.eqn(0) ? shares : shares.addn(1);
@@ -355,7 +355,7 @@ export const deriveEphemeralStakeAddress = async (
355
355
  * Constant keys (Chainlink)
356
356
  */
357
357
  export const CHAINLINK_FEED = new PublicKey(
358
- '99B2bTijsU6f1GCT73HmdR7HCFFjGMBcPZY6jZ96ynrR',
358
+ 'CH31Xns5z3M1cTAbKW34jcxPPciazARpijcHj9rxtemt',
359
359
  );
360
360
  export const CHAINLINK_PROGRAM = new PublicKey(
361
361
  'HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny',