@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,237 +1,173 @@
|
|
|
1
1
|
import { AnchorProvider, BN, Program } from '@coral-xyz/anchor';
|
|
2
2
|
import {
|
|
3
|
-
Connection,
|
|
4
3
|
PublicKey,
|
|
5
4
|
SystemProgram,
|
|
6
|
-
Transaction,
|
|
7
5
|
TransactionInstruction,
|
|
8
6
|
} from '@solana/web3.js';
|
|
9
7
|
import {
|
|
10
8
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
11
9
|
TOKEN_2022_PROGRAM_ID,
|
|
12
|
-
createAssociatedTokenAccountInstruction,
|
|
13
10
|
} from '@solana/spl-token';
|
|
11
|
+
|
|
12
|
+
import liqsolCoreIDL from '../../../assets/solana/idl/liqsol_core.json';
|
|
13
|
+
import { LiqsolCore } from '../../../assets/solana/types/liqsol_core';
|
|
14
|
+
|
|
14
15
|
import {
|
|
16
|
+
OutpostAccounts,
|
|
15
17
|
buildOutpostAccounts,
|
|
16
|
-
getLiqsolCoreProgram,
|
|
17
|
-
getErrorMessage,
|
|
18
18
|
} from '../utils';
|
|
19
|
-
|
|
20
|
-
import {
|
|
21
|
-
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
OutpostWireStateSnapshot,
|
|
22
|
+
SharesPreview,
|
|
23
|
+
TrancheState,
|
|
24
|
+
WalletLike,
|
|
25
|
+
} from '../types';
|
|
26
|
+
import { INDEX_SCALE, PROGRAM_IDS } from '../constants';
|
|
22
27
|
|
|
23
28
|
/**
|
|
24
|
-
*
|
|
25
|
-
* - Read state
|
|
26
|
-
* - Build + (optionally) send user instructions
|
|
29
|
+
* OutpostClient
|
|
27
30
|
*
|
|
28
|
-
*
|
|
31
|
+
* Read-only + preview helper for:
|
|
32
|
+
* - Outpost config + per-user pretokens
|
|
33
|
+
* - Tranche ladder snapshot
|
|
34
|
+
* - Pretoken preview math
|
|
35
|
+
*
|
|
36
|
+
* Write helpers:
|
|
37
|
+
* - buildStakeIx → liqsol_core::synd
|
|
38
|
+
* - buildUnstakeIx → liqsol_core::desynd
|
|
39
|
+
*
|
|
40
|
+
* All PDAs / ATAs are sourced via buildOutpostAccounts, which is kept
|
|
41
|
+
* in sync with the on-chain PDAs and capital-staking Wire utils.
|
|
29
42
|
*/
|
|
30
43
|
export class OutpostClient {
|
|
31
|
-
private readonly program: Program<LiqsolCore>;
|
|
32
|
-
|
|
33
|
-
// keep consistent with your tests
|
|
34
|
-
static readonly INDEX_SCALE = new BN('1000000000000');
|
|
44
|
+
private readonly program: Program<LiqsolCore>;
|
|
35
45
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const svc = new SolanaProgramService(provider);
|
|
41
|
-
this.program = svc.getProgram('liqsolCore');
|
|
46
|
+
constructor(
|
|
47
|
+
private readonly provider: AnchorProvider,
|
|
48
|
+
) {
|
|
49
|
+
this.program = new Program(liqsolCoreIDL as any, provider);
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* without requiring a wallet. Kept here as sugar for callers.
|
|
47
|
-
*/
|
|
48
|
-
getReadOnlyProgram(): Program<LiqsolCore> {
|
|
49
|
-
return getLiqsolCoreProgram(this.connection);
|
|
52
|
+
get connection() {
|
|
53
|
+
return this.provider.connection;
|
|
50
54
|
}
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
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);
|
|
56
|
+
get wallet(): WalletLike {
|
|
57
|
+
return this.provider.wallet as unknown as WalletLike;
|
|
64
58
|
}
|
|
65
59
|
|
|
66
60
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
61
|
+
* Expose the raw Program in case callers need to build custom
|
|
62
|
+
* instructions outside this client.
|
|
69
63
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
};
|
|
64
|
+
get liqsolCoreProgram(): Program<LiqsolCore> {
|
|
65
|
+
return this.program;
|
|
105
66
|
}
|
|
106
67
|
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
// Account helpers (user-facing, not admin bootstrap)
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
|
|
111
68
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
69
|
+
* Build all outpost-related PDAs / ATAs for a given user.
|
|
70
|
+
* Defaults to the connected wallet.
|
|
114
71
|
*/
|
|
115
|
-
async
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
72
|
+
async buildAccounts(user?: PublicKey): Promise<OutpostAccounts> {
|
|
73
|
+
const userPk = user ?? this.wallet.publicKey;
|
|
74
|
+
if (!userPk) {
|
|
75
|
+
throw new Error('OutpostClient.buildAccounts: wallet not connected');
|
|
76
|
+
}
|
|
77
|
+
return buildOutpostAccounts(this.connection, userPk);
|
|
119
78
|
}
|
|
120
79
|
|
|
121
80
|
/**
|
|
122
|
-
*
|
|
123
|
-
* This is general-purpose sugar for any flow that needs the ATA.
|
|
81
|
+
* Internal helper: get raw token balance (BN) for a given ATA.
|
|
124
82
|
*/
|
|
125
|
-
async
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
accounts.liqsolMint, // mint
|
|
133
|
-
TOKEN_2022_PROGRAM_ID,
|
|
134
|
-
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
135
|
-
);
|
|
83
|
+
private async getTokenBalance(ata: PublicKey): Promise<BN> {
|
|
84
|
+
try {
|
|
85
|
+
const bal = await this.connection.getTokenAccountBalance(ata);
|
|
86
|
+
return new BN(bal.value.amount);
|
|
87
|
+
} catch {
|
|
88
|
+
return new BN(0);
|
|
89
|
+
}
|
|
136
90
|
}
|
|
137
91
|
|
|
138
92
|
/**
|
|
139
|
-
*
|
|
140
|
-
* (Does NOT send; caller composes tx)
|
|
93
|
+
* Fetch the core "Wire state" for Outpost / pretokens for a given user.
|
|
141
94
|
*/
|
|
142
|
-
async
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ---------------------------------------------------------------------------
|
|
150
|
-
// Reads (UI-first)
|
|
151
|
-
// ---------------------------------------------------------------------------
|
|
95
|
+
async fetchWireState(user?: PublicKey): Promise<OutpostWireStateSnapshot> {
|
|
96
|
+
const userPk = user ?? this.wallet.publicKey;
|
|
97
|
+
if (!userPk) {
|
|
98
|
+
throw new Error('OutpostClient.fetchWireState: wallet not connected');
|
|
99
|
+
}
|
|
152
100
|
|
|
153
|
-
|
|
154
|
-
const accounts = await buildOutpostAccounts(this.connection, user);
|
|
101
|
+
const pdas = await this.buildAccounts(userPk);
|
|
155
102
|
|
|
156
103
|
const [
|
|
157
104
|
globalState,
|
|
158
|
-
|
|
105
|
+
outpostAccount,
|
|
159
106
|
distributionState,
|
|
160
|
-
|
|
107
|
+
userPretokenRecord,
|
|
161
108
|
trancheState,
|
|
162
109
|
] = await Promise.all([
|
|
163
|
-
this.program.account.globalState.fetch(
|
|
164
|
-
this.program.account.
|
|
165
|
-
this.program.account.distributionState.
|
|
166
|
-
this.program.account.
|
|
167
|
-
this.program.account.trancheState.fetchNullable(
|
|
110
|
+
this.program.account.globalState.fetch(pdas.globalState),
|
|
111
|
+
this.program.account.outpostAccount.fetchNullable(pdas.outpostAccount),
|
|
112
|
+
this.program.account.distributionState.fetchNullable(pdas.distributionState),
|
|
113
|
+
this.program.account.userPretokenRecord.fetchNullable(pdas.userPretokenRecord),
|
|
114
|
+
this.program.account.trancheState.fetchNullable(pdas.trancheState),
|
|
168
115
|
]);
|
|
169
116
|
|
|
170
|
-
const [
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
userLiqsolBalance,
|
|
174
|
-
] = await Promise.all([
|
|
175
|
-
this.getTokenBalanceSafe(accounts.liqsolPoolAta),
|
|
176
|
-
this.connection.getBalance(accounts.solBucket),
|
|
177
|
-
this.getTokenBalanceSafe(accounts.userAta),
|
|
117
|
+
const [liqsolPoolBalance, userLiqsolBalance] = await Promise.all([
|
|
118
|
+
this.getTokenBalance(pdas.liqsolPoolAta),
|
|
119
|
+
this.getTokenBalance(pdas.userAta),
|
|
178
120
|
]);
|
|
179
121
|
|
|
180
122
|
return {
|
|
181
123
|
globalState,
|
|
124
|
+
outpostAccount,
|
|
182
125
|
distributionState,
|
|
183
|
-
wireReceipt,
|
|
184
126
|
trancheState,
|
|
185
|
-
|
|
127
|
+
userPretokenRecord,
|
|
186
128
|
liqsolPoolBalance,
|
|
187
|
-
solBucketLamports,
|
|
188
129
|
userLiqsolBalance,
|
|
189
130
|
};
|
|
190
131
|
}
|
|
191
132
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
// ---------------------------------------------------------------------------
|
|
133
|
+
// -------------------------------------------------------------------------
|
|
134
|
+
// Outpost stake / unstake builders (synd / desynd)
|
|
135
|
+
// -------------------------------------------------------------------------
|
|
210
136
|
|
|
211
137
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
138
|
+
* Build the `synd` (stake) instruction:
|
|
139
|
+
* user liqSOL ATA → liqSOL pool ATA
|
|
214
140
|
*/
|
|
215
|
-
async
|
|
216
|
-
|
|
217
|
-
|
|
141
|
+
async buildStakeIx(
|
|
142
|
+
amountLamports: bigint,
|
|
143
|
+
user?: PublicKey,
|
|
144
|
+
): Promise<TransactionInstruction> {
|
|
145
|
+
const userPk = user ?? this.wallet.publicKey;
|
|
146
|
+
if (!userPk) {
|
|
147
|
+
throw new Error('OutpostClient.buildStakeIx: wallet not connected');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const a = await this.buildAccounts(userPk);
|
|
218
151
|
|
|
219
152
|
return this.program.methods
|
|
220
|
-
.
|
|
153
|
+
.synd(new BN(amountLamports.toString()))
|
|
221
154
|
.accounts({
|
|
222
|
-
user:
|
|
155
|
+
user: userPk,
|
|
223
156
|
liqsolMint: a.liqsolMint,
|
|
224
157
|
globalState: a.globalState,
|
|
158
|
+
distributionState: a.distributionState,
|
|
225
159
|
userAta: a.userAta,
|
|
226
160
|
poolAuthority: a.poolAuthority,
|
|
227
|
-
liqsolPoolAta: a.liqsolPoolAta,
|
|
228
|
-
warrantDepositRecord: a.wireReceipt,
|
|
229
|
-
liqsolPoolUserRecord: a.poolUserRecord,
|
|
230
|
-
distributionState: a.distributionState,
|
|
231
|
-
payRateHistory: a.payRateHistory,
|
|
232
161
|
bucketAuthority: a.bucketAuthority,
|
|
233
162
|
bucketTokenAccount: a.bucketTokenAccount,
|
|
234
|
-
|
|
163
|
+
bucketUserRecord: a.bucketUserRecord,
|
|
164
|
+
senderUserRecord: a.userUserRecord,
|
|
165
|
+
receiverUserRecord: a.liqsolPoolUserRecord,
|
|
166
|
+
extraAccountMetaList: a.extraAccountMetaList,
|
|
167
|
+
liqsolCoreProgram: PROGRAM_IDS.LIQSOL_CORE,
|
|
168
|
+
transferHookProgram: PROGRAM_IDS.TRANSFER_HOOK,
|
|
169
|
+
liqsolPoolAta: a.liqsolPoolAta,
|
|
170
|
+
outpostAccount: a.outpostAccount,
|
|
235
171
|
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
236
172
|
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
237
173
|
systemProgram: SystemProgram.programId,
|
|
@@ -240,235 +176,128 @@ export class OutpostClient {
|
|
|
240
176
|
}
|
|
241
177
|
|
|
242
178
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
179
|
+
* Build the `desynd` (unstake) instruction:
|
|
180
|
+
* liqSOL pool ATA → user liqSOL ATA
|
|
245
181
|
*/
|
|
246
|
-
async
|
|
247
|
-
|
|
248
|
-
|
|
182
|
+
async buildUnstakeIx(
|
|
183
|
+
amountLamports: bigint,
|
|
184
|
+
user?: PublicKey,
|
|
185
|
+
): Promise<TransactionInstruction> {
|
|
186
|
+
const userPk = user ?? this.wallet.publicKey;
|
|
187
|
+
if (!userPk) {
|
|
188
|
+
throw new Error('OutpostClient.buildUnstakeIx: wallet not connected');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const a = await this.buildAccounts(userPk);
|
|
249
192
|
|
|
250
193
|
return this.program.methods
|
|
251
|
-
.
|
|
194
|
+
.desynd(new BN(amountLamports.toString()))
|
|
252
195
|
.accounts({
|
|
253
|
-
user:
|
|
196
|
+
user: userPk,
|
|
254
197
|
liqsolMint: a.liqsolMint,
|
|
255
198
|
globalState: a.globalState,
|
|
199
|
+
distributionState: a.distributionState,
|
|
256
200
|
userAta: a.userAta,
|
|
257
201
|
poolAuthority: a.poolAuthority,
|
|
258
|
-
liqsolPoolAta: a.liqsolPoolAta,
|
|
259
|
-
warrantDepositRecord: a.wireReceipt,
|
|
260
|
-
liqsolPoolUserRecord: a.poolUserRecord,
|
|
261
|
-
distributionState: a.distributionState,
|
|
262
|
-
payRateHistory: a.payRateHistory,
|
|
263
202
|
bucketAuthority: a.bucketAuthority,
|
|
264
203
|
bucketTokenAccount: a.bucketTokenAccount,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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,
|
|
204
|
+
bucketUserRecord: a.bucketUserRecord,
|
|
205
|
+
senderUserRecord: a.liqsolPoolUserRecord,
|
|
206
|
+
receiverUserRecord: a.userUserRecord,
|
|
207
|
+
extraAccountMetaList: a.extraAccountMetaList,
|
|
208
|
+
liqsolCoreProgram: PROGRAM_IDS.LIQSOL_CORE,
|
|
209
|
+
transferHookProgram: PROGRAM_IDS.TRANSFER_HOOK,
|
|
287
210
|
liqsolPoolAta: a.liqsolPoolAta,
|
|
288
|
-
|
|
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,
|
|
211
|
+
outpostAccount: a.outpostAccount,
|
|
299
212
|
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
300
|
-
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
301
213
|
systemProgram: SystemProgram.programId,
|
|
302
214
|
})
|
|
303
215
|
.instruction();
|
|
304
216
|
}
|
|
305
217
|
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
}
|
|
218
|
+
// -------------------------------------------------------------------------
|
|
219
|
+
// Pretoken preview helper (unchanged)
|
|
220
|
+
// -------------------------------------------------------------------------
|
|
337
221
|
|
|
338
222
|
/**
|
|
339
|
-
*
|
|
223
|
+
* Local preview for "how many pretokens would I get for this liqSOL
|
|
224
|
+
* amount at the current tranche price and SOL/USD price?"
|
|
225
|
+
*
|
|
226
|
+
* SCALE = 1e8
|
|
227
|
+
* usdValue = solAmountLamports * solPriceUsd / 1e9
|
|
228
|
+
* pretokens = usdValue * SCALE / pretokenPriceUsd
|
|
340
229
|
*
|
|
341
|
-
*
|
|
342
|
-
* liqsolPoolUserRecord: pdas.userUserRecord, not poolUserRecord).
|
|
230
|
+
* All values are BN integers; solPriceUsd and pretokenPriceUsd are 1e8 scaled.
|
|
343
231
|
*/
|
|
344
|
-
|
|
345
|
-
|
|
232
|
+
previewPretokens(params: {
|
|
233
|
+
liqsolAmountLamports: BN;
|
|
234
|
+
solPriceUsd: BN; // 1e8 scale
|
|
235
|
+
trancheState: TrancheState;
|
|
236
|
+
}): SharesPreview {
|
|
237
|
+
const { liqsolAmountLamports, solPriceUsd, trancheState } = params;
|
|
238
|
+
|
|
239
|
+
// Current tranche price in USD (1e8 scale)
|
|
240
|
+
const pretokenPriceUsd = new BN(
|
|
241
|
+
trancheState.currentTranchePriceUsd.toString(),
|
|
242
|
+
);
|
|
346
243
|
|
|
347
|
-
|
|
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
|
-
}
|
|
244
|
+
const SCALE = new BN('100000000'); // 1e8
|
|
370
245
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
246
|
+
// usdValue = (lamports * solPriceUsd) / 1e9
|
|
247
|
+
const usdValue = liqsolAmountLamports
|
|
248
|
+
.mul(solPriceUsd)
|
|
249
|
+
.div(new BN(1_000_000_000));
|
|
374
250
|
|
|
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);
|
|
251
|
+
const estimatedPretokens = usdValue.mul(SCALE).div(pretokenPriceUsd);
|
|
387
252
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
253
|
+
return {
|
|
254
|
+
liqsolAmountLamports,
|
|
255
|
+
solPriceUsd,
|
|
256
|
+
pretokenPriceUsd,
|
|
257
|
+
estimatedPretokens,
|
|
258
|
+
};
|
|
395
259
|
}
|
|
396
260
|
|
|
397
261
|
/**
|
|
398
|
-
*
|
|
262
|
+
* Convert liqSOL tokens (lamports) → Outpost “shares”, rounding UP
|
|
263
|
+
* when there is any remainder.
|
|
264
|
+
*
|
|
265
|
+
* This matches the on-chain logic and the capital-staking helper:
|
|
266
|
+
*
|
|
267
|
+
* INDEX_SCALE = 1e12
|
|
268
|
+
* numerator = amount * INDEX_SCALE
|
|
269
|
+
* shares = numerator / currentIndex
|
|
270
|
+
* remainder = numerator % currentIndex
|
|
271
|
+
* if remainder > 0 → shares + 1
|
|
399
272
|
*/
|
|
400
|
-
|
|
401
|
-
amount
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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);
|
|
273
|
+
static tokensToShares(amount: BN, currentIndex: BN): BN {
|
|
274
|
+
const numerator = amount.mul(INDEX_SCALE);
|
|
275
|
+
const shares = numerator.div(currentIndex);
|
|
276
|
+
const remainder = numerator.mod(currentIndex);
|
|
277
|
+
return remainder.eqn(0) ? shares : shares.addn(1);
|
|
457
278
|
}
|
|
458
279
|
|
|
459
|
-
// ---------------------------------------------------------------------------
|
|
460
|
-
// Internals
|
|
461
|
-
// ---------------------------------------------------------------------------
|
|
462
|
-
|
|
463
280
|
/**
|
|
464
|
-
*
|
|
281
|
+
* Format a 1e8-scaled integer (e.g. USD price, pretokens, etc.)
|
|
282
|
+
* into a fixed 8-decimal string.
|
|
283
|
+
*
|
|
284
|
+
* Examples:
|
|
285
|
+
* 0 -> "0.00000000"
|
|
286
|
+
* 1 -> "0.00000001"
|
|
287
|
+
* 100000000 -> "1.00000000"
|
|
288
|
+
* 123456789 -> "1.23456789"
|
|
465
289
|
*/
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
290
|
+
static formatScale8(value: BN): string {
|
|
291
|
+
const decimals = 8;
|
|
292
|
+
const valueStr = value.toString();
|
|
293
|
+
|
|
294
|
+
if (valueStr.length <= decimals) {
|
|
295
|
+
const padding = '0'.repeat(decimals - valueStr.length);
|
|
296
|
+
return `0.${padding}${valueStr}`;
|
|
472
297
|
}
|
|
298
|
+
|
|
299
|
+
const intPart = valueStr.slice(0, valueStr.length - decimals);
|
|
300
|
+
const decPart = valueStr.slice(valueStr.length - decimals);
|
|
301
|
+
return `${intPart}.${decPart}`;
|
|
473
302
|
}
|
|
474
303
|
}
|