@wireio/stake 0.3.1 → 0.4.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.
Files changed (36) hide show
  1. package/lib/stake.browser.js +12887 -10017
  2. package/lib/stake.browser.js.map +1 -1
  3. package/lib/stake.d.ts +3305 -1364
  4. package/lib/stake.js +16298 -13436
  5. package/lib/stake.js.map +1 -1
  6. package/lib/stake.m.js +12887 -10017
  7. package/lib/stake.m.js.map +1 -1
  8. package/package.json +3 -1
  9. package/src/assets/solana/idl/liqsol_core.json +2327 -887
  10. package/src/assets/solana/idl/liqsol_token.json +1 -1
  11. package/src/assets/solana/idl/transfer_hook.json +192 -0
  12. package/src/assets/solana/idl/validator_leaderboard.json +147 -4
  13. package/src/assets/solana/types/liqsol_core.ts +2327 -887
  14. package/src/assets/solana/types/liqsol_token.ts +1 -1
  15. package/src/assets/solana/types/transfer_hook.ts +198 -0
  16. package/src/assets/solana/types/validator_leaderboard.ts +147 -4
  17. package/src/networks/ethereum/clients/{deposit.client.ts → convert.client.ts} +36 -4
  18. package/src/networks/ethereum/clients/opp.client.ts +390 -0
  19. package/src/networks/ethereum/clients/pretoken.client.ts +88 -49
  20. package/src/networks/ethereum/clients/receipt.client.ts +129 -0
  21. package/src/networks/ethereum/clients/stake.client.ts +1 -148
  22. package/src/networks/ethereum/contract.ts +7 -4
  23. package/src/networks/ethereum/ethereum.ts +44 -70
  24. package/src/networks/ethereum/types.ts +1 -0
  25. package/src/networks/ethereum/utils.ts +1 -1
  26. package/src/networks/solana/clients/deposit.client.ts +154 -8
  27. package/src/networks/solana/clients/distribution.client.ts +72 -291
  28. package/src/networks/solana/clients/leaderboard.client.ts +59 -14
  29. package/src/networks/solana/clients/outpost.client.ts +188 -359
  30. package/src/networks/solana/clients/token.client.ts +85 -100
  31. package/src/networks/solana/constants.ts +155 -64
  32. package/src/networks/solana/solana.ts +273 -154
  33. package/src/networks/solana/types.ts +532 -71
  34. package/src/networks/solana/utils.ts +68 -51
  35. package/src/types.ts +161 -17
  36. package/src/networks/ethereum/clients/liq.client.ts +0 -47
@@ -1,11 +1,10 @@
1
1
  import {
2
2
  Commitment,
3
+ ComputeBudgetProgram,
3
4
  Connection,
4
5
  ConnectionConfig,
5
- LAMPORTS_PER_SOL,
6
6
  PublicKey as SolPubKey,
7
7
  Transaction,
8
- TransactionInstruction,
9
8
  TransactionSignature,
10
9
  } from '@solana/web3.js';
11
10
  import { AnchorProvider } from '@coral-xyz/anchor';
@@ -41,6 +40,7 @@ import {
41
40
  deriveLiqsolMintPda,
42
41
  deriveReservePoolPda,
43
42
  deriveVaultPda,
43
+ INDEX_SCALE,
44
44
  } from './constants';
45
45
 
46
46
  import { buildSolanaTrancheSnapshot } from './utils';
@@ -52,23 +52,23 @@ const commitment: Commitment = 'confirmed';
52
52
  * Solana implementation of IStakingClient.
53
53
  *
54
54
  * Responsibilities:
55
- * - Wire together liqSOL deposit/withdraw
56
- * - Outpost stake/unstake
57
- * - Prelaunch WIRE (pretokens) buy flows
58
- * - Unified portfolio + tranche snapshot + buy quotes
55
+ * - liqSOL deposit (SOL -> liqSOL)
56
+ * - withdraw requests (liqSOL -> NFT receipt / encumbered SOL)
57
+ * - Pretoken (WIRE) buys via liqSOL
58
+ * - Unified portfolio + tranche snapshot + balance correction (later)
59
59
  *
60
- * This class composes lower-level clients; it does not know about UI.
60
+ * This composes lower-level clients; it does not know about UI.
61
61
  */
62
62
  export class SolanaStakingClient implements IStakingClient {
63
- public pubKey?: PublicKey;
63
+ public pubKey?: PublicKey; // Wire ED key (optional → read-only)
64
64
  public connection: Connection;
65
65
  public anchor: AnchorProvider;
66
66
 
67
- private depositClient: DepositClient;
68
- private distributionClient: DistributionClient;
69
- private leaderboardClient: LeaderboardClient;
70
- private outpostClient: OutpostClient;
71
- private tokenClient: TokenClient;
67
+ public depositClient: DepositClient;
68
+ public distributionClient: DistributionClient;
69
+ public leaderboardClient: LeaderboardClient;
70
+ public outpostClient: OutpostClient;
71
+ public tokenClient: TokenClient;
72
72
 
73
73
  get solPubKey(): SolPubKey {
74
74
  if (!this.pubKey) throw new Error('pubKey is undefined');
@@ -80,20 +80,53 @@ export class SolanaStakingClient implements IStakingClient {
80
80
  }
81
81
 
82
82
  constructor(private config: StakerConfig) {
83
- const adapter = config.provider as BaseSignerWalletAdapter;
84
- // if (!adapter?.publicKey) throw new Error('Solana wallet adapter not connected');
85
- if (!config.network.rpcUrls.length) throw new Error('No RPC URLs provided');
86
-
87
- const publicKey = adapter.publicKey || new SolPubKey(new Uint8Array(32))
88
- if (config.pubKey){
89
- // If pubKey provided, ensure it matches the adapter's pubkey
90
- const wirePub = new PublicKey(KeyType.ED, publicKey.toBytes());
91
- if (!wirePub.equals(config.pubKey))
92
- throw new Error("Passed-in pubKey doesn't match adapter.publicKey");
93
- // All good; assign it
83
+ const adapter = config.provider as BaseSignerWalletAdapter | undefined;
84
+
85
+ if (!config.network.rpcUrls.length) {
86
+ throw new Error('No RPC URLs provided');
87
+ }
88
+
89
+ // -------------------------------------------------------------
90
+ // Resolve Solana wallet pubkey (or dummy in read-only mode)
91
+ // -------------------------------------------------------------
92
+ let solWalletPubkey: SolPubKey;
93
+ if (adapter?.publicKey) {
94
+ solWalletPubkey = adapter.publicKey;
95
+ } else {
96
+ // Zeroed pubkey when no wallet (read-only usage)
97
+ solWalletPubkey = new SolPubKey(new Uint8Array(32));
98
+ }
99
+
100
+ // -------------------------------------------------------------
101
+ // Resolve Wire pubKey
102
+ // -------------------------------------------------------------
103
+ if (config.pubKey) {
104
+ const wirePub = config.pubKey;
105
+
106
+ if (adapter?.publicKey) {
107
+ const derived = new PublicKey(
108
+ KeyType.ED,
109
+ adapter.publicKey.toBytes(),
110
+ );
111
+ if (!derived.equals(wirePub)) {
112
+ throw new Error(
113
+ "Passed-in pubKey doesn't match adapter.publicKey",
114
+ );
115
+ }
116
+ }
117
+
94
118
  this.pubKey = wirePub;
119
+ } else if (adapter?.publicKey) {
120
+ // Derive Wire pubKey from adapter when not explicitly passed
121
+ this.pubKey = new PublicKey(
122
+ KeyType.ED,
123
+ adapter.publicKey.toBytes(),
124
+ );
95
125
  }
96
126
 
127
+ // -------------------------------------------------------------
128
+ // Connection + AnchorProvider
129
+ // -------------------------------------------------------------
97
130
  const opts: ConnectionConfig = { commitment };
98
131
  if (
99
132
  config.network.rpcUrls.length > 1 &&
@@ -102,20 +135,47 @@ export class SolanaStakingClient implements IStakingClient {
102
135
  opts.wsEndpoint = config.network.rpcUrls[1];
103
136
  }
104
137
 
105
- const anchorWallet = {
106
- publicKey,
107
- async signTransaction<T extends SolanaTransaction>(tx: T): Promise<T> {
108
- return adapter.signTransaction(tx);
109
- },
110
- async signAllTransactions<T extends SolanaTransaction>(
111
- txs: T[],
112
- ): Promise<T[]> {
113
- return Promise.all(txs.map((tx) => adapter.signTransaction(tx)));
114
- },
115
- };
138
+ const anchorWallet = adapter
139
+ ? {
140
+ publicKey: solWalletPubkey,
141
+ async signTransaction<T extends SolanaTransaction>(
142
+ tx: T,
143
+ ): Promise<T> {
144
+ return adapter.signTransaction(tx);
145
+ },
146
+ async signAllTransactions<T extends SolanaTransaction>(
147
+ txs: T[],
148
+ ): Promise<T[]> {
149
+ if (adapter.signAllTransactions) {
150
+ return adapter.signAllTransactions(txs);
151
+ }
152
+ return Promise.all(
153
+ txs.map((tx) => adapter.signTransaction(tx)),
154
+ );
155
+ },
156
+ }
157
+ : {
158
+ publicKey: solWalletPubkey,
159
+ async signTransaction<T extends SolanaTransaction>(
160
+ _tx: T,
161
+ ): Promise<T> {
162
+ throw new Error(
163
+ 'Wallet not connected: signTransaction not available',
164
+ );
165
+ },
166
+ async signAllTransactions<T extends SolanaTransaction>(
167
+ _txs: T[],
168
+ ): Promise<T[]> {
169
+ throw new Error(
170
+ 'Wallet not connected: signAllTransactions not available',
171
+ );
172
+ },
173
+ };
116
174
 
117
175
  this.connection = new Connection(config.network.rpcUrls[0], opts);
118
- this.anchor = new AnchorProvider(this.connection, anchorWallet, { commitment });
176
+ this.anchor = new AnchorProvider(this.connection, anchorWallet, {
177
+ commitment,
178
+ });
119
179
 
120
180
  this.depositClient = new DepositClient(this.anchor);
121
181
  this.distributionClient = new DistributionClient(this.anchor);
@@ -139,9 +199,8 @@ export class SolanaStakingClient implements IStakingClient {
139
199
  }
140
200
 
141
201
  const tx = await this.depositClient.buildDepositTx(amountLamports);
142
- const { tx: prepared, blockhash, lastValidBlockHeight } = await this.prepareTx(
143
- tx,
144
- );
202
+ const { tx: prepared, blockhash, lastValidBlockHeight } =
203
+ await this.prepareTx(tx);
145
204
  const signed = await this.signTransaction(prepared);
146
205
  return await this.sendAndConfirmHttp(signed, {
147
206
  blockhash,
@@ -150,89 +209,109 @@ export class SolanaStakingClient implements IStakingClient {
150
209
  }
151
210
 
152
211
  /**
153
- * Withdraw SOL from liqSOL protocol.
154
- * NOTE: placeholder until a withdraw flow is implemented in DepositClient.
212
+ * Request a withdraw from liqSOL to SOL (liqsol_core::requestWithdraw).
213
+ *
214
+ * This:
215
+ * - burns liqSOL from the user ATA
216
+ * - mints an NFT withdrawal receipt
217
+ * - increases totalEncumberedFunds in global state
218
+ *
219
+ * Actual SOL payout happens later via the operator-side flow.
155
220
  */
156
- async withdraw(_amountLamports: bigint): Promise<string> {
221
+ async withdraw(amountLamports: bigint): Promise<string> {
157
222
  this.ensureWriteAccess();
158
- throw new Error('Withdraw method not yet implemented.');
223
+ if (amountLamports <= BigInt(0)) {
224
+ throw new Error('Withdraw amount must be greater than zero.');
225
+ }
226
+
227
+ const tx = await this.depositClient.buildWithdrawTx(amountLamports);
228
+ const { tx: prepared, blockhash, lastValidBlockHeight } =
229
+ await this.prepareTx(tx);
230
+ const signed = await this.signTransaction(prepared);
231
+ return await this.sendAndConfirmHttp(signed, {
232
+ blockhash,
233
+ lastValidBlockHeight,
234
+ });
159
235
  }
160
236
 
161
237
  /**
162
- * Stake liqSOL into Outpost (liqSOL -> pool).
163
- * Ensures user ATA exists, then stakes via outpostClient.
238
+ * Stake liqSOL into Outpost (liqSOL pool) via liqsol_core::synd.
164
239
  */
165
240
  async stake(amountLamports: bigint): Promise<string> {
166
241
  this.ensureWriteAccess();
167
- if (amountLamports <= BigInt(0)) {
242
+
243
+ if (!amountLamports || amountLamports <= BigInt(0)) {
168
244
  throw new Error('Stake amount must be greater than zero.');
169
245
  }
170
246
 
171
- const preIxs = await this.outpostClient.maybeBuildCreateUserAtaIx(
172
- this.solPubKey,
173
- );
174
- const stakeIx = await this.outpostClient.buildStakeLiqsolIx(amountLamports);
175
- const tx = new Transaction().add(...preIxs, stakeIx);
247
+ const user = this.solPubKey;
248
+
249
+ // Build the Outpost synd instruction
250
+ const ix = await this.outpostClient.buildStakeIx(amountLamports, user);
176
251
 
252
+ // Wrap in a transaction and send
253
+ const tx = new Transaction().add(ix);
177
254
  const prepared = await this.prepareTx(tx);
178
255
  const signed = await this.signTransaction(prepared.tx);
179
- return await this.sendAndConfirmHttp(signed, prepared);
256
+
257
+ return this.sendAndConfirmHttp(signed, prepared);
180
258
  }
181
259
 
182
260
  /**
183
- * Unstake liqSOL from Outpost (pool -> liqSOL).
184
- * Mirrors stake() but calls withdrawStake.
261
+ * Unstake liqSOL from Outpost (pool liqSOL) via liqsol_core::desynd.
185
262
  */
186
263
  async unstake(amountLamports: bigint): Promise<string> {
187
264
  this.ensureWriteAccess();
188
- if (amountLamports <= BigInt(0)) {
265
+
266
+ if (!amountLamports || amountLamports <= BigInt(0)) {
189
267
  throw new Error('Unstake amount must be greater than zero.');
190
268
  }
191
269
 
192
270
  const user = this.solPubKey;
193
- const preIxs = await this.outpostClient.maybeBuildCreateUserAtaIx(user);
194
- const withdrawIx =
195
- await this.outpostClient.buildWithdrawStakeIx(amountLamports);
196
- const tx = new Transaction().add(...preIxs, withdrawIx);
197
271
 
272
+ // Build the Outpost desynd instruction
273
+ const ix = await this.outpostClient.buildUnstakeIx(amountLamports, user);
274
+
275
+ // Wrap in a transaction and send
276
+ const tx = new Transaction().add(ix);
198
277
  const prepared = await this.prepareTx(tx);
199
278
  const signed = await this.signTransaction(prepared.tx);
200
- return await this.sendAndConfirmHttp(signed, prepared);
279
+
280
+ return this.sendAndConfirmHttp(signed, prepared);
201
281
  }
202
282
 
203
283
  /**
204
- * Buy prelaunch WIRE “pretokens” using a supported asset.
284
+ * Buy prelaunch WIRE “pretokens” using liqSOL.
205
285
  *
206
- * - SOL: uses purchase_with_sol
207
- * - LIQSOL: uses purchase_with_liqsol
208
- * - YIELD: uses purchase_warrants_from_yield
209
- *
210
- * ETH / LIQETH are not valid on Solana.
286
+ * This delegates to TokenClient, which uses the `purchase`-style
287
+ * instruction under the new IDL (no more native-SOL purchase).
211
288
  */
212
289
  async buy(amountLamports: bigint): Promise<string> {
213
290
  this.ensureWriteAccess();
214
- if (!amountLamports || amountLamports <= BigInt(0))
291
+ if (!amountLamports || amountLamports <= BigInt(0)) {
215
292
  throw new Error('liqSOL pretoken purchase requires a positive amount.');
216
-
293
+ }
294
+
217
295
  const user = this.solPubKey;
218
- const preIxs = await this.outpostClient.maybeBuildCreateUserAtaIx(user);
219
- const ix = await this.tokenClient.buildPurchaseWithLiqsolIx(
220
- amountLamports,
221
- user,
222
- );
223
- const tx = new Transaction().add(...preIxs, ix);
224
- const prepared = await this.prepareTx(tx);
225
- const signed = await this.signTransaction(prepared.tx);
226
- return await this.sendAndConfirmHttp(signed, prepared);
296
+ const cuIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 });
297
+ const ix = await this.tokenClient.buildPurchaseIx(amountLamports, user);
298
+ const tx = new Transaction().add(cuIx, ix);
299
+ const { tx: prepared, blockhash, lastValidBlockHeight } =
300
+ await this.prepareTx(tx);
301
+ const signed = await this.signTransaction(prepared);
302
+ return await this.sendAndConfirmHttp(signed, {
303
+ blockhash,
304
+ lastValidBlockHeight,
305
+ });
227
306
  }
228
307
 
229
308
  /**
230
309
  * Aggregate view of the user’s balances on Solana:
231
310
  * - native: SOL wallet balance
232
311
  * - liq: liqSOL token balance (Token-2022 ATA)
233
- * - staked: Outpost-staked liqSOL principal
234
- * - tracked: distribution program trackedBalance (liqSOL)
235
- * - wire: total prelaunch WIRE shares (warrants/pretokens, 1e8)
312
+ * - staked: liqSOL staked in Outpost (synd/desynd) → outpostAccount.stakedLiqsol
313
+ * - wire: total WIRE pretokens purchased (1e8 scale)
314
+ * - yield: on-chain index/shares plus an estimated accrued liqSOL yield
236
315
  * - extras: useful internal addresses and raw state for debugging/UX
237
316
  */
238
317
  async getPortfolio(): Promise<Portfolio> {
@@ -252,75 +331,123 @@ export class SolanaStakingClient implements IStakingClient {
252
331
  ASSOCIATED_TOKEN_PROGRAM_ID,
253
332
  );
254
333
 
255
- const [
256
- nativeLamports,
257
- actualBalResp,
258
- userRecord,
259
- snapshot,
260
- ] = await Promise.all([
334
+ // NOTE:
335
+ // - nativeLamports: wallet SOL
336
+ // - actualBalResp: liqSOL balance in user ATA
337
+ // - snapshot: Outpost + pretokens + global index/shares
338
+ const [nativeLamports, actualBalResp, snapshot] = await Promise.all([
261
339
  this.connection.getBalance(user, 'confirmed'),
262
340
  this.connection
263
341
  .getTokenAccountBalance(userLiqsolAta, 'confirmed')
264
342
  .catch(() => null),
265
- this.distributionClient.getUserRecord(user).catch(() => null),
266
- this.outpostClient.getWireStateSnapshot(user).catch(() => null),
343
+ this.outpostClient.fetchWireState(user).catch(() => null),
267
344
  ]);
268
345
 
269
346
  const LIQSOL_DECIMALS = 9;
270
347
 
271
348
  const actualAmountStr = actualBalResp?.value?.amount ?? '0';
272
- const trackedAmountStr =
273
- userRecord?.trackedBalance?.toString() ?? '0';
274
349
 
275
- const wireReceipt = snapshot?.wireReceipt ?? null;
276
- const userWarrantRecord = snapshot?.userWarrantRecord ?? null;
277
- const trancheState = snapshot?.trancheState ?? null;
278
350
  const globalState = snapshot?.globalState ?? null;
279
-
280
- const stakedAmountStr =
281
- wireReceipt?.stakedLiqsol?.toString() ?? '0';
282
-
283
- const wireSharesStr =
284
- userWarrantRecord?.totalWarrantsPurchased?.toString() ?? '0';
351
+ const outpostAccount = snapshot?.outpostAccount ?? null;
352
+ const trancheState = snapshot?.trancheState ?? null;
353
+ const userPretokenRecord = snapshot?.userPretokenRecord ?? null;
354
+
355
+ // -----------------------------
356
+ // Staked liqSOL (Outpost)
357
+ // -----------------------------
358
+ // This is the liqSOL that has been syndicated into Outpost via `synd`.
359
+ // It lives on the outpost account as `stakedLiqsol`.
360
+ const stakedLiqsolStr =
361
+ outpostAccount?.stakedLiqsol?.toString?.() ?? '0';
362
+
363
+ // -----------------------------
364
+ // WIRE pretokens (1e8 scale)
365
+ // -----------------------------
366
+ // This is NOT stake — it’s the prelaunch WIRE position.
367
+ const wirePretokensStr =
368
+ userPretokenRecord?.totalPretokensPurchased?.toString?.() ??
369
+ '0';
370
+
371
+ // -----------------------------
372
+ // Yield view (index + shares)
373
+ // -----------------------------
374
+ // We expose:
375
+ // - currentIndex: globalState.currentIndex (1e12 scale)
376
+ // - totalShares: globalState.totalShares
377
+ // - userShares: outpostAccount.stakedShares
378
+ // - estimatedClaimLiqsol: floor(userShares * index / INDEX_SCALE)
379
+ // - estimatedYieldLiqsol: max(0, estimatedClaim - stakedLiqsol)
380
+ //
381
+ // This matches the capital-staking math:
382
+ // sharesToTokens(shares, index) = shares * index / INDEX_SCALE
383
+ const currentIndexStr =
384
+ globalState?.currentIndex?.toString?.() ?? '0';
385
+ const totalSharesStr =
386
+ globalState?.totalShares?.toString?.() ?? '0';
387
+ const userSharesStr =
388
+ outpostAccount?.stakedShares?.toString?.() ?? '0';
389
+
390
+ const stakedLiqsol = BigInt(stakedLiqsolStr);
391
+ const currentIndex = BigInt(currentIndexStr);
392
+ const totalShares = BigInt(totalSharesStr);
393
+ const userShares = BigInt(userSharesStr);
394
+
395
+ let estimatedClaimLiqsol = BigInt(0);
396
+ let estimatedYieldLiqsol = BigInt(0);
397
+
398
+ if (userShares > BigInt(0) && currentIndex > BigInt(0)) {
399
+ // sharesToTokens(userShares, currentIndex)
400
+ estimatedClaimLiqsol = (userShares * currentIndex) / INDEX_SCALE;
401
+
402
+ if (estimatedClaimLiqsol > stakedLiqsol) {
403
+ estimatedYieldLiqsol = estimatedClaimLiqsol - stakedLiqsol;
404
+ }
405
+ }
285
406
 
286
407
  return {
287
408
  native: {
288
409
  amount: BigInt(nativeLamports),
289
- symbol: "SOL",
410
+ symbol: 'SOL',
290
411
  decimals: 9,
291
412
  },
292
413
  liq: {
293
414
  amount: BigInt(actualAmountStr),
294
- symbol: "LiqSOL",
415
+ symbol: 'LiqSOL',
295
416
  decimals: LIQSOL_DECIMALS,
296
417
  ata: userLiqsolAta,
297
418
  },
298
419
  staked: {
299
- amount: BigInt(stakedAmountStr),
300
- symbol: "LiqSOL",
420
+ // liqSOL staked in Outpost via `synd`
421
+ amount: stakedLiqsol,
422
+ symbol: 'LiqSOL',
301
423
  decimals: LIQSOL_DECIMALS,
302
424
  },
303
425
  wire: {
304
- // Prelaunch pretokens / WIRE shares (1e8)
305
- amount: BigInt(wireSharesStr),
306
- symbol: "$WIRE",
426
+ // Prelaunch WIRE pretokens (1e8 scale)
427
+ amount: BigInt(wirePretokensStr),
428
+ symbol: '$WIRE',
307
429
  decimals: 8,
308
430
  },
309
- tracked: {
310
- amount: BigInt(trackedAmountStr),
311
- symbol: "LiqSOL",
312
- decimals: LIQSOL_DECIMALS,
431
+ yield: {
432
+ // Raw primitives so the frontend can display curves, charts, etc.
433
+ currentIndex,
434
+ indexScale: INDEX_SCALE,
435
+ totalShares,
436
+ userShares,
437
+ // liqSOL amounts (lamports) implied by index/shares
438
+ estimatedClaimLiqsol,
439
+ estimatedYieldLiqsol,
313
440
  },
314
441
  extras: {
315
442
  userLiqsolAta: userLiqsolAta.toBase58(),
316
443
  reservePoolPDA: reservePoolPDA.toBase58(),
317
444
  vaultPDA: vaultPDA.toBase58(),
318
- wireReceipt,
319
- userWarrantRecord,
320
445
  globalIndex: globalState?.currentIndex?.toString(),
321
446
  totalShares: globalState?.totalShares?.toString(),
322
- currentTrancheNumber: trancheState?.currentTrancheNumber?.toString(),
323
- currentTranchePriceUsd: trancheState?.currentTranchePriceUsd?.toString(), // 1e8 USD
447
+ currentTrancheNumber:
448
+ trancheState?.currentTrancheNumber?.toString(),
449
+ currentTranchePriceUsd:
450
+ trancheState?.currentTranchePriceUsd?.toString(), // 1e8 USD
324
451
  },
325
452
  chainID: this.network.chainId,
326
453
  };
@@ -332,13 +459,9 @@ export class SolanaStakingClient implements IStakingClient {
332
459
  * Uses:
333
460
  * - liqsol_core.globalState (currentIndex, totalShares, etc.)
334
461
  * - liqsol_core.trancheState (price, supply, total sold, etc.)
335
- * - Chainlink/PriceHistory for SOL/USD (via TokenClient.getSolPriceUsdSafe)
462
+ * - PriceHistory/Chainlink SOL/USD via TokenClient.getSolPriceUsdSafe()
336
463
  *
337
- * windowBefore/windowAfter control how many ladder rows we precompute
338
- * around the current tranche for UI, but you can pass nothing if you
339
- * only need current tranche info.
340
- *
341
- * READ-ONLY allowed
464
+ * This is READ-ONLY and works even with no connected wallet.
342
465
  */
343
466
  async getTrancheSnapshot(options?: {
344
467
  chainID?: ChainID;
@@ -351,14 +474,11 @@ export class SolanaStakingClient implements IStakingClient {
351
474
  windowAfter,
352
475
  } = options ?? {};
353
476
 
354
- // Canonical program state
355
477
  const [globalState, trancheState] = await Promise.all([
356
478
  this.tokenClient.fetchGlobalState(),
357
479
  this.tokenClient.fetchTrancheState(),
358
480
  ]);
359
481
 
360
-
361
- // Latest SOL/USD price (1e8) + timestamp from PriceHistory
362
482
  const { price: solPriceUsd, timestamp } =
363
483
  await this.tokenClient.getSolPriceUsdSafe();
364
484
 
@@ -382,29 +502,6 @@ export class SolanaStakingClient implements IStakingClient {
382
502
  return this.distributionClient.getUserRecord(this.solPubKey);
383
503
  }
384
504
 
385
- /**
386
- * Run the "correct & register" flow on Solana:
387
- * - builds the minimal transaction (maybe multi-user) to reconcile liqSOL
388
- * - signs and sends the transaction if it can succeed
389
- */
390
- async correctBalance(amount?: bigint): Promise<string> {
391
- this.ensureWriteAccess();
392
- const build = await this.distributionClient.buildCorrectRegisterTx({ amount });
393
- if (!build.canSucceed || !build.transaction) {
394
- throw new Error(build.reason ?? 'Unable to build Correct&Register transaction');
395
- }
396
-
397
- const { tx, blockhash, lastValidBlockHeight } = await this.prepareTx(
398
- build.transaction,
399
- );
400
- const signed = await this.signTransaction(tx);
401
- const signature = await this.sendAndConfirmHttp(signed, {
402
- blockhash,
403
- lastValidBlockHeight,
404
- });
405
- return signature;
406
- }
407
-
408
505
  // ---------------------------------------------------------------------
409
506
  // Tx helpers
410
507
  // ---------------------------------------------------------------------
@@ -418,7 +515,8 @@ export class SolanaStakingClient implements IStakingClient {
418
515
  ctx: { blockhash: string; lastValidBlockHeight: number },
419
516
  ): Promise<string> {
420
517
  this.ensureWriteAccess();
421
- const signature = await this.connection.sendRawTransaction(
518
+
519
+ const signature = await this.connection.sendRawTransaction(
422
520
  signed.serialize(),
423
521
  {
424
522
  skipPreflight: false,
@@ -437,7 +535,9 @@ export class SolanaStakingClient implements IStakingClient {
437
535
  );
438
536
 
439
537
  if (conf.value.err) {
440
- throw new Error(`Transaction failed: ${JSON.stringify(conf.value.err)}`);
538
+ throw new Error(
539
+ `Transaction failed: ${JSON.stringify(conf.value.err)}`,
540
+ );
441
541
  }
442
542
 
443
543
  return signature;
@@ -446,7 +546,9 @@ export class SolanaStakingClient implements IStakingClient {
446
546
  /**
447
547
  * Sign a single Solana transaction using the connected wallet adapter.
448
548
  */
449
- async signTransaction(tx: SolanaTransaction): Promise<SolanaTransaction> {
549
+ async signTransaction(
550
+ tx: SolanaTransaction,
551
+ ): Promise<SolanaTransaction> {
450
552
  this.ensureWriteAccess();
451
553
  return this.anchor.wallet.signTransaction(tx);
452
554
  }
@@ -455,7 +557,9 @@ export class SolanaStakingClient implements IStakingClient {
455
557
  * Generic "fire and forget" send helper if the caller already
456
558
  * prepared and signed the transaction.
457
559
  */
458
- async sendTransaction(signed: SolanaTransaction): Promise<TransactionSignature> {
560
+ async sendTransaction(
561
+ signed: SolanaTransaction,
562
+ ): Promise<TransactionSignature> {
459
563
  this.ensureWriteAccess();
460
564
  return this.anchor.sendAndConfirm(signed);
461
565
  }
@@ -466,7 +570,11 @@ export class SolanaStakingClient implements IStakingClient {
466
570
  */
467
571
  async prepareTx(
468
572
  tx: Transaction,
469
- ): Promise<{ tx: Transaction; blockhash: string; lastValidBlockHeight: number }> {
573
+ ): Promise<{
574
+ tx: Transaction;
575
+ blockhash: string;
576
+ lastValidBlockHeight: number;
577
+ }> {
470
578
  const { blockhash, lastValidBlockHeight } =
471
579
  await this.connection.getLatestBlockhash('confirmed');
472
580
  tx.recentBlockhash = blockhash;
@@ -474,10 +582,21 @@ export class SolanaStakingClient implements IStakingClient {
474
582
  return { tx, blockhash, lastValidBlockHeight };
475
583
  }
476
584
 
585
+ /**
586
+ * Guard for all write operations (deposit/withdraw/stake/unstake/buy).
587
+ * Ensures we have a Wire pubKey and an Anchor wallet pubKey, and that they match.
588
+ */
477
589
  ensureWriteAccess() {
478
- if (!this.pubKey || !this.anchor.wallet.publicKey)
590
+ if (!this.pubKey || !this.anchor.wallet.publicKey) {
479
591
  throw new Error('User Authorization required: pubKey is undefined');
480
- if (this.solPubKey.toBase58() !== this.anchor.wallet.publicKey.toBase58())
481
- throw new Error('Write access requires connected wallet to match pubKey');
592
+ }
593
+ if (
594
+ this.solPubKey.toBase58() !==
595
+ this.anchor.wallet.publicKey.toBase58()
596
+ ) {
597
+ throw new Error(
598
+ 'Write access requires connected wallet to match pubKey',
599
+ );
600
+ }
482
601
  }
483
602
  }