@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.
- package/lib/stake.browser.js +12887 -10017
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +3305 -1364
- package/lib/stake.js +16298 -13436
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +12887 -10017
- package/lib/stake.m.js.map +1 -1
- package/package.json +3 -1
- package/src/assets/solana/idl/liqsol_core.json +2327 -887
- package/src/assets/solana/idl/liqsol_token.json +1 -1
- package/src/assets/solana/idl/transfer_hook.json +192 -0
- package/src/assets/solana/idl/validator_leaderboard.json +147 -4
- package/src/assets/solana/types/liqsol_core.ts +2327 -887
- package/src/assets/solana/types/liqsol_token.ts +1 -1
- package/src/assets/solana/types/transfer_hook.ts +198 -0
- package/src/assets/solana/types/validator_leaderboard.ts +147 -4
- package/src/networks/ethereum/clients/{deposit.client.ts → convert.client.ts} +36 -4
- package/src/networks/ethereum/clients/opp.client.ts +390 -0
- package/src/networks/ethereum/clients/pretoken.client.ts +88 -49
- package/src/networks/ethereum/clients/receipt.client.ts +129 -0
- package/src/networks/ethereum/clients/stake.client.ts +1 -148
- package/src/networks/ethereum/contract.ts +7 -4
- package/src/networks/ethereum/ethereum.ts +44 -70
- package/src/networks/ethereum/types.ts +1 -0
- package/src/networks/ethereum/utils.ts +1 -1
- package/src/networks/solana/clients/deposit.client.ts +154 -8
- package/src/networks/solana/clients/distribution.client.ts +72 -291
- package/src/networks/solana/clients/leaderboard.client.ts +59 -14
- package/src/networks/solana/clients/outpost.client.ts +188 -359
- package/src/networks/solana/clients/token.client.ts +85 -100
- package/src/networks/solana/constants.ts +155 -64
- package/src/networks/solana/solana.ts +273 -154
- package/src/networks/solana/types.ts +532 -71
- package/src/networks/solana/utils.ts +68 -51
- package/src/types.ts +161 -17
- 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
|
-
* -
|
|
56
|
-
* -
|
|
57
|
-
* -
|
|
58
|
-
* - Unified portfolio + tranche snapshot +
|
|
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
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
85
|
-
if (!config.network.rpcUrls.length)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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, {
|
|
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 } =
|
|
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
|
-
*
|
|
154
|
-
*
|
|
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(
|
|
221
|
+
async withdraw(amountLamports: bigint): Promise<string> {
|
|
157
222
|
this.ensureWriteAccess();
|
|
158
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
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
|
-
|
|
256
|
+
|
|
257
|
+
return this.sendAndConfirmHttp(signed, prepared);
|
|
180
258
|
}
|
|
181
259
|
|
|
182
260
|
/**
|
|
183
|
-
* Unstake liqSOL from Outpost (pool
|
|
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
|
-
|
|
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
|
-
|
|
279
|
+
|
|
280
|
+
return this.sendAndConfirmHttp(signed, prepared);
|
|
201
281
|
}
|
|
202
282
|
|
|
203
283
|
/**
|
|
204
|
-
* Buy prelaunch WIRE “pretokens” using
|
|
284
|
+
* Buy prelaunch WIRE “pretokens” using liqSOL.
|
|
205
285
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
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
|
|
219
|
-
const ix = await this.tokenClient.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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:
|
|
234
|
-
* -
|
|
235
|
-
* -
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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.
|
|
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
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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:
|
|
410
|
+
symbol: 'SOL',
|
|
290
411
|
decimals: 9,
|
|
291
412
|
},
|
|
292
413
|
liq: {
|
|
293
414
|
amount: BigInt(actualAmountStr),
|
|
294
|
-
symbol:
|
|
415
|
+
symbol: 'LiqSOL',
|
|
295
416
|
decimals: LIQSOL_DECIMALS,
|
|
296
417
|
ata: userLiqsolAta,
|
|
297
418
|
},
|
|
298
419
|
staked: {
|
|
299
|
-
|
|
300
|
-
|
|
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
|
|
305
|
-
amount: BigInt(
|
|
306
|
-
symbol:
|
|
426
|
+
// Prelaunch WIRE pretokens (1e8 scale)
|
|
427
|
+
amount: BigInt(wirePretokensStr),
|
|
428
|
+
symbol: '$WIRE',
|
|
307
429
|
decimals: 8,
|
|
308
430
|
},
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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:
|
|
323
|
-
|
|
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
|
|
462
|
+
* - PriceHistory/Chainlink SOL/USD via TokenClient.getSolPriceUsdSafe()
|
|
336
463
|
*
|
|
337
|
-
*
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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<{
|
|
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
|
-
|
|
481
|
-
|
|
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
|
}
|