@wireio/stake 0.2.4 → 0.3.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.
Files changed (100) hide show
  1. package/lib/stake.browser.js +5991 -4340
  2. package/lib/stake.browser.js.map +1 -1
  3. package/lib/stake.d.ts +74 -60
  4. package/lib/stake.js +9782 -8054
  5. package/lib/stake.js.map +1 -1
  6. package/lib/stake.m.js +5991 -4340
  7. package/lib/stake.m.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/assets/ethereum/ABI/liqEth/BeaconState.sol/BeaconState.dbg.json +4 -0
  10. package/src/assets/ethereum/ABI/liqEth/BeaconState.sol/BeaconState.json +807 -0
  11. package/src/assets/ethereum/ABI/liqEth/DepositManager.sol/DepositManager.dbg.json +1 -1
  12. package/src/assets/ethereum/ABI/liqEth/DepositManager.sol/DepositManager.json +184 -346
  13. package/src/assets/ethereum/ABI/liqEth/LiqEthAuthority.sol/LiqEthAuthority.dbg.json +4 -0
  14. package/src/assets/ethereum/ABI/liqEth/LiqEthAuthority.sol/LiqEthAuthority.json +1289 -0
  15. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IAccounting.dbg.json +1 -1
  16. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IDepositContract.dbg.json +1 -1
  17. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IDepositManager.dbg.json +1 -1
  18. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/ILiqEthUpgradeable.dbg.json +4 -0
  19. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/ILiqEthUpgradeable.json +29 -0
  20. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20.dbg.json +1 -1
  21. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20.json +36 -0
  22. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20Pausable.dbg.json +4 -0
  23. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IRewardsERC20Pausable.json +263 -0
  24. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IStakingModule.dbg.json +1 -1
  25. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IStakingModule.json +226 -0
  26. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IValidatorBalanceVerifier.dbg.json +1 -1
  27. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IValidatorBalanceVerifier.json +59 -0
  28. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IWithdrawalRecord.dbg.json +1 -1
  29. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/IWithdrawalRecord.json +14 -12
  30. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/LiqEthCommon.dbg.json +1 -1
  31. package/src/assets/ethereum/ABI/liqEth/LiqEthCommon.sol/LiqEthCommon.json +2 -2
  32. package/src/assets/ethereum/ABI/liqEth/LiqEthManaged.sol/LiqEthManaged.dbg.json +4 -0
  33. package/src/assets/ethereum/ABI/liqEth/LiqEthManaged.sol/LiqEthManaged.json +229 -0
  34. package/src/assets/ethereum/ABI/liqEth/RewardsERC20.sol/RewardsERC20Upgradeable.dbg.json +4 -0
  35. package/src/assets/ethereum/ABI/liqEth/{RewardsERC20Pausable.sol/RewardsERC20Pausable.json → RewardsERC20.sol/RewardsERC20Upgradeable.json} +140 -78
  36. package/src/assets/ethereum/ABI/liqEth/RewardsERC20Pausable.sol/RewardsERC20PausableUpgradeable.dbg.json +4 -0
  37. package/src/assets/ethereum/ABI/liqEth/{RewardsERC20.sol/RewardsERC20.json → RewardsERC20Pausable.sol/RewardsERC20PausableUpgradeable.json} +218 -30
  38. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/BeaconRoots.dbg.json +1 -1
  39. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/BeaconRoots.json +2 -2
  40. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZ.dbg.json +1 -1
  41. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZ.json +2 -2
  42. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZExtras.dbg.json +4 -0
  43. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZExtras.json +10 -0
  44. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZVec48.dbg.json +4 -0
  45. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/SSZVec48.json +10 -0
  46. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/ValidatorBalanceVerifier.dbg.json +1 -1
  47. package/src/assets/ethereum/ABI/liqEth/ValidatorBalanceVerifier.sol/ValidatorBalanceVerifier.json +121 -55
  48. package/src/assets/ethereum/ABI/liqEth/Yield.sol/YieldOracle.dbg.json +1 -1
  49. package/src/assets/ethereum/ABI/liqEth/Yield.sol/YieldOracle.json +836 -273
  50. package/src/assets/ethereum/ABI/liqEth/accounting.sol/Accounting.dbg.json +1 -1
  51. package/src/assets/ethereum/ABI/liqEth/accounting.sol/Accounting.json +150 -168
  52. package/src/assets/ethereum/ABI/liqEth/liqEth.sol/LiqEthToken.dbg.json +1 -1
  53. package/src/assets/ethereum/ABI/liqEth/liqEth.sol/LiqEthToken.json +301 -186
  54. package/src/assets/ethereum/ABI/liqEth/stakingModule.sol/StakingModule.dbg.json +1 -1
  55. package/src/assets/ethereum/ABI/liqEth/stakingModule.sol/StakingModule.json +814 -206
  56. package/src/assets/ethereum/ABI/liqEth/withdrawalQueue.sol/WithdrawalQueue.dbg.json +1 -1
  57. package/src/assets/ethereum/ABI/liqEth/withdrawalQueue.sol/WithdrawalQueue.json +244 -198
  58. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/Uint64BE.dbg.json +1 -1
  59. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/Uint64BE.json +2 -2
  60. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/WithdrawalVault.dbg.json +1 -1
  61. package/src/assets/ethereum/ABI/liqEth/withdrawalVault.sol/WithdrawalVault.json +165 -152
  62. package/src/assets/ethereum/ABI/outpost/Aggregator.sol/Aggregator.json +82 -0
  63. package/src/assets/ethereum/ABI/outpost/Depositor.sol/Depositor.dbg.json +1 -1
  64. package/src/assets/ethereum/ABI/outpost/Depositor.sol/Depositor.json +167 -282
  65. package/src/assets/ethereum/ABI/outpost/EthUsdPriceConsumer.sol/AggregatorV3Interface.dbg.json +1 -1
  66. package/src/assets/ethereum/ABI/outpost/EthUsdPriceConsumer.sol/EthUsdPriceConsumer.dbg.json +1 -1
  67. package/src/assets/ethereum/ABI/outpost/EthUsdPriceConsumer.sol/EthUsdPriceConsumer.json +2 -54
  68. package/src/assets/ethereum/ABI/outpost/OPP.sol/OPP.dbg.json +1 -1
  69. package/src/assets/ethereum/ABI/outpost/OPP.sol/OPP.json +26 -8
  70. package/src/assets/ethereum/ABI/outpost/OPPInbound.sol/OPPInbound.dbg.json +1 -1
  71. package/src/assets/ethereum/ABI/outpost/OPPInbound.sol/OPPInbound.json +2 -2
  72. package/src/assets/ethereum/ABI/outpost/Pretoken.sol/Pretoken.dbg.json +4 -0
  73. package/src/assets/ethereum/ABI/outpost/Pretoken.sol/Pretoken.json +1650 -0
  74. package/src/assets/ethereum/ABI/outpost/ReceiptNFT.sol/ReceiptNFT.dbg.json +1 -1
  75. package/src/assets/ethereum/ABI/outpost/ReceiptNFT.sol/ReceiptNFT.json +2 -22
  76. package/src/assets/ethereum/ABI/outpost/interfaces/IPretoken.sol/IPretoken.dbg.json +4 -0
  77. package/src/assets/ethereum/ABI/outpost/interfaces/IPretoken.sol/IPretoken.json +29 -0
  78. package/src/assets/ethereum/ABI/outpost/interfaces/IWarrant.sol/IWarrant.dbg.json +1 -1
  79. package/src/networks/ethereum/clients/deposit.client.ts +86 -8
  80. package/src/networks/ethereum/clients/liq.client.ts +47 -0
  81. package/src/networks/ethereum/clients/pretoken.client.ts +127 -0
  82. package/src/networks/ethereum/clients/stake.client.ts +87 -24
  83. package/src/networks/ethereum/contract.ts +34 -48
  84. package/src/networks/ethereum/ethereum.ts +219 -96
  85. package/src/networks/ethereum/types.ts +9 -6
  86. package/src/networks/ethereum/utils.ts +308 -0
  87. package/src/networks/solana/clients/token.client.ts +0 -1
  88. package/src/networks/solana/solana.ts +44 -146
  89. package/src/networks/solana/types.ts +6 -2
  90. package/src/networks/solana/utils.ts +4 -6
  91. package/src/staker/types.ts +62 -0
  92. package/src/types.ts +18 -33
  93. package/src/assets/ethereum/ABI/liqEth/RewardsERC20.sol/RewardsERC20.dbg.json +0 -4
  94. package/src/assets/ethereum/ABI/liqEth/RewardsERC20Pausable.sol/RewardsERC20Pausable.dbg.json +0 -4
  95. package/src/assets/ethereum/ABI/liqEth/Yield.sol/BeaconRoots.dbg.json +0 -4
  96. package/src/assets/ethereum/ABI/liqEth/Yield.sol/BeaconRoots.json +0 -10
  97. package/src/assets/ethereum/ABI/liqEth/Yield.sol/SSZ.dbg.json +0 -4
  98. package/src/assets/ethereum/ABI/liqEth/Yield.sol/SSZ.json +0 -10
  99. package/src/assets/ethereum/ABI/outpost/Warrant.sol/Warrant.dbg.json +0 -4
  100. package/src/assets/ethereum/ABI/outpost/Warrant.sol/Warrant.json +0 -1650
@@ -0,0 +1,308 @@
1
+
2
+ import { ethers } from "ethers";
3
+ import { TrancheLadderItem, TrancheSnapshot } from "../../types";
4
+ import { ChainID } from "@wireio/core";
5
+
6
+
7
+ const BPS = BigInt(10_000);
8
+
9
+ export interface CustomContractError {
10
+ name?: string;
11
+ signature?: string;
12
+ args?: any[];
13
+ data?: any;
14
+ method?: string;
15
+ code?: any;
16
+ raw: string;
17
+ }
18
+
19
+
20
+
21
+ export function formatContractErrors(err: any): CustomContractError {
22
+ // Extract custom error details if present
23
+ if (err.errorName && err.errorArgs) {
24
+ const errorObj: CustomContractError = {
25
+ name: err.errorName,
26
+ signature: err.errorSignature,
27
+ args: err.errorArgs.map((arg: any) =>
28
+ arg && arg._isBigNumber ? ethers.BigNumber.from(arg).toString() : arg
29
+ ),
30
+ data: err.data,
31
+ method: err.method,
32
+ code: err.code,
33
+ raw: err,
34
+ };
35
+ console.error("Custom contract error:", errorObj);
36
+ return errorObj;
37
+ } else {
38
+ console.error("Contract Error:", err);
39
+ return {
40
+ raw: typeof err === 'string' ? err : (err?.message || String(err))
41
+ };
42
+ }
43
+ }
44
+
45
+
46
+
47
+ /**
48
+ * Finalize an OPP epoch. Optionally accept a gasLimit override (ethers BigNumberish).
49
+ * If gasLimit is not provided, the method will attempt estimateGas.finalizeEpoch()
50
+ * and pad it (1.2x). If estimateGas fails, a conservative fallback is used.
51
+ */
52
+ export async function sendOPPFinalize(opp: ethers.Contract, gasLimit?: ethers.BigNumberish) {
53
+ const overrides: any = {};
54
+ try {
55
+ if (gasLimit === undefined) {
56
+ try {
57
+ const estimated = await opp.estimateGas.finalizeEpoch();
58
+ const padded = ethers.BigNumber.from(estimated).mul(12).div(10); // 1.2x
59
+ overrides.gasLimit = padded;
60
+ console.log('sendFinalize: estimated gas', estimated.toString(), 'padded to', overrides.gasLimit.toString());
61
+ } catch (estErr) {
62
+ // estimateGas can throw UNPREDICTABLE_GAS_LIMIT; fall back to a safe default
63
+ console.warn('sendFinalize: estimateGas.finalizeEpoch() failed, falling back to hardcoded gasLimit', estErr);
64
+ overrides.gasLimit = ethers.BigNumber.from(8000000);
65
+ }
66
+ } else {
67
+ overrides.gasLimit = ethers.BigNumber.from(gasLimit);
68
+ console.log('sendFinalize: using provided gasLimit override', overrides.gasLimit.toString());
69
+ }
70
+
71
+ const tx = await opp.finalizeEpoch(overrides); // submit tx with overrides
72
+ console.log('sendFinalize tx hash:', tx.hash, 'overrides:', overrides);
73
+
74
+ const receipt = await tx.wait(); // wait for mined
75
+ console.log('sendFinalize tx mined, block:', receipt.blockNumber);
76
+ return receipt;
77
+ } catch (err: any) {
78
+ // Verbose error logging to help debugging reverts / provider issues
79
+ console.error('sendFinalize() failed:', err?.message || err);
80
+
81
+ // Try to extract common raw payload locations used by ethers errors
82
+ const raw = err?.error?.data || err?.data || err?.body || err?.receipt || err?.transaction || err;
83
+ console.error('sendFinalize raw payload:', raw);
84
+
85
+ // Try parsing with contractService if available
86
+ try {
87
+ const parsed = formatContractErrors(raw);
88
+ console.error('sendFinalize parsed error:', parsed.name, parsed.args);
89
+ } catch (parseErr) {
90
+ // Fallback: decode Error(string) ABI encoded revert (0x08c379a0)
91
+ try {
92
+ const hex = (typeof raw === 'string') ? raw : (raw && raw.data) ? raw.data : null;
93
+ if (hex && typeof hex === 'string' && hex.startsWith('0x08c379a0')) {
94
+ const reason = ethers.utils.defaultAbiCoder.decode(['string'], '0x' + hex.slice(10))[0];
95
+ console.error('sendFinalize revert reason:', reason);
96
+ } else {
97
+ console.error('sendFinalize: unable to decode revert payload (not standard Error(string))');
98
+ }
99
+ } catch (fallbackErr) {
100
+ console.error('sendFinalize: fallback decode failed:', fallbackErr);
101
+ }
102
+ }
103
+
104
+ // If there is a receipt, print it for extra context
105
+ if (err?.receipt) console.error('sendFinalize receipt:', err.receipt);
106
+ if (err?.transaction) console.error('sendFinalize transaction object:', err.transaction);
107
+
108
+ // Re-throw so callers can handle it as well
109
+ throw err;
110
+ }
111
+ }
112
+
113
+
114
+
115
+ /**
116
+ * Apply one forward growth step: value * (BPS + growthBps) / BPS.
117
+ * Simple integer round-half-up.
118
+ */
119
+ function growOnce(value: bigint, growthBps: number): bigint {
120
+ const g = BigInt(growthBps);
121
+ return (value * (BPS + g) + BPS / BigInt(2)) / BPS;
122
+ }
123
+
124
+
125
+ /**
126
+ * Apply one backward step: value * BPS / (BPS + growthBps).
127
+ * Also integer round-half-up.
128
+ */
129
+ function shrinkOnce(value: bigint, growthBps: number): bigint {
130
+ const g = BigInt(growthBps);
131
+ return (value * BPS + (BPS + g) / BigInt(2)) / (BPS + g);
132
+ }
133
+
134
+
135
+ /**
136
+ * Calculate the full supply for a given tranche using BigInt math.
137
+ * trancheNumber is 1-based (tranche 1 = startSupply)
138
+ */
139
+ function getTrancheSize(startSupply: bigint, supplyGrowthBps: number, trancheNumber: number): bigint {
140
+ let supply = startSupply;
141
+ for (let i = 0; i < trancheNumber; i++) {
142
+ supply = (supply * (BPS + BigInt(supplyGrowthBps)) + BPS / BigInt(2)) / BPS;
143
+ }
144
+ return supply;
145
+ }
146
+
147
+
148
+ /**
149
+ * Build a local tranche ladder around the current tranche
150
+ * using only on-chain config + current state.
151
+ *
152
+ */
153
+ export function buildEthereumTrancheLadder(options: {
154
+ currentTranche: number;
155
+ totalTrancheSupply: bigint,
156
+ initialTrancheSupply: bigint;
157
+ currentTrancheSupply: bigint;
158
+ currentPriceUsd: bigint;
159
+ supplyGrowthBps: number;
160
+ priceGrowthBps: number;
161
+ windowBefore?: number;
162
+ windowAfter?: number;
163
+ }): TrancheLadderItem[] {
164
+ const {
165
+ currentTranche,
166
+ initialTrancheSupply,
167
+ currentTrancheSupply,
168
+ currentPriceUsd,
169
+ supplyGrowthBps,
170
+ priceGrowthBps,
171
+ windowBefore = 5,
172
+ windowAfter = 5,
173
+ } = options;
174
+
175
+ const startId = Math.max(0, currentTranche - windowBefore);
176
+ const endId = currentTranche + windowAfter;
177
+
178
+ //calculate total tranche size (e.g. 60,600 on tranche 2)
179
+ const currentTrancheSize = getTrancheSize(initialTrancheSupply, supplyGrowthBps, currentTranche);
180
+
181
+ const capacity = new Map<number, bigint>();
182
+ const price = new Map<number, bigint>();
183
+
184
+ // Seed current
185
+ capacity.set(currentTranche, currentTrancheSize);
186
+ price.set(currentTranche, currentPriceUsd);
187
+
188
+ // Forward (future tranches)
189
+ for (let id = currentTranche + 1; id <= endId; id++) {
190
+ const prevCap = capacity.get(id - 1)!;
191
+ const prevPrice = price.get(id - 1)!;
192
+ capacity.set(id, growOnce(prevCap, supplyGrowthBps));
193
+ price.set(id, growOnce(prevPrice, priceGrowthBps));
194
+ }
195
+
196
+ // Backward (past tranches)
197
+ for (let id = currentTranche - 1; id >= startId; id--) {
198
+ const nextCap = capacity.get(id + 1)!;
199
+ const nextPrice = price.get(id + 1)!;
200
+ capacity.set(id, shrinkOnce(nextCap, supplyGrowthBps));
201
+ price.set(id, shrinkOnce(nextPrice, priceGrowthBps));
202
+ }
203
+
204
+ const ladder: TrancheLadderItem[] = [];
205
+ for (let id = startId; id <= endId; id++) {
206
+ const cap = capacity.get(id)!;
207
+ let sold: bigint;
208
+ if (id < currentTranche) {
209
+ sold = cap;
210
+ } else if (id === currentTranche) {
211
+ sold = cap - currentTrancheSupply;
212
+ } else {
213
+ sold = BigInt(0);
214
+ }
215
+
216
+ ladder.push({
217
+ id,
218
+ capacity: cap,
219
+ sold,
220
+ remaining: cap - sold,
221
+ priceUsd: price.get(id)!,
222
+ });
223
+ }
224
+
225
+ return ladder;
226
+ }
227
+
228
+
229
+ /**
230
+ * Turn raw liqsol_core accounts into a chain-agnostic TrancheSnapshot for SOL.
231
+ * All math stays here; TokenClient just wires accounts + connection.
232
+ */
233
+ export async function buildEthereumTrancheSnapshot(options: {
234
+ chainID: ChainID;
235
+ totalSharesBn;
236
+ indexBn;
237
+ trancheNumberBn;
238
+ currentTrancheSupply;
239
+ tranchePriceWadBn;
240
+ totalTrancheSupply;
241
+ initialTrancheSupply;
242
+ supplyGrowthBps;
243
+ priceGrowthBps;
244
+ minPriceUsd;
245
+ maxPriceUsd;
246
+
247
+ ethPriceUsd?: bigint;
248
+ nativePriceTimestamp?: number;
249
+ ladderWindowBefore?: number;
250
+ ladderWindowAfter?: number;
251
+ }): Promise<TrancheSnapshot> {
252
+ const {
253
+ chainID,
254
+ ethPriceUsd,
255
+ nativePriceTimestamp,
256
+ ladderWindowBefore,
257
+ ladderWindowAfter,
258
+
259
+ totalSharesBn,
260
+ indexBn,
261
+ trancheNumberBn,
262
+ currentTrancheSupply,
263
+ tranchePriceWadBn,
264
+ totalTrancheSupply,
265
+ initialTrancheSupply,
266
+ supplyGrowthBps,
267
+ priceGrowthBps,
268
+ minPriceUsd,
269
+ maxPriceUsd,
270
+ } = options;
271
+
272
+
273
+ // convert default BigNumber to bigint for hub to handle, and partially convert from 1e18 to 1e8 for the hub
274
+ const totalShares = BigInt(totalSharesBn.toString()) / BigInt(1e10);
275
+ const currentIndex = BigInt(indexBn.toString()); // RAY (1e27)
276
+ const currentTranche = Number(trancheNumberBn.toString());
277
+ const currentPriceUsd = BigInt(tranchePriceWadBn.toString()) / BigInt(1e10); // 1e18 WAD
278
+
279
+
280
+ const ladder = buildEthereumTrancheLadder({
281
+ currentTranche,
282
+ totalTrancheSupply,
283
+ initialTrancheSupply,
284
+ currentTrancheSupply,
285
+ currentPriceUsd,
286
+ supplyGrowthBps,
287
+ priceGrowthBps,
288
+ windowBefore: ladderWindowBefore,
289
+ windowAfter: ladderWindowAfter,
290
+ });
291
+
292
+
293
+ return {
294
+ chainID,
295
+ currentIndex,
296
+ totalShares,
297
+ currentTranche,
298
+ currentPriceUsd,
299
+ supplyGrowthBps,
300
+ priceGrowthBps,
301
+ currentTrancheSupply,
302
+ initialTrancheSupply,
303
+ totalPretokensSold: totalTrancheSupply,
304
+ nativePriceUsd: ethPriceUsd,
305
+ nativePriceTimestamp,
306
+ ladder,
307
+ };
308
+ }
@@ -20,7 +20,6 @@ import {
20
20
  import { derivePriceHistoryPda } from '../constants';
21
21
  import type { } from '../types';
22
22
  import { ChainID, SolChainID } from '@wireio/core';
23
- import { PurchaseAsset, PurchaseQuote, TrancheSnapshot } from '../../../types';
24
23
 
25
24
  export class TokenClient {
26
25
  private readonly program: Program<LiqsolCore>;
@@ -27,8 +27,6 @@ import {
27
27
  import {
28
28
  IStakingClient,
29
29
  Portfolio,
30
- PurchaseAsset,
31
- PurchaseQuote,
32
30
  StakerConfig,
33
31
  TrancheSnapshot,
34
32
  } from '../../types';
@@ -62,7 +60,7 @@ const commitment: Commitment = 'confirmed';
62
60
  * This class composes lower-level clients; it does not know about UI.
63
61
  */
64
62
  export class SolanaStakingClient implements IStakingClient {
65
- public pubKey: PublicKey;
63
+ public pubKey?: PublicKey;
66
64
  public connection: Connection;
67
65
  public anchor: AnchorProvider;
68
66
 
@@ -73,6 +71,7 @@ export class SolanaStakingClient implements IStakingClient {
73
71
  private tokenClient: TokenClient;
74
72
 
75
73
  get solPubKey(): SolPubKey {
74
+ if (!this.pubKey) throw new Error('pubKey is undefined');
76
75
  return new SolPubKey(this.pubKey.data.array);
77
76
  }
78
77
 
@@ -82,13 +81,17 @@ export class SolanaStakingClient implements IStakingClient {
82
81
 
83
82
  constructor(private config: StakerConfig) {
84
83
  const adapter = config.provider as BaseSignerWalletAdapter;
85
- if (!adapter?.publicKey) throw new Error('Solana wallet adapter not connected');
84
+ // if (!adapter?.publicKey) throw new Error('Solana wallet adapter not connected');
86
85
  if (!config.network.rpcUrls.length) throw new Error('No RPC URLs provided');
87
-
88
- const publicKey = adapter.publicKey;
89
- const wirePub = new PublicKey(KeyType.ED, publicKey.toBytes());
90
- if (!wirePub.equals(config.pubKey)) {
91
- throw new Error("Passed-in pubKey doesn't match adapter.publicKey");
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
94
+ this.pubKey = wirePub;
92
95
  }
93
96
 
94
97
  const opts: ConnectionConfig = { commitment };
@@ -111,7 +114,6 @@ export class SolanaStakingClient implements IStakingClient {
111
114
  },
112
115
  };
113
116
 
114
- this.pubKey = wirePub;
115
117
  this.connection = new Connection(config.network.rpcUrls[0], opts);
116
118
  this.anchor = new AnchorProvider(this.connection, anchorWallet, { commitment });
117
119
 
@@ -120,7 +122,6 @@ export class SolanaStakingClient implements IStakingClient {
120
122
  this.leaderboardClient = new LeaderboardClient(this.anchor);
121
123
  this.outpostClient = new OutpostClient(this.anchor);
122
124
  this.tokenClient = new TokenClient(this.anchor);
123
- this.tokenClient = new TokenClient(this.anchor);
124
125
  }
125
126
 
126
127
  // ---------------------------------------------------------------------
@@ -132,6 +133,7 @@ export class SolanaStakingClient implements IStakingClient {
132
133
  * Handles tx build, sign, send, and confirmation.
133
134
  */
134
135
  async deposit(amountLamports: bigint): Promise<string> {
136
+ this.ensureWriteAccess();
135
137
  if (amountLamports <= BigInt(0)) {
136
138
  throw new Error('Deposit amount must be greater than zero.');
137
139
  }
@@ -152,6 +154,7 @@ export class SolanaStakingClient implements IStakingClient {
152
154
  * NOTE: placeholder until a withdraw flow is implemented in DepositClient.
153
155
  */
154
156
  async withdraw(_amountLamports: bigint): Promise<string> {
157
+ this.ensureWriteAccess();
155
158
  throw new Error('Withdraw method not yet implemented.');
156
159
  }
157
160
 
@@ -160,6 +163,7 @@ export class SolanaStakingClient implements IStakingClient {
160
163
  * Ensures user ATA exists, then stakes via outpostClient.
161
164
  */
162
165
  async stake(amountLamports: bigint): Promise<string> {
166
+ this.ensureWriteAccess();
163
167
  if (amountLamports <= BigInt(0)) {
164
168
  throw new Error('Stake amount must be greater than zero.');
165
169
  }
@@ -180,6 +184,7 @@ export class SolanaStakingClient implements IStakingClient {
180
184
  * Mirrors stake() but calls withdrawStake.
181
185
  */
182
186
  async unstake(amountLamports: bigint): Promise<string> {
187
+ this.ensureWriteAccess();
183
188
  if (amountLamports <= BigInt(0)) {
184
189
  throw new Error('Unstake amount must be greater than zero.');
185
190
  }
@@ -204,56 +209,17 @@ export class SolanaStakingClient implements IStakingClient {
204
209
  *
205
210
  * ETH / LIQETH are not valid on Solana.
206
211
  */
207
- async buy(amountLamports: bigint, purchaseAsset: PurchaseAsset): Promise<string> {
212
+ async buy(amountLamports: bigint): Promise<string> {
213
+ this.ensureWriteAccess();
214
+ if (!amountLamports || amountLamports <= BigInt(0))
215
+ throw new Error('liqSOL pretoken purchase requires a positive amount.');
216
+
208
217
  const user = this.solPubKey;
209
- let ix: TransactionInstruction;
210
- let preIxs: TransactionInstruction[] = [];
211
-
212
- switch (purchaseAsset) {
213
- case PurchaseAsset.SOL: {
214
- if (!amountLamports || amountLamports <= BigInt(0)) {
215
- throw new Error('SOL pretoken purchase requires a positive amount.');
216
- }
217
- ix = await this.tokenClient.buildPurchaseWithSolIx(
218
- amountLamports,
219
- user,
220
- );
221
- break;
222
- }
223
-
224
- case PurchaseAsset.LIQSOL: {
225
- if (!amountLamports || amountLamports <= BigInt(0)) {
226
- throw new Error(
227
- 'liqSOL pretoken purchase requires a positive amount.',
228
- );
229
- }
230
- preIxs = await this.outpostClient.maybeBuildCreateUserAtaIx(user);
231
- ix = await this.tokenClient.buildPurchaseWithLiqsolIx(
232
- amountLamports,
233
- user,
234
- );
235
- break;
236
- }
237
-
238
- case PurchaseAsset.YIELD: {
239
- // Amount is ignored by the on-chain program; it consumes tracked yield.
240
- ix = await this.tokenClient.buildPurchaseFromYieldIx(user);
241
- break;
242
- }
243
-
244
- case PurchaseAsset.ETH:
245
- case PurchaseAsset.LIQETH: {
246
- throw new Error(
247
- 'ETH / LIQETH pretoken purchases are not supported on Solana.',
248
- );
249
- }
250
-
251
- default:
252
- throw new Error(`Unsupported pretoken purchase asset: ${String(
253
- purchaseAsset,
254
- )}`);
255
- }
256
-
218
+ const preIxs = await this.outpostClient.maybeBuildCreateUserAtaIx(user);
219
+ const ix = await this.tokenClient.buildPurchaseWithLiqsolIx(
220
+ amountLamports,
221
+ user,
222
+ );
257
223
  const tx = new Transaction().add(...preIxs, ix);
258
224
  const prepared = await this.prepareTx(tx);
259
225
  const signed = await this.signTransaction(prepared.tx);
@@ -270,6 +236,8 @@ export class SolanaStakingClient implements IStakingClient {
270
236
  * - extras: useful internal addresses and raw state for debugging/UX
271
237
  */
272
238
  async getPortfolio(): Promise<Portfolio> {
239
+ if (!this.pubKey) throw new Error('User pubKey is undefined');
240
+
273
241
  const user = this.solPubKey;
274
242
 
275
243
  const reservePoolPDA = deriveReservePoolPda();
@@ -313,7 +281,7 @@ export class SolanaStakingClient implements IStakingClient {
313
281
  wireReceipt?.stakedLiqsol?.toString() ?? '0';
314
282
 
315
283
  const wireSharesStr =
316
- userWarrantRecord?.totalWarrantsPurchased?.toString() ?? '0';
284
+ userWarrantRecord?.totalPretokensPurchased?.toString() ?? '0';
317
285
 
318
286
  return {
319
287
  native: {
@@ -369,6 +337,8 @@ export class SolanaStakingClient implements IStakingClient {
369
337
  * windowBefore/windowAfter control how many ladder rows we precompute
370
338
  * around the current tranche for UI, but you can pass nothing if you
371
339
  * only need current tranche info.
340
+ *
341
+ * READ-ONLY allowed
372
342
  */
373
343
  async getTrancheSnapshot(options?: {
374
344
  chainID?: ChainID;
@@ -402,95 +372,12 @@ export class SolanaStakingClient implements IStakingClient {
402
372
  });
403
373
  }
404
374
 
405
- /**
406
- * Approximate prelaunch WIRE quote for a given amount & asset.
407
- *
408
- * Uses TrancheSnapshot + SOL/USD price for:
409
- * - SOL: amount is lamports
410
- * - LIQSOL: amount is liqSOL base units (decimals = 9)
411
- * - YIELD: amount is treated as SOL lamports-equivalent of yield
412
- *
413
- * NOTE: On-chain rounding may differ slightly (this is UI-only).
414
- */
415
- async getBuyQuote(
416
- amount: bigint,
417
- asset: PurchaseAsset,
418
- opts?: { chainID?: ChainID },
419
- ): Promise<PurchaseQuote> {
420
- // For non-YIELD purchases we require a positive amount.
421
- if (asset !== PurchaseAsset.YIELD && amount <= BigInt(0)) {
422
- throw new Error('amount must be > 0 for non-YIELD purchases');
423
- }
424
-
425
- const snapshot = await this.getTrancheSnapshot({
426
- chainID: opts?.chainID,
427
- });
428
-
429
- const wirePriceUsd = snapshot.currentPriceUsd; // 1e8
430
- const solPriceUsd = snapshot.nativePriceUsd; // 1e8
431
-
432
- if (!wirePriceUsd || wirePriceUsd <= BigInt(0)) {
433
- throw new Error('Invalid WIRE price in tranche snapshot');
434
- }
435
- if (!solPriceUsd || solPriceUsd <= BigInt(0)) {
436
- throw new Error('No SOL/USD price available');
437
- }
438
-
439
- const ONE_E9 = BigInt(LAMPORTS_PER_SOL); // 1e9
440
- const ONE_E8 = BigInt(100_000_000); // 1e8
441
-
442
- let notionalUsd: bigint;
443
-
444
- switch (asset) {
445
- case PurchaseAsset.SOL: {
446
- // lamports * solPriceUsd / 1e9 → 1e8 USD
447
- notionalUsd = (amount * solPriceUsd) / ONE_E9;
448
- break;
449
- }
450
-
451
- case PurchaseAsset.LIQSOL: {
452
- // liqSOL also uses 9 decimals; use same conversion.
453
- notionalUsd = (amount * solPriceUsd) / ONE_E9;
454
- break;
455
- }
456
-
457
- case PurchaseAsset.YIELD: {
458
- // Treat amount as lamports-equivalent of SOL yield (UI convention).
459
- notionalUsd = (amount * solPriceUsd) / ONE_E9;
460
- break;
461
- }
462
-
463
- case PurchaseAsset.ETH:
464
- case PurchaseAsset.LIQETH:
465
- throw new Error('getBuyQuote for ETH/LIQETH is not supported on Solana');
466
-
467
- default:
468
- throw new Error(`Unsupported purchase asset: ${String(asset)}`);
469
- }
470
-
471
- // WIRE shares (1e8) = (notionalUsd * 1e8) / wirePriceUsd
472
- // Add a small bias to avoid truncating to 0 on tiny buys.
473
- const numerator = notionalUsd * ONE_E8;
474
- const wireShares =
475
- numerator === BigInt(0)
476
- ? BigInt(0)
477
- : (numerator + wirePriceUsd - BigInt(1)) / wirePriceUsd;
478
-
479
- return {
480
- purchaseAsset: asset,
481
- amountIn: amount,
482
- wireShares,
483
- wireDecimals: 8,
484
- wirePriceUsd,
485
- notionalUsd,
486
- };
487
- }
488
-
489
375
  /**
490
376
  * Convenience helper to fetch the distribution userRecord for the current user.
491
377
  * Used by balance-correction flows and debugging.
492
378
  */
493
379
  async getUserRecord() {
380
+ if (!this.pubKey) throw new Error('User pubKey is undefined');
494
381
  return this.distributionClient.getUserRecord(this.solPubKey);
495
382
  }
496
383
 
@@ -500,6 +387,7 @@ export class SolanaStakingClient implements IStakingClient {
500
387
  * - signs and sends the transaction if it can succeed
501
388
  */
502
389
  async correctBalance(amount?: bigint): Promise<string> {
390
+ this.ensureWriteAccess();
503
391
  const build = await this.distributionClient.buildCorrectRegisterTx({ amount });
504
392
  if (!build.canSucceed || !build.transaction) {
505
393
  throw new Error(build.reason ?? 'Unable to build Correct&Register transaction');
@@ -528,7 +416,8 @@ export class SolanaStakingClient implements IStakingClient {
528
416
  signed: SolanaTransaction,
529
417
  ctx: { blockhash: string; lastValidBlockHeight: number },
530
418
  ): Promise<string> {
531
- const signature = await this.connection.sendRawTransaction(
419
+ this.ensureWriteAccess();
420
+ const signature = await this.connection.sendRawTransaction(
532
421
  signed.serialize(),
533
422
  {
534
423
  skipPreflight: false,
@@ -557,6 +446,7 @@ export class SolanaStakingClient implements IStakingClient {
557
446
  * Sign a single Solana transaction using the connected wallet adapter.
558
447
  */
559
448
  async signTransaction(tx: SolanaTransaction): Promise<SolanaTransaction> {
449
+ this.ensureWriteAccess();
560
450
  return this.anchor.wallet.signTransaction(tx);
561
451
  }
562
452
 
@@ -565,6 +455,7 @@ export class SolanaStakingClient implements IStakingClient {
565
455
  * prepared and signed the transaction.
566
456
  */
567
457
  async sendTransaction(signed: SolanaTransaction): Promise<TransactionSignature> {
458
+ this.ensureWriteAccess();
568
459
  return this.anchor.sendAndConfirm(signed);
569
460
  }
570
461
 
@@ -581,4 +472,11 @@ export class SolanaStakingClient implements IStakingClient {
581
472
  tx.feePayer = this.solPubKey;
582
473
  return { tx, blockhash, lastValidBlockHeight };
583
474
  }
475
+
476
+ ensureWriteAccess() {
477
+ if (!this.pubKey || !this.anchor.wallet.publicKey)
478
+ throw new Error('User Authorization required: pubKey is undefined');
479
+ if (this.solPubKey.toBase58() !== this.anchor.wallet.publicKey.toBase58())
480
+ throw new Error('Write access requires connected wallet to match pubKey');
481
+ }
584
482
  }
@@ -99,7 +99,7 @@ export type GlobalState = {
99
99
  export type UserWarrantRecord = {
100
100
  user: PublicKey;
101
101
  totalSolDeposited: BN;
102
- totalWarrantsPurchased: BN;
102
+ totalPretokensPurchased: BN;
103
103
  lastTrancheNumber: BN;
104
104
  lastTranchePriceUsd: BN;
105
105
  bump: number;
@@ -110,7 +110,11 @@ export type TrancheState = {
110
110
  currentTrancheNumber: BN;
111
111
  currentTrancheSupply: BN;
112
112
  currentTranchePriceUsd: BN;
113
- totalWarrantsSold: BN;
113
+ //Currently working on renaming Warrants to Pretokens,
114
+ // current version of sol contracts appears to still
115
+ // be using warrants phrasing for this variable
116
+ totalPretokensSold?: BN;
117
+ totalWarrantsSold?: BN;
114
118
  initialTrancheSupply: BN;
115
119
  supplyGrowthBps: number;
116
120
  priceGrowthBps: number;