@wireio/stake 0.4.1 → 0.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wireio/stake",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "LIQ Staking Module for Wire Network",
5
5
  "homepage": "https://gitea.gitgo.app/Wire/sdk-stake",
6
6
  "license": "FSL-1.1-Apache-2.0",
@@ -1,11 +1,11 @@
1
1
  import { BigNumber, ethers } from 'ethers';
2
2
  import { ChainID, EvmChainID, PublicKey as WirePubKey } from '@wireio/core';
3
- import {
3
+ import {
4
4
  IStakingClient,
5
5
  OPPAssertion,
6
- Portfolio,
7
- StakerConfig,
8
- TrancheSnapshot
6
+ Portfolio,
7
+ StakerConfig,
8
+ TrancheSnapshot
9
9
  } from '../../types';
10
10
  import { EthereumContractService } from './contract';
11
11
  import { preLaunchReceipt } from './types';
@@ -16,13 +16,10 @@ import { PretokenClient } from './clients/pretoken.client';
16
16
  import { OPPClient } from './clients/opp.client';
17
17
  import { ReceiptClient } from './clients/receipt.client';
18
18
 
19
-
20
-
21
-
22
19
  export class EthereumStakingClient implements IStakingClient {
20
+ private readonly provider: ethers.providers.Web3Provider | ethers.providers.JsonRpcProvider;
23
21
  public readonly pubKey?: WirePubKey;
24
- private readonly provider: ethers.providers.Web3Provider;
25
- private readonly signer: ethers.Signer;
22
+ private readonly signer?: ethers.Signer;
26
23
  private readonly contractService: EthereumContractService;
27
24
 
28
25
  private convertClient: ConvertClient;
@@ -37,29 +34,32 @@ export class EthereumStakingClient implements IStakingClient {
37
34
 
38
35
  constructor(private config: StakerConfig) {
39
36
  try {
40
- this.provider = config.provider as ethers.providers.Web3Provider;
41
- this.signer = this.provider.getSigner();
37
+ if (config.provider) {
38
+ this.provider = config.provider as ethers.providers.Web3Provider;
39
+ this.signer = this.provider.getSigner();
40
+ }
41
+ else {
42
+ this.provider = new ethers.providers.JsonRpcProvider(config.network.rpcUrls[0]);
43
+ }
42
44
  this.pubKey = config.pubKey;
43
-
45
+
44
46
  this.contractService = new EthereumContractService({
45
47
  provider: this.provider,
46
48
  signer: this.signer,
47
49
  });
48
-
50
+
49
51
  this.convertClient = new ConvertClient(this.contractService);
50
52
  this.pretokenClient = new PretokenClient(this.contractService);
51
53
  this.stakeClient = new StakeClient(this.contractService);
52
54
  this.oppClient = new OPPClient(this.contractService);
53
55
  this.receiptClient = new ReceiptClient(this.contractService);
54
- }
56
+ }
55
57
  catch (error) {
56
58
  // console.error('Error initializing EthereumStakingClient:', error);
57
59
  throw error;
58
- }
60
+ }
59
61
  }
60
62
 
61
-
62
-
63
63
  // ---------------------------------------------------------------------
64
64
  // Public IStakingClient Interface Methods
65
65
  // ---------------------------------------------------------------------
@@ -71,6 +71,8 @@ export class EthereumStakingClient implements IStakingClient {
71
71
  * @returns transaction hash
72
72
  */
73
73
  async deposit(amount: number | string | bigint | BigNumber): Promise<string> {
74
+ this.ensureUser();
75
+
74
76
  const amountWei = BigNumber.isBigNumber(amount)
75
77
  ? amount
76
78
  : BigNumber.from(amount);
@@ -79,15 +81,15 @@ export class EthereumStakingClient implements IStakingClient {
79
81
  return result.txHash;
80
82
  }
81
83
 
82
-
83
-
84
84
  /**
85
85
  * Withdraw native ETH from the liqETH protocol via the liqeth safeBurn function, which burns the LiqETH and adds the user to the withdrawal queue.
86
86
  * @param amount Amount in wei (or something convertible to BigNumber).
87
87
  * @returns transaction hash
88
88
  */
89
89
  async withdraw(amount: bigint): Promise<string> {
90
- const address = await this.signer.getAddress();
90
+ this.ensureUser();
91
+
92
+ const address = await this.signer!.getAddress();
91
93
  const amountWei = BigNumber.from(amount);
92
94
 
93
95
  const result = await this.convertClient.performWithdraw(address, amountWei)
@@ -101,7 +103,9 @@ export class EthereumStakingClient implements IStakingClient {
101
103
  * @returns transaction hash
102
104
  */
103
105
  async stake(amount: bigint): Promise<string> {
104
- const walletAddress = await this.signer.getAddress();
106
+ this.ensureUser();
107
+
108
+ const walletAddress = await this.signer!.getAddress();
105
109
  const amountWei = BigNumber.from(amount);
106
110
 
107
111
  const result = await this.stakeClient.performStake(amountWei, walletAddress);
@@ -122,15 +126,18 @@ export class EthereumStakingClient implements IStakingClient {
122
126
  * @returns the transaction hash
123
127
  */
124
128
  async unstakePrelaunch(tokenId: bigint, recipient: string): Promise<string> {
129
+ this.ensureUser();
130
+
125
131
  const tokenIdBigNum = BigNumber.from(tokenId)
126
132
  const result = await this.stakeClient.performWithdrawStake(tokenIdBigNum, recipient);
127
133
  return result.txHash;
128
134
  }
129
135
 
130
136
 
131
-
132
137
  async buy(amount: bigint): Promise<string> {
133
- const buyer = await this.signer.getAddress();
138
+ this.ensureUser();
139
+
140
+ const buyer = await this.signer!.getAddress();
134
141
 
135
142
  // ! Hoodi only - check if the mock aggregator price is stale, and if so, update it before submitting the buy request
136
143
  await this.updateMockAggregatorPrice();
@@ -140,46 +147,6 @@ export class EthereumStakingClient implements IStakingClient {
140
147
  }
141
148
 
142
149
 
143
- async getOPPMessages(address?: string): Promise<OPPAssertion[]> {
144
- if(!address) address = await this.signer.getAddress();
145
-
146
- return await this.oppClient.getMessages(address);
147
- }
148
-
149
-
150
- async getOPPStatus(): Promise<any> {
151
- return await this.oppClient.getStatus();
152
- }
153
-
154
-
155
-
156
- /**
157
- * ETH Prelaunch function to list the Stake ReceiptNFTs owned by a specific user
158
- * @param address address to query the receipts for
159
- * @returns array of receipts
160
- */
161
- async fetchPrelaunchReceipts(address?: string): Promise<preLaunchReceipt[]> {
162
- if(address === undefined) address = await this.signer.getAddress();
163
-
164
- //default to stake receipts
165
- return await this.receiptClient.stakeReceipts(address);
166
- }
167
-
168
-
169
-
170
- async getEthStats(): Promise<any> {
171
- let withdrawDelay = await this.contract.DepositManager.withdrawDelay();
172
- let minDeposit = await this.contract.DepositManager.minDeposit();
173
- let rewardCooldown = await this.contract.DepositManager.rewardCooldown();
174
-
175
- return {
176
- withdrawDelay,
177
- minDeposit,
178
- rewardCooldown,
179
- }
180
- }
181
-
182
-
183
150
  /**
184
151
  * Resolve the user's ETH + liqETH balances.
185
152
  *
@@ -188,7 +155,9 @@ export class EthereumStakingClient implements IStakingClient {
188
155
  * tracked = liqETH tracked balance (protocol/accounting view)
189
156
  */
190
157
  async getPortfolio(): Promise<Portfolio> {
191
- const walletAddress = await this.signer.getAddress();
158
+ this.ensureUser();
159
+
160
+ const walletAddress = await this.signer!.getAddress();
192
161
 
193
162
  // 1) Native ETH balance
194
163
  const nativeBalance = await this.provider.getBalance(walletAddress);
@@ -206,7 +175,6 @@ export class EthereumStakingClient implements IStakingClient {
206
175
  stakeBalanceBN = stakeBalanceBN.add(BigNumber.from(r.receipt.principal.amount));
207
176
  }
208
177
 
209
-
210
178
  // 4) WIRE pretoken balance
211
179
  const wireBalance: ethers.BigNumber = await this.contract.Pretoken.balanceOf(walletAddress);
212
180
 
@@ -236,9 +204,74 @@ export class EthereumStakingClient implements IStakingClient {
236
204
  return portfolio;
237
205
  }
238
206
 
207
+ /**
208
+ * ETH Prelaunch function to list the Stake ReceiptNFTs owned by a specific user
209
+ * @param address address to query the receipts for
210
+ * @returns array of receipts
211
+ */
212
+ async fetchPrelaunchReceipts(address?: string): Promise<preLaunchReceipt[]> {
213
+ this.ensureUser();
214
+
215
+ if (address === undefined) address = await this.signer!.getAddress();
216
+
217
+ //default to stake receipts
218
+ return await this.receiptClient.stakeReceipts(address);
219
+ }
220
+
221
+ async getOPPMessages(address?: string): Promise<OPPAssertion[]> {
222
+ this.ensureUser();
223
+
224
+ if (!address) address = await this.signer!.getAddress();
225
+
226
+ return await this.oppClient.getMessages(address);
227
+ }
228
+
229
+ // Ensure that signer wallet is available for write operations
230
+ private ensureUser() {
231
+ if (!this.signer) {
232
+ throw new Error(
233
+ 'EthereumStakingClient: write operation requires a wallet-connected Web3 provider',
234
+ );
235
+ }
236
+ }
237
+
238
+
239
+ // ---------------------------------------------------------------------
240
+ // READ-ONLY Public Methods
241
+ // ---------------------------------------------------------------------
242
+
243
+ // Estimated total APY for staking yeild
244
+ getSystemAPY(): Promise<number> {
245
+ // TODO
246
+ return Promise.resolve(0);
247
+ }
248
+
249
+ // Protocol fee charged for deposit from Native to LIQ
250
+ getDepositFee(amount: bigint): Promise<bigint> {
251
+ // TODO
252
+ return Promise.resolve(BigInt(0));
253
+ }
254
+
255
+ async getOPPStatus(): Promise<any> {
256
+ return await this.oppClient.getStatus();
257
+ }
258
+
259
+ async getEthStats(): Promise<any> {
260
+ let withdrawDelay = await this.contract.DepositManager.withdrawDelay();
261
+ let minDeposit = await this.contract.DepositManager.minDeposit();
262
+ let rewardCooldown = await this.contract.DepositManager.rewardCooldown();
263
+
264
+ return {
265
+ withdrawDelay,
266
+ minDeposit,
267
+ rewardCooldown,
268
+ }
269
+ }
239
270
 
240
271
  /**
241
272
  * Program-level prelaunch WIRE / tranche snapshot for Ethereum
273
+ *
274
+ * SUPPORTS READ-ONLY ACcESS
242
275
  */
243
276
  async getTrancheSnapshot(options?: {
244
277
  chainID?: ChainID;
@@ -275,25 +308,25 @@ export class EthereumStakingClient implements IStakingClient {
275
308
 
276
309
 
277
310
  // fetch price and timestamp from aggregator
278
- const [ roundId, answer, startedAt, updatedAt, answeredInRound ] = await this.contract.Aggregator.latestRoundData();
311
+ const [roundId, answer, startedAt, updatedAt, answeredInRound] = await this.contract.Aggregator.latestRoundData();
279
312
  let ethPriceUsd: bigint = BigInt(answer.toString());
280
313
  let nativePriceTimestamp: number = Number(updatedAt);
281
314
 
282
315
  // ! Placeholder from hoodi deployment - don't think this can be fetched dynamically
283
- const initialTrancheSupply = BigInt(60000) * BigInt(1e8);
316
+ const initialTrancheSupply = BigInt(60000) * BigInt(1e8);
284
317
 
285
318
  return buildEthereumTrancheSnapshot({
286
319
  chainID,
287
- totalSharesBn,
288
- indexBn,
320
+ totalSharesBn,
321
+ indexBn,
289
322
  trancheNumberBn,
290
- currentTrancheSupply,
291
- tranchePriceWadBn,
323
+ currentTrancheSupply,
324
+ tranchePriceWadBn,
292
325
  totalTrancheSupply,
293
326
  initialTrancheSupply,
294
327
  supplyGrowthBps,
295
328
  priceGrowthBps,
296
- minPriceUsd,
329
+ minPriceUsd,
297
330
  maxPriceUsd,
298
331
 
299
332
  ethPriceUsd,
@@ -303,16 +336,10 @@ export class EthereumStakingClient implements IStakingClient {
303
336
  });
304
337
  }
305
338
 
306
-
307
-
308
-
309
339
  // ---------------------------------------------------------------------
310
340
  // Internal ETH Staking client helper functions
311
341
  // ---------------------------------------------------------------------
312
342
 
313
-
314
-
315
-
316
343
  // ! This is a temporary measure for Hoodi testnet because there is no aggregator deployed
317
344
  private async updateMockAggregatorPrice() {
318
345
  const aggregator = this.contract.Aggregator;
@@ -328,7 +355,7 @@ export class EthereumStakingClient implements IStakingClient {
328
355
  // safety check - only run in non-production contexts
329
356
  const network = await this.provider.getNetwork();
330
357
  const chainId = network.chainId;
331
- const allowedTestChains = new Set([560048]);
358
+ const allowedTestChains = new Set([560048]);
332
359
  if (!allowedTestChains.has(chainId)) {
333
360
  console.warn(`MockAggregator is stale (${ageSec}s) but chainId ${chainId} is not a test/local network — skipping update.`);
334
361
  return;
@@ -359,10 +386,4 @@ export class EthereumStakingClient implements IStakingClient {
359
386
  console.log(`MockAggregator updated ${ageSec}s ago — no update needed`);
360
387
  }
361
388
  }
362
-
363
-
364
-
365
-
366
-
367
- // TODO: implement claimRewards, etc.
368
389
  }
@@ -193,7 +193,7 @@ export class SolanaStakingClient implements IStakingClient {
193
193
  * Handles tx build, sign, send, and confirmation.
194
194
  */
195
195
  async deposit(amountLamports: bigint): Promise<string> {
196
- this.ensureWriteAccess();
196
+ this.ensureUser();
197
197
  if (amountLamports <= BigInt(0)) {
198
198
  throw new Error('Deposit amount must be greater than zero.');
199
199
  }
@@ -219,7 +219,7 @@ export class SolanaStakingClient implements IStakingClient {
219
219
  * Actual SOL payout happens later via the operator-side flow.
220
220
  */
221
221
  async withdraw(amountLamports: bigint): Promise<string> {
222
- this.ensureWriteAccess();
222
+ this.ensureUser();
223
223
  if (amountLamports <= BigInt(0)) {
224
224
  throw new Error('Withdraw amount must be greater than zero.');
225
225
  }
@@ -238,7 +238,7 @@ export class SolanaStakingClient implements IStakingClient {
238
238
  * Stake liqSOL into Outpost (liqSOL → pool) via liqsol_core::synd.
239
239
  */
240
240
  async stake(amountLamports: bigint): Promise<string> {
241
- this.ensureWriteAccess();
241
+ this.ensureUser();
242
242
 
243
243
  if (!amountLamports || amountLamports <= BigInt(0)) {
244
244
  throw new Error('Stake amount must be greater than zero.');
@@ -261,7 +261,7 @@ export class SolanaStakingClient implements IStakingClient {
261
261
  * Unstake liqSOL from Outpost (pool → liqSOL) via liqsol_core::desynd.
262
262
  */
263
263
  async unstake(amountLamports: bigint): Promise<string> {
264
- this.ensureWriteAccess();
264
+ this.ensureUser();
265
265
 
266
266
  if (!amountLamports || amountLamports <= BigInt(0)) {
267
267
  throw new Error('Unstake amount must be greater than zero.');
@@ -287,7 +287,7 @@ export class SolanaStakingClient implements IStakingClient {
287
287
  * instruction under the new IDL (no more native-SOL purchase).
288
288
  */
289
289
  async buy(amountLamports: bigint): Promise<string> {
290
- this.ensureWriteAccess();
290
+ this.ensureUser();
291
291
  if (!amountLamports || amountLamports <= BigInt(0)) {
292
292
  throw new Error('liqSOL pretoken purchase requires a positive amount.');
293
293
  }
@@ -453,6 +453,32 @@ export class SolanaStakingClient implements IStakingClient {
453
453
  };
454
454
  }
455
455
 
456
+ /**
457
+ * Convenience helper to fetch the distribution userRecord for the current user.
458
+ * Used by balance-correction flows and debugging.
459
+ */
460
+ async getUserRecord() {
461
+ if (!this.pubKey) throw new Error('User pubKey is undefined');
462
+ return this.distributionClient.getUserRecord(this.solPubKey);
463
+ }
464
+
465
+
466
+ // ---------------------------------------------------------------------
467
+ // READ-ONLY Public Methods
468
+ // ---------------------------------------------------------------------
469
+
470
+ // Estimated total APY for staking yeild
471
+ getSystemAPY(): Promise<number> {
472
+ // TODO
473
+ return Promise.resolve(0);
474
+ }
475
+
476
+ // Protocol fee charged for deposit from Native to LIQ
477
+ getDepositFee(amount: bigint): Promise<bigint> {
478
+ // TODO
479
+ return Promise.resolve(BigInt(0));
480
+ }
481
+
456
482
  /**
457
483
  * Unified, chain-agnostic tranche snapshot for Solana.
458
484
  *
@@ -493,15 +519,6 @@ export class SolanaStakingClient implements IStakingClient {
493
519
  });
494
520
  }
495
521
 
496
- /**
497
- * Convenience helper to fetch the distribution userRecord for the current user.
498
- * Used by balance-correction flows and debugging.
499
- */
500
- async getUserRecord() {
501
- if (!this.pubKey) throw new Error('User pubKey is undefined');
502
- return this.distributionClient.getUserRecord(this.solPubKey);
503
- }
504
-
505
522
  // ---------------------------------------------------------------------
506
523
  // Tx helpers
507
524
  // ---------------------------------------------------------------------
@@ -514,7 +531,7 @@ export class SolanaStakingClient implements IStakingClient {
514
531
  signed: SolanaTransaction,
515
532
  ctx: { blockhash: string; lastValidBlockHeight: number },
516
533
  ): Promise<string> {
517
- this.ensureWriteAccess();
534
+ this.ensureUser();
518
535
 
519
536
  const signature = await this.connection.sendRawTransaction(
520
537
  signed.serialize(),
@@ -549,7 +566,7 @@ export class SolanaStakingClient implements IStakingClient {
549
566
  async signTransaction(
550
567
  tx: SolanaTransaction,
551
568
  ): Promise<SolanaTransaction> {
552
- this.ensureWriteAccess();
569
+ this.ensureUser();
553
570
  return this.anchor.wallet.signTransaction(tx);
554
571
  }
555
572
 
@@ -560,7 +577,7 @@ export class SolanaStakingClient implements IStakingClient {
560
577
  async sendTransaction(
561
578
  signed: SolanaTransaction,
562
579
  ): Promise<TransactionSignature> {
563
- this.ensureWriteAccess();
580
+ this.ensureUser();
564
581
  return this.anchor.sendAndConfirm(signed);
565
582
  }
566
583
 
@@ -586,7 +603,7 @@ export class SolanaStakingClient implements IStakingClient {
586
603
  * Guard for all write operations (deposit/withdraw/stake/unstake/buy).
587
604
  * Ensures we have a Wire pubKey and an Anchor wallet pubKey, and that they match.
588
605
  */
589
- ensureWriteAccess() {
606
+ ensureUser() {
590
607
  if (!this.pubKey || !this.anchor.wallet.publicKey) {
591
608
  throw new Error('User Authorization required: pubKey is undefined');
592
609
  }
package/src/types.ts CHANGED
@@ -5,7 +5,7 @@ import { ethers } from 'ethers';
5
5
 
6
6
  export type StakerConfig = {
7
7
  network: ExternalNetwork;
8
- provider: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
8
+ provider?: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
9
9
  pubKey?: PublicKey;
10
10
  }
11
11
 
@@ -23,6 +23,12 @@ export interface IStakingClient {
23
23
  /** Fetch the complete user portfolio */
24
24
  getPortfolio(): Promise<Portfolio>;
25
25
 
26
+ // Estimated total APY for staking yeild
27
+ getSystemAPY(): Promise<number>;
28
+
29
+ // Protocol fee charged for deposit from Native to LIQ
30
+ getDepositFee(amount: bigint): Promise<bigint>;
31
+
26
32
  /**
27
33
  * Program-level prelaunch WIRE/tranche snapshot for this chain.
28
34
  *