@wireio/stake 0.1.2 → 0.2.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.
- package/lib/stake.browser.js +2360 -1742
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +4796 -82
- package/lib/stake.js +8661 -7954
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +2360 -1742
- package/lib/stake.m.js.map +1 -1
- package/package.json +1 -1
- package/src/assets/solana/idl/liqsol_core.json +1369 -1129
- package/src/assets/solana/idl/liqsol_token.json +1 -1
- package/src/assets/solana/idl/validator_leaderboard.json +1 -1
- package/src/assets/solana/types/liqsol_core.ts +1369 -1129
- package/src/assets/solana/types/liqsol_token.ts +1 -1
- package/src/assets/solana/types/validator_leaderboard.ts +1 -1
- package/src/index.ts +1 -0
- package/src/networks/ethereum/ethereum.ts +74 -51
- package/src/networks/solana/clients/deposit.client.ts +7 -11
- package/src/networks/solana/clients/distribution.client.ts +1 -3
- package/src/networks/solana/clients/outpost.client.ts +474 -0
- package/src/networks/solana/constants.ts +90 -52
- package/src/networks/solana/solana.ts +155 -128
- package/src/networks/solana/types.ts +95 -8
- package/src/networks/solana/utils.ts +111 -94
- package/src/staker/staker.ts +1 -1
- package/src/staker/types.ts +7 -5
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { AnchorProvider, BN, Program } from '@coral-xyz/anchor';
|
|
2
|
+
import {
|
|
3
|
+
Connection,
|
|
4
|
+
PublicKey,
|
|
5
|
+
SystemProgram,
|
|
6
|
+
Transaction,
|
|
7
|
+
TransactionInstruction,
|
|
8
|
+
} from '@solana/web3.js';
|
|
9
|
+
import {
|
|
10
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
11
|
+
TOKEN_2022_PROGRAM_ID,
|
|
12
|
+
createAssociatedTokenAccountInstruction,
|
|
13
|
+
} from '@solana/spl-token';
|
|
14
|
+
import {
|
|
15
|
+
buildOutpostAccounts,
|
|
16
|
+
getLiqsolCoreProgram,
|
|
17
|
+
getErrorMessage,
|
|
18
|
+
} from '../utils';
|
|
19
|
+
import { SolanaProgramService } from '../program';
|
|
20
|
+
import { LiqsolCore } from '../../../assets/solana/types/liqsol_core';
|
|
21
|
+
import { WalletLike, OutpostWireStateSnapshot, SharesPreview, UserWarrantRecord, TrancheState, WireReceipt } from '../types';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Minimal, user-flow focused OutpostClient.
|
|
25
|
+
* - Read state
|
|
26
|
+
* - Build + (optionally) send user instructions
|
|
27
|
+
*
|
|
28
|
+
* No admin/bootstrap/reset helpers by design.
|
|
29
|
+
*/
|
|
30
|
+
export class OutpostClient {
|
|
31
|
+
private readonly program: Program<LiqsolCore>;
|
|
32
|
+
|
|
33
|
+
// keep consistent with your tests
|
|
34
|
+
static readonly INDEX_SCALE = new BN('1000000000000');
|
|
35
|
+
|
|
36
|
+
get connection(): Connection { return this.provider.connection; }
|
|
37
|
+
get wallet(): WalletLike { return this.provider.wallet; }
|
|
38
|
+
|
|
39
|
+
constructor(private provider: AnchorProvider) {
|
|
40
|
+
const svc = new SolanaProgramService(provider);
|
|
41
|
+
this.program = svc.getProgram('liqsolCore');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Read-only Program helper (dummy wallet), useful if you want to do pure reads
|
|
46
|
+
* without requiring a wallet. Kept here as sugar for callers.
|
|
47
|
+
*/
|
|
48
|
+
getReadOnlyProgram(): Program<LiqsolCore> {
|
|
49
|
+
return getLiqsolCoreProgram(this.connection);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// General-purpose formatting + math (SDK sugar)
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Same rounding logic from your tests: ceil(amount * INDEX_SCALE / currentIndex)
|
|
58
|
+
*/
|
|
59
|
+
static tokensToShares(amount: BN, currentIndex: BN): BN {
|
|
60
|
+
const numerator = amount.mul(OutpostClient.INDEX_SCALE);
|
|
61
|
+
const shares = numerator.div(currentIndex);
|
|
62
|
+
const remainder = numerator.mod(currentIndex);
|
|
63
|
+
return remainder.eqn(0) ? shares : shares.addn(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* UI helper: formats a BN assumed to be scale=1e8 into a decimal string.
|
|
68
|
+
* (Matches your test helper; useful for tranche price, USD price, etc.)
|
|
69
|
+
*/
|
|
70
|
+
static formatScale8(value: BN): string {
|
|
71
|
+
const decimals = 8;
|
|
72
|
+
const valueStr = value.toString();
|
|
73
|
+
if (valueStr.length <= decimals) {
|
|
74
|
+
const padding = '0'.repeat(decimals - valueStr.length);
|
|
75
|
+
return `0.${padding}${valueStr}`;
|
|
76
|
+
}
|
|
77
|
+
const intPart = valueStr.slice(0, valueStr.length - decimals);
|
|
78
|
+
const decPart = valueStr.slice(valueStr.length - decimals);
|
|
79
|
+
return `${intPart}.${decPart}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Previews expected shares for a purchase/stake flow that mints shares
|
|
84
|
+
* relative to globalState.currentIndex.
|
|
85
|
+
*/
|
|
86
|
+
async previewExpectedShares(params: {
|
|
87
|
+
amountLamports: BN;
|
|
88
|
+
user?: PublicKey; // optional; defaults to wallet.publicKey
|
|
89
|
+
}): Promise<SharesPreview> {
|
|
90
|
+
const user = params.user ?? this.wallet.publicKey;
|
|
91
|
+
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
92
|
+
const globalState = await this.program.account.globalState.fetch(accounts.globalState);
|
|
93
|
+
const currentIndex = new BN(globalState.currentIndex.toString());
|
|
94
|
+
const expectedShares = OutpostClient.tokensToShares(
|
|
95
|
+
params.amountLamports,
|
|
96
|
+
currentIndex,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
amountLamports: params.amountLamports,
|
|
101
|
+
currentIndex,
|
|
102
|
+
indexScale: OutpostClient.INDEX_SCALE,
|
|
103
|
+
expectedShares,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Account helpers (user-facing, not admin bootstrap)
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Returns whether the user's liqSOL ATA exists.
|
|
113
|
+
* Useful for UX (show "Create token account" prompt).
|
|
114
|
+
*/
|
|
115
|
+
async doesUserAtaExist(user: PublicKey = this.wallet.publicKey): Promise<boolean> {
|
|
116
|
+
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
117
|
+
const info = await this.connection.getAccountInfo(accounts.userAta);
|
|
118
|
+
return !!info;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Builds an instruction to create the user's liqSOL ATA (Token-2022).
|
|
123
|
+
* This is general-purpose sugar for any flow that needs the ATA.
|
|
124
|
+
*/
|
|
125
|
+
async buildCreateUserAtaIx(user: PublicKey = this.wallet.publicKey): Promise<TransactionInstruction> {
|
|
126
|
+
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
127
|
+
|
|
128
|
+
return createAssociatedTokenAccountInstruction(
|
|
129
|
+
this.wallet.publicKey, // payer
|
|
130
|
+
accounts.userAta, // ata
|
|
131
|
+
user, // owner
|
|
132
|
+
accounts.liqsolMint, // mint
|
|
133
|
+
TOKEN_2022_PROGRAM_ID,
|
|
134
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Convenience: ensures ATA exists by returning either [] or [createATAIx].
|
|
140
|
+
* (Does NOT send; caller composes tx)
|
|
141
|
+
*/
|
|
142
|
+
async maybeBuildCreateUserAtaIx(user: PublicKey = this.wallet.publicKey): Promise<TransactionInstruction[]> {
|
|
143
|
+
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
144
|
+
const info = await this.connection.getAccountInfo(accounts.userAta);
|
|
145
|
+
if (info) return [];
|
|
146
|
+
return [await this.buildCreateUserAtaIx(user)];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Reads (UI-first)
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
async getWireStateSnapshot(user: PublicKey = this.wallet.publicKey): Promise<OutpostWireStateSnapshot> {
|
|
154
|
+
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
155
|
+
|
|
156
|
+
const [
|
|
157
|
+
globalState,
|
|
158
|
+
wireReceipt,
|
|
159
|
+
distributionState,
|
|
160
|
+
userWarrantRecord,
|
|
161
|
+
trancheState,
|
|
162
|
+
] = await Promise.all([
|
|
163
|
+
this.program.account.globalState.fetch(accounts.globalState),
|
|
164
|
+
this.program.account.wireReceipt.fetchNullable(accounts.wireReceipt),
|
|
165
|
+
this.program.account.distributionState.fetch(accounts.distributionState),
|
|
166
|
+
this.program.account.userWarrantRecord.fetchNullable(accounts.userWarrantRecord),
|
|
167
|
+
this.program.account.trancheState.fetchNullable(accounts.trancheState),
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
const [
|
|
171
|
+
liqsolPoolBalance,
|
|
172
|
+
solBucketLamports,
|
|
173
|
+
userLiqsolBalance,
|
|
174
|
+
] = await Promise.all([
|
|
175
|
+
this.getTokenBalanceSafe(accounts.liqsolPoolAta),
|
|
176
|
+
this.connection.getBalance(accounts.solBucket),
|
|
177
|
+
this.getTokenBalanceSafe(accounts.userAta),
|
|
178
|
+
]);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
globalState,
|
|
182
|
+
distributionState,
|
|
183
|
+
wireReceipt,
|
|
184
|
+
trancheState,
|
|
185
|
+
userWarrantRecord,
|
|
186
|
+
liqsolPoolBalance,
|
|
187
|
+
solBucketLamports,
|
|
188
|
+
userLiqsolBalance,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async getWireReceipt(user: PublicKey = this.wallet.publicKey): Promise<WireReceipt | null> {
|
|
193
|
+
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
194
|
+
return this.program.account.wireReceipt.fetchNullable(accounts.wireReceipt);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async getTrancheState(): Promise<TrancheState | null> {
|
|
198
|
+
const accounts = await buildOutpostAccounts(this.connection, this.wallet.publicKey);
|
|
199
|
+
return this.program.account.trancheState.fetchNullable(accounts.trancheState);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async getUserWarrantRecord(user: PublicKey = this.wallet.publicKey): Promise<UserWarrantRecord | null> {
|
|
203
|
+
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
204
|
+
return this.program.account.userWarrantRecord.fetchNullable(accounts.userWarrantRecord);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Instruction builders (source of truth: your test utils)
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* stakeLiqsol(amount)
|
|
213
|
+
* (Outpost staking: liqSOL -> warrant pool)
|
|
214
|
+
*/
|
|
215
|
+
async buildStakeLiqsolIx(amountLamports: bigint, userPubKey = this.wallet.publicKey): Promise<TransactionInstruction> {
|
|
216
|
+
const a = await buildOutpostAccounts(this.connection, userPubKey);
|
|
217
|
+
const amount = new BN(amountLamports.toString());
|
|
218
|
+
|
|
219
|
+
return this.program.methods
|
|
220
|
+
.stakeLiqsol(amount)
|
|
221
|
+
.accounts({
|
|
222
|
+
user: a.user,
|
|
223
|
+
liqsolMint: a.liqsolMint,
|
|
224
|
+
globalState: a.globalState,
|
|
225
|
+
userAta: a.userAta,
|
|
226
|
+
poolAuthority: a.poolAuthority,
|
|
227
|
+
liqsolPoolAta: a.liqsolPoolAta,
|
|
228
|
+
warrantDepositRecord: a.wireReceipt,
|
|
229
|
+
liqsolPoolUserRecord: a.poolUserRecord,
|
|
230
|
+
distributionState: a.distributionState,
|
|
231
|
+
payRateHistory: a.payRateHistory,
|
|
232
|
+
bucketAuthority: a.bucketAuthority,
|
|
233
|
+
bucketTokenAccount: a.bucketTokenAccount,
|
|
234
|
+
solBucket: a.solBucket,
|
|
235
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
236
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
237
|
+
systemProgram: SystemProgram.programId,
|
|
238
|
+
})
|
|
239
|
+
.instruction();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* withdrawStake(amount)
|
|
244
|
+
* (Outpost unstake: warrant pool -> user liqSOL)
|
|
245
|
+
*/
|
|
246
|
+
async buildWithdrawStakeIx(amountLamports: bigint, userPubKey = this.wallet.publicKey): Promise<TransactionInstruction> {
|
|
247
|
+
const a = await buildOutpostAccounts(this.connection, userPubKey);
|
|
248
|
+
const amount = new BN(amountLamports.toString());
|
|
249
|
+
|
|
250
|
+
return this.program.methods
|
|
251
|
+
.withdrawStake(amount)
|
|
252
|
+
.accounts({
|
|
253
|
+
user: a.user,
|
|
254
|
+
liqsolMint: a.liqsolMint,
|
|
255
|
+
globalState: a.globalState,
|
|
256
|
+
userAta: a.userAta,
|
|
257
|
+
poolAuthority: a.poolAuthority,
|
|
258
|
+
liqsolPoolAta: a.liqsolPoolAta,
|
|
259
|
+
warrantDepositRecord: a.wireReceipt,
|
|
260
|
+
liqsolPoolUserRecord: a.poolUserRecord,
|
|
261
|
+
distributionState: a.distributionState,
|
|
262
|
+
payRateHistory: a.payRateHistory,
|
|
263
|
+
bucketAuthority: a.bucketAuthority,
|
|
264
|
+
bucketTokenAccount: a.bucketTokenAccount,
|
|
265
|
+
solBucket: a.solBucket,
|
|
266
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
267
|
+
systemProgram: SystemProgram.programId,
|
|
268
|
+
})
|
|
269
|
+
.instruction();
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* purchaseWithLiqsol(amount)
|
|
274
|
+
*/
|
|
275
|
+
async buildPurchaseWithLiqsolIx(amountLamports: bigint, userPubKey = this.wallet.publicKey): Promise<TransactionInstruction> {
|
|
276
|
+
const a = await buildOutpostAccounts(this.connection, userPubKey);
|
|
277
|
+
const amount = new BN(amountLamports.toString());
|
|
278
|
+
|
|
279
|
+
return this.program.methods
|
|
280
|
+
.purchaseWithLiqsol(amount)
|
|
281
|
+
.accounts({
|
|
282
|
+
user: a.user,
|
|
283
|
+
liqsolMint: a.liqsolMint,
|
|
284
|
+
globalState: a.globalState,
|
|
285
|
+
buyerAta: a.userAta,
|
|
286
|
+
poolAuthority: a.poolAuthority,
|
|
287
|
+
liqsolPoolAta: a.liqsolPoolAta,
|
|
288
|
+
warrantDepositRecord: a.wireReceipt,
|
|
289
|
+
liqsolPoolUserRecord: a.poolUserRecord,
|
|
290
|
+
distributionState: a.distributionState,
|
|
291
|
+
payRateHistory: a.payRateHistory,
|
|
292
|
+
bucketAuthority: a.bucketAuthority,
|
|
293
|
+
bucketTokenAccount: a.bucketTokenAccount,
|
|
294
|
+
solBucket: a.solBucket,
|
|
295
|
+
trancheState: a.trancheState,
|
|
296
|
+
userWarrantRecord: a.userWarrantRecord,
|
|
297
|
+
chainlinkFeed: a.chainLinkFeed,
|
|
298
|
+
chainlinkProgram: a.chainLinkProgram,
|
|
299
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
300
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
301
|
+
systemProgram: SystemProgram.programId,
|
|
302
|
+
})
|
|
303
|
+
.instruction();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* purchaseWithSol(amount)
|
|
308
|
+
*/
|
|
309
|
+
async buildPurchaseWithSolIx(amountLamports: bigint, userPubKey = this.wallet.publicKey): Promise<TransactionInstruction> {
|
|
310
|
+
const a = await buildOutpostAccounts(this.connection, userPubKey);
|
|
311
|
+
const amount = new BN(amountLamports.toString());
|
|
312
|
+
|
|
313
|
+
return this.program.methods
|
|
314
|
+
.purchaseWithSol(amount)
|
|
315
|
+
.accounts({
|
|
316
|
+
user: a.user,
|
|
317
|
+
liqsolMint: a.liqsolMint,
|
|
318
|
+
globalState: a.globalState,
|
|
319
|
+
poolAuthority: a.poolAuthority,
|
|
320
|
+
liqsolPoolAta: a.liqsolPoolAta,
|
|
321
|
+
liqsolPoolUserRecord: a.poolUserRecord,
|
|
322
|
+
distributionState: a.distributionState,
|
|
323
|
+
payRateHistory: a.payRateHistory,
|
|
324
|
+
bucketAuthority: a.bucketAuthority,
|
|
325
|
+
bucketTokenAccount: a.bucketTokenAccount,
|
|
326
|
+
solBucket: a.solBucket,
|
|
327
|
+
warrantDepositRecord: a.wireReceipt,
|
|
328
|
+
trancheState: a.trancheState,
|
|
329
|
+
userWarrantRecord: a.userWarrantRecord,
|
|
330
|
+
chainlinkFeed: a.chainLinkFeed,
|
|
331
|
+
chainlinkProgram: a.chainLinkProgram,
|
|
332
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
333
|
+
systemProgram: SystemProgram.programId,
|
|
334
|
+
})
|
|
335
|
+
.instruction();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* purchaseWarrantsFromYield()
|
|
340
|
+
*
|
|
341
|
+
* Source of truth: your test util executePurchaseFromYield (note: it passed
|
|
342
|
+
* liqsolPoolUserRecord: pdas.userUserRecord, not poolUserRecord).
|
|
343
|
+
*/
|
|
344
|
+
async buildPurchaseFromYieldIx(userPubKey = this.wallet.publicKey): Promise<TransactionInstruction> {
|
|
345
|
+
const a = await buildOutpostAccounts(this.connection, userPubKey);
|
|
346
|
+
|
|
347
|
+
return this.program.methods
|
|
348
|
+
.purchaseWarrantsFromYield()
|
|
349
|
+
.accounts({
|
|
350
|
+
user: a.user,
|
|
351
|
+
globalState: a.globalState,
|
|
352
|
+
liqsolMint: a.liqsolMint,
|
|
353
|
+
poolAuthority: a.poolAuthority,
|
|
354
|
+
liqsolPoolAta: a.liqsolPoolAta,
|
|
355
|
+
solBucket: a.solBucket,
|
|
356
|
+
liqsolPoolUserRecord: a.userUserRecord,
|
|
357
|
+
distributionState: a.distributionState,
|
|
358
|
+
payRateHistory: a.payRateHistory,
|
|
359
|
+
bucketAuthority: a.bucketAuthority,
|
|
360
|
+
bucketTokenAccount: a.bucketTokenAccount,
|
|
361
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
362
|
+
systemProgram: SystemProgram.programId,
|
|
363
|
+
trancheState: a.trancheState,
|
|
364
|
+
userWarrantRecord: a.userWarrantRecord,
|
|
365
|
+
chainlinkFeed: a.chainLinkFeed,
|
|
366
|
+
chainlinkProgram: a.chainLinkProgram,
|
|
367
|
+
})
|
|
368
|
+
.instruction();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ---------------------------------------------------------------------------
|
|
372
|
+
// Optional send helpers (wallet-facing sugar)
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Builds and sends a single-instruction transaction.
|
|
377
|
+
* (Hub can ignore this and use builder methods only.)
|
|
378
|
+
*/
|
|
379
|
+
async sendIx(ix: TransactionInstruction, opts?: {
|
|
380
|
+
additionalIxs?: TransactionInstruction[];
|
|
381
|
+
}): Promise<string> {
|
|
382
|
+
const tx = new Transaction();
|
|
383
|
+
if (opts?.additionalIxs?.length) {
|
|
384
|
+
tx.add(...opts.additionalIxs);
|
|
385
|
+
}
|
|
386
|
+
tx.add(ix);
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const sig = await this.provider.sendAndConfirm(tx, [], { commitment: 'confirmed' });
|
|
390
|
+
return sig;
|
|
391
|
+
} catch (e: any) {
|
|
392
|
+
const msg = getErrorMessage(e);
|
|
393
|
+
throw new Error(`OutpostClient sendIx failed: ${msg}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Convenience: stake liqSOL and (optionally) create ATA first.
|
|
399
|
+
*/
|
|
400
|
+
async stakeLiqsol(params: {
|
|
401
|
+
amount: BN;
|
|
402
|
+
user?: PublicKey;
|
|
403
|
+
ensureAta?: boolean; // default true
|
|
404
|
+
}): Promise<string> {
|
|
405
|
+
const user = params.user ?? this.wallet.publicKey;
|
|
406
|
+
const pre: TransactionInstruction[] = [];
|
|
407
|
+
if (params.ensureAta ?? true) {
|
|
408
|
+
pre.push(...(await this.maybeBuildCreateUserAtaIx(user)));
|
|
409
|
+
}
|
|
410
|
+
const ix = await this.buildStakeLiqsolIx(params.amount, user);
|
|
411
|
+
return this.sendIx(ix, { additionalIxs: pre });
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async withdrawStake(params: {
|
|
415
|
+
amount: BN;
|
|
416
|
+
user?: PublicKey;
|
|
417
|
+
ensureAta?: boolean; // default true (usually exists, but harmless)
|
|
418
|
+
}): Promise<string> {
|
|
419
|
+
const user = params.user ?? this.wallet.publicKey;
|
|
420
|
+
const pre: TransactionInstruction[] = [];
|
|
421
|
+
if (params.ensureAta ?? true) {
|
|
422
|
+
pre.push(...(await this.maybeBuildCreateUserAtaIx(user)));
|
|
423
|
+
}
|
|
424
|
+
const ix = await this.buildWithdrawStakeIx(params.amount, user);
|
|
425
|
+
return this.sendIx(ix, { additionalIxs: pre });
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async purchaseWithLiqsol(params: {
|
|
429
|
+
amount: BN;
|
|
430
|
+
user?: PublicKey;
|
|
431
|
+
ensureAta?: boolean; // default true
|
|
432
|
+
}): Promise<string> {
|
|
433
|
+
const user = params.user ?? this.wallet.publicKey;
|
|
434
|
+
const pre: TransactionInstruction[] = [];
|
|
435
|
+
if (params.ensureAta ?? true) {
|
|
436
|
+
pre.push(...(await this.maybeBuildCreateUserAtaIx(user)));
|
|
437
|
+
}
|
|
438
|
+
const ix = await this.buildPurchaseWithLiqsolIx(params.amount, user);
|
|
439
|
+
return this.sendIx(ix, { additionalIxs: pre });
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async purchaseWithSol(params: {
|
|
443
|
+
amount: BN;
|
|
444
|
+
user?: PublicKey;
|
|
445
|
+
}): Promise<string> {
|
|
446
|
+
const user = params.user ?? this.wallet.publicKey;
|
|
447
|
+
const ix = await this.buildPurchaseWithSolIx(params.amount, user);
|
|
448
|
+
return this.sendIx(ix);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async purchaseFromYield(params?: {
|
|
452
|
+
user?: PublicKey;
|
|
453
|
+
}): Promise<string> {
|
|
454
|
+
const user = params?.user ?? this.wallet.publicKey;
|
|
455
|
+
const ix = await this.buildPurchaseFromYieldIx(user);
|
|
456
|
+
return this.sendIx(ix);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ---------------------------------------------------------------------------
|
|
460
|
+
// Internals
|
|
461
|
+
// ---------------------------------------------------------------------------
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Token balance helper returning BN(0) if ATA missing.
|
|
465
|
+
*/
|
|
466
|
+
private async getTokenBalanceSafe(ata: PublicKey): Promise<BN> {
|
|
467
|
+
try {
|
|
468
|
+
const bal = await this.connection.getTokenAccountBalance(ata);
|
|
469
|
+
return new BN(bal.value.amount);
|
|
470
|
+
} catch {
|
|
471
|
+
return new BN(0);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
@@ -43,7 +43,7 @@ export const PDA_SEEDS = {
|
|
|
43
43
|
VAULT: 'vault',
|
|
44
44
|
RESERVE_POOL: 'reserve_pool',
|
|
45
45
|
STAKE_CONTROLLER_STATE: 'stake_controller',
|
|
46
|
-
PAYOUT_STATE: '
|
|
46
|
+
PAYOUT_STATE: 'payout_state',
|
|
47
47
|
|
|
48
48
|
// liqsol_token: mint + bucket
|
|
49
49
|
LIQSOL_MINT: 'liqsol_mint',
|
|
@@ -61,12 +61,24 @@ export const PDA_SEEDS = {
|
|
|
61
61
|
LEADERBOARD_STATE: 'leaderboard_state',
|
|
62
62
|
VALIDATOR_RECORD: 'validator',
|
|
63
63
|
GLOBAL_STAKE_INFO: 'global_stake_info',
|
|
64
|
+
|
|
65
|
+
// Outpost
|
|
66
|
+
WARRANT_DEPOSIT_GLOBAL: 'warrant_deposit_global',
|
|
67
|
+
LIQSOL_POOL: 'liqsol_pool',
|
|
68
|
+
WARRANT_DEPOSIT_RECORD: 'warrant_deposit_record',
|
|
69
|
+
WIRE_SOL_BUCKET: 'wire_sol_bucket',
|
|
70
|
+
TRANCHE_STATE: 'tranche_state',
|
|
71
|
+
USER_WARRANT_RECORD: 'user_warrant_record',
|
|
72
|
+
|
|
73
|
+
// BAR
|
|
74
|
+
BAR_STATE_SEED: 'bar_state',
|
|
75
|
+
BONDED_ACTOR_SEED: 'bonded_actor',
|
|
76
|
+
BOND_LEVEL_SEED: 'bond_level',
|
|
64
77
|
} as const;
|
|
65
78
|
|
|
66
79
|
/**
|
|
67
80
|
* Helpers for PDA derivation so clients don’t duplicate logic.
|
|
68
81
|
*/
|
|
69
|
-
|
|
70
82
|
export const deriveLiqsolMintPda = () =>
|
|
71
83
|
PublicKey.findProgramAddressSync(
|
|
72
84
|
[Buffer.from(PDA_SEEDS.LIQSOL_MINT)],
|
|
@@ -133,12 +145,6 @@ export const derivePayRateHistoryPda = () =>
|
|
|
133
145
|
PROGRAM_IDS.LIQSOL_CORE,
|
|
134
146
|
)[0];
|
|
135
147
|
|
|
136
|
-
// export const deriveGlobalStakeInfoPda = () =>
|
|
137
|
-
// PublicKey.findProgramAddressSync(
|
|
138
|
-
// [Buffer.from(PDA_SEEDS.GLOBAL_STAKE_INFO)],
|
|
139
|
-
// PROGRAM_IDS.LIQSOL_CORE,
|
|
140
|
-
// )[0];
|
|
141
|
-
|
|
142
148
|
export const deriveLeaderboardStatePda = () =>
|
|
143
149
|
PublicKey.findProgramAddressSync(
|
|
144
150
|
[Buffer.from(PDA_SEEDS.LEADERBOARD_STATE)],
|
|
@@ -151,16 +157,81 @@ export const deriveValidatorRecordPda = (voteAccount: PublicKey) =>
|
|
|
151
157
|
PROGRAM_IDS.VALIDATOR_LEADERBOARD,
|
|
152
158
|
)[0];
|
|
153
159
|
|
|
154
|
-
/**
|
|
155
|
-
* Stake controller vault PDA (reserve pool SOL vault).
|
|
156
|
-
* This is the same as VAULT, but named more explicitly for SDK callers.
|
|
157
|
-
*/
|
|
158
160
|
export const deriveStakeControllerVaultPda = () =>
|
|
159
161
|
PublicKey.findProgramAddressSync(
|
|
160
162
|
[Buffer.from(PDA_SEEDS.VAULT)],
|
|
161
163
|
PROGRAM_IDS.LIQSOL_CORE,
|
|
162
164
|
)[0];
|
|
163
165
|
|
|
166
|
+
/**
|
|
167
|
+
* OUTPOST
|
|
168
|
+
*/
|
|
169
|
+
export const deriveOutpostGlobalStatePda = () =>
|
|
170
|
+
PublicKey.findProgramAddressSync(
|
|
171
|
+
[Buffer.from(PDA_SEEDS.WARRANT_DEPOSIT_GLOBAL)],
|
|
172
|
+
LIQSOL_CORE,
|
|
173
|
+
)[0];
|
|
174
|
+
|
|
175
|
+
export const deriveOutpostPoolAuthorityPda = () =>
|
|
176
|
+
PublicKey.findProgramAddressSync(
|
|
177
|
+
[Buffer.from(PDA_SEEDS.LIQSOL_POOL)],
|
|
178
|
+
LIQSOL_CORE,
|
|
179
|
+
)[0];
|
|
180
|
+
|
|
181
|
+
export const deriveWireReceiptPda = (user: PublicKey) =>
|
|
182
|
+
PublicKey.findProgramAddressSync(
|
|
183
|
+
[Buffer.from(PDA_SEEDS.WARRANT_DEPOSIT_RECORD), user.toBuffer()],
|
|
184
|
+
LIQSOL_CORE,
|
|
185
|
+
)[0];
|
|
186
|
+
|
|
187
|
+
export const derivePoolUserRecordPda = (poolAuthority: PublicKey) =>
|
|
188
|
+
PublicKey.findProgramAddressSync(
|
|
189
|
+
[Buffer.from(PDA_SEEDS.USER_RECORD), poolAuthority.toBuffer()],
|
|
190
|
+
LIQSOL_CORE,
|
|
191
|
+
)[0];
|
|
192
|
+
|
|
193
|
+
export const deriveUserUserRecordPda = (user: PublicKey) =>
|
|
194
|
+
PublicKey.findProgramAddressSync(
|
|
195
|
+
[Buffer.from(PDA_SEEDS.USER_RECORD), user.toBuffer()],
|
|
196
|
+
LIQSOL_CORE,
|
|
197
|
+
)[0];
|
|
198
|
+
|
|
199
|
+
export const deriveSolBucketPda = () =>
|
|
200
|
+
PublicKey.findProgramAddressSync(
|
|
201
|
+
[Buffer.from(PDA_SEEDS.WIRE_SOL_BUCKET)],
|
|
202
|
+
LIQSOL_CORE,
|
|
203
|
+
)[0];
|
|
204
|
+
|
|
205
|
+
export const deriveTrancheStatePda = () =>
|
|
206
|
+
PublicKey.findProgramAddressSync(
|
|
207
|
+
[Buffer.from(PDA_SEEDS.TRANCHE_STATE)],
|
|
208
|
+
LIQSOL_CORE,
|
|
209
|
+
)[0];
|
|
210
|
+
|
|
211
|
+
export const deriveUserWarrantRecordPda = (user: PublicKey) =>
|
|
212
|
+
PublicKey.findProgramAddressSync(
|
|
213
|
+
[Buffer.from(PDA_SEEDS.USER_WARRANT_RECORD), user.toBuffer()],
|
|
214
|
+
LIQSOL_CORE,
|
|
215
|
+
)[0];
|
|
216
|
+
|
|
217
|
+
export const deriveBarConfigPda = () =>
|
|
218
|
+
PublicKey.findProgramAddressSync(
|
|
219
|
+
[Buffer.from(PDA_SEEDS.BAR_STATE_SEED)],
|
|
220
|
+
LIQSOL_CORE,
|
|
221
|
+
)[0];
|
|
222
|
+
|
|
223
|
+
export const deriveBondLevelPda = (bondLevelId: number[]) =>
|
|
224
|
+
PublicKey.findProgramAddressSync(
|
|
225
|
+
[Buffer.from(PDA_SEEDS.BOND_LEVEL_SEED), Buffer.from(bondLevelId)],
|
|
226
|
+
LIQSOL_CORE,
|
|
227
|
+
)[0];
|
|
228
|
+
|
|
229
|
+
export const deriveBondedActorPda = (actor: PublicKey) =>
|
|
230
|
+
PublicKey.findProgramAddressSync(
|
|
231
|
+
[Buffer.from(PDA_SEEDS.BONDED_ACTOR_SEED), actor.toBuffer()],
|
|
232
|
+
LIQSOL_CORE,
|
|
233
|
+
)[0];
|
|
234
|
+
|
|
164
235
|
/**
|
|
165
236
|
* Ephemeral stake account address used per-deposit.
|
|
166
237
|
* On-chain convention: seed = `ephemeral_<u32>` under StakeProgram.programId.
|
|
@@ -173,7 +244,12 @@ export const deriveEphemeralStakeAddress = async (
|
|
|
173
244
|
return await PublicKey.createWithSeed(user, seedStr, StakeProgram.programId);
|
|
174
245
|
};
|
|
175
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Constant keys
|
|
249
|
+
*/
|
|
176
250
|
|
|
251
|
+
export const CHAINLINK_FEED = new PublicKey("99B2bTijsU6f1GCT73HmdR7HCFFjGMBcPZY6jZ96ynrR");
|
|
252
|
+
export const CHAINLINK_PROGRAM = new PublicKey("HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny");
|
|
177
253
|
|
|
178
254
|
/**
|
|
179
255
|
* ---------------------------------------------------------------------------
|
|
@@ -200,43 +276,5 @@ export const LAMPORTS_PER_SOL = 1_000_000_000;
|
|
|
200
276
|
export const lamportsToSol = (lamports: number | bigint): number =>
|
|
201
277
|
Number(lamports) / LAMPORTS_PER_SOL;
|
|
202
278
|
|
|
203
|
-
export const solToLamports = (sol: number):
|
|
204
|
-
Math.round(sol * LAMPORTS_PER_SOL);
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* ---------------------------------------------------------------------------
|
|
208
|
-
* CLUSTER / ENVIRONMENT CONFIG (optional but handy)
|
|
209
|
-
* ---------------------------------------------------------------------------
|
|
210
|
-
*/
|
|
211
|
-
|
|
212
|
-
export type SolanaCluster = 'localnet' | 'devnet' | 'mainnet';
|
|
213
|
-
|
|
214
|
-
// export interface SolanaStakeProgramAddresses {
|
|
215
|
-
// liqsolCore: PublicKey;
|
|
216
|
-
// liqsolToken: PublicKey;
|
|
217
|
-
// validatorLeaderboard: PublicKey;
|
|
218
|
-
// }
|
|
219
|
-
|
|
220
|
-
// export const PROGRAMS_BY_CLUSTER: Record<SolanaCluster, SolanaStakeProgramAddresses> = {
|
|
221
|
-
// localnet: {
|
|
222
|
-
// liqsolCore: PROGRAM_IDS.LIQSOL_CORE,
|
|
223
|
-
// liqsolToken: PROGRAM_IDS.LIQSOL_TOKEN,
|
|
224
|
-
// validatorLeaderboard: PROGRAM_IDS.VALIDATOR_LEADERBOARD,
|
|
225
|
-
// },
|
|
226
|
-
// devnet: {
|
|
227
|
-
// liqsolCore: PROGRAM_IDS.LIQSOL_CORE,
|
|
228
|
-
// liqsolToken: PROGRAM_IDS.LIQSOL_TOKEN,
|
|
229
|
-
// validatorLeaderboard: PROGRAM_IDS.VALIDATOR_LEADERBOARD,
|
|
230
|
-
// },
|
|
231
|
-
// mainnet: {
|
|
232
|
-
// liqsolCore: PROGRAM_IDS.LIQSOL_CORE,
|
|
233
|
-
// liqsolToken: PROGRAM_IDS.LIQSOL_TOKEN,
|
|
234
|
-
// validatorLeaderboard: PROGRAM_IDS.VALIDATOR_LEADERBOARD,
|
|
235
|
-
// },
|
|
236
|
-
// };
|
|
237
|
-
|
|
238
|
-
// export const DEFAULT_CLUSTER: SolanaCluster = 'localnet';
|
|
239
|
-
|
|
240
|
-
// export function getProgramIds(cluster: SolanaCluster = DEFAULT_CLUSTER): SolanaStakeProgramAddresses {
|
|
241
|
-
// return PROGRAMS_BY_CLUSTER[cluster];
|
|
242
|
-
// }
|
|
279
|
+
export const solToLamports = (sol: number): bigint =>
|
|
280
|
+
BigInt(Math.round(sol * LAMPORTS_PER_SOL));
|