@zcomb/programs-sdk 1.5.0 → 1.6.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/src/index.ts CHANGED
@@ -112,10 +112,52 @@ export {
112
112
  MIN_OPTIONS as FUTARCHY_MIN_OPTIONS,
113
113
  } from "./futarchy";
114
114
 
115
+ /* SVault Sub-SDK */
116
+
117
+ export {
118
+ // Client
119
+ SVaultClient,
120
+ // Types
121
+ StakingConfigAccount,
122
+ UserStakeAccount,
123
+ DelegateAccount,
124
+ StakingVaultInitializedEvent,
125
+ StakedEvent,
126
+ UnstakeInitiatedEvent,
127
+ WithdrawnEvent,
128
+ RewardsPostedEvent,
129
+ RewardsClaimedEvent,
130
+ SlashedEvent,
131
+ DelegateAddedEvent,
132
+ DelegateRemovedEvent,
133
+ SVaultEvent,
134
+ SVaultTxOptions,
135
+ // Utils
136
+ deriveStakingConfigPDA,
137
+ deriveUserStakePDA,
138
+ deriveDelegatePDA,
139
+ deriveStakeVaultPDA,
140
+ deriveRewardVaultPDA,
141
+ fetchStakingConfigAccount,
142
+ fetchUserStakeAccount,
143
+ fetchDelegateAccount,
144
+ computeWithdrawAvailableAt,
145
+ isWithdrawAvailable,
146
+ getTimeUntilWithdraw,
147
+ // Constants
148
+ PROGRAM_ID as SVAULT_PROGRAM_ID,
149
+ STAKING_CONFIG_SEED,
150
+ USER_STAKE_SEED,
151
+ STAKE_VAULT_SEED,
152
+ REWARD_VAULT_SEED,
153
+ SECONDS_PER_DAY,
154
+ } from "./svault";
155
+
115
156
  /* Sub-SDK Namespaces */
116
157
 
117
158
  import * as vault from "./vault";
118
159
  import * as amm from "./amm";
119
160
  import * as futarchy from "./futarchy";
161
+ import * as svault from "./svault";
120
162
 
121
- export { vault, amm, futarchy };
163
+ export { vault, amm, futarchy, svault };
@@ -0,0 +1,401 @@
1
+ /*
2
+ * High-level client for the SVault program.
3
+ * Provides ergonomic methods for staking operations with automatic PDA derivation
4
+ * and compute budget management.
5
+ */
6
+
7
+ import { Program, AnchorProvider, BN } from "@coral-xyz/anchor";
8
+ import { PublicKey, ComputeBudgetProgram } from "@solana/web3.js";
9
+ import { PROGRAM_ID } from "./constants";
10
+ import {
11
+ Svault,
12
+ SVaultTxOptions,
13
+ StakingConfigAccount,
14
+ UserStakeAccount,
15
+ DelegateAccount,
16
+ } from "./types";
17
+ import {
18
+ deriveStakingConfigPDA,
19
+ deriveUserStakePDA,
20
+ deriveDelegatePDA,
21
+ deriveStakeVaultPDA,
22
+ deriveRewardVaultPDA,
23
+ fetchStakingConfigAccount,
24
+ fetchUserStakeAccount,
25
+ fetchDelegateAccount,
26
+ } from "./utils";
27
+ import {
28
+ initializeStakingVault,
29
+ stake,
30
+ initiateUnstake,
31
+ withdraw,
32
+ postRewards,
33
+ claimRewards,
34
+ setConfig,
35
+ slash,
36
+ addDelegate,
37
+ removeDelegate,
38
+ } from "./instructions";
39
+
40
+ import { SvaultIDL } from "../generated/idls";
41
+
42
+ const DEFAULT_COMPUTE_UNITS = 200_000;
43
+
44
+ export class SVaultClient {
45
+ public program: Program<Svault>;
46
+ public programId: PublicKey;
47
+ public computeUnits: number;
48
+
49
+ constructor(
50
+ provider: AnchorProvider,
51
+ programId?: PublicKey,
52
+ computeUnits?: number
53
+ ) {
54
+ this.programId = programId ?? PROGRAM_ID;
55
+ this.computeUnits = computeUnits ?? DEFAULT_COMPUTE_UNITS;
56
+ this.program = new Program(SvaultIDL as Svault, provider);
57
+ }
58
+
59
+ /* PDA Helpers */
60
+
61
+ deriveStakingConfigPDA(tokenMint: PublicKey, nonce: number): [PublicKey, number] {
62
+ return deriveStakingConfigPDA(tokenMint, nonce, this.programId);
63
+ }
64
+
65
+ deriveUserStakePDA(
66
+ stakingConfig: PublicKey,
67
+ user: PublicKey
68
+ ): [PublicKey, number] {
69
+ return deriveUserStakePDA(stakingConfig, user, this.programId);
70
+ }
71
+
72
+ deriveDelegatePDA(
73
+ stakingConfig: PublicKey,
74
+ delegate: PublicKey
75
+ ): [PublicKey, number] {
76
+ return deriveDelegatePDA(stakingConfig, delegate, this.programId);
77
+ }
78
+
79
+ deriveStakeVaultPDA(stakingConfig: PublicKey): [PublicKey, number] {
80
+ return deriveStakeVaultPDA(stakingConfig, this.programId);
81
+ }
82
+
83
+ deriveRewardVaultPDA(stakingConfig: PublicKey): [PublicKey, number] {
84
+ return deriveRewardVaultPDA(stakingConfig, this.programId);
85
+ }
86
+
87
+ /* State Fetching */
88
+
89
+ async fetchStakingConfig(pda: PublicKey): Promise<StakingConfigAccount> {
90
+ return fetchStakingConfigAccount(this.program, pda);
91
+ }
92
+
93
+ async fetchUserStake(pda: PublicKey): Promise<UserStakeAccount> {
94
+ return fetchUserStakeAccount(this.program, pda);
95
+ }
96
+
97
+ async fetchDelegate(pda: PublicKey): Promise<DelegateAccount> {
98
+ return fetchDelegateAccount(this.program, pda);
99
+ }
100
+
101
+ /**
102
+ * Convenience method: Derive staking config PDA from mint + nonce, then fetch user stake.
103
+ */
104
+ async fetchUserStakeByMint(
105
+ tokenMint: PublicKey,
106
+ nonce: number,
107
+ user: PublicKey
108
+ ): Promise<UserStakeAccount> {
109
+ const [configPda] = this.deriveStakingConfigPDA(tokenMint, nonce);
110
+ const [userStakePda] = this.deriveUserStakePDA(configPda, user);
111
+ return this.fetchUserStake(userStakePda);
112
+ }
113
+
114
+ /**
115
+ * Convenience method: Derive staking config PDA from mint + nonce, then fetch config.
116
+ */
117
+ async fetchStakingConfigByMint(
118
+ tokenMint: PublicKey,
119
+ nonce: number
120
+ ): Promise<StakingConfigAccount> {
121
+ const [configPda] = this.deriveStakingConfigPDA(tokenMint, nonce);
122
+ return this.fetchStakingConfig(configPda);
123
+ }
124
+
125
+ /* Instruction Builders */
126
+
127
+ initializeStakingVault(
128
+ admin: PublicKey,
129
+ tokenMint: PublicKey,
130
+ unstakingPeriod: BN | number,
131
+ volumeWindow: BN | number,
132
+ nonce: number,
133
+ options?: SVaultTxOptions
134
+ ) {
135
+ const { includeCuBudget = true, computeUnits } = options ?? {};
136
+
137
+ let builder = initializeStakingVault(
138
+ this.program,
139
+ admin,
140
+ tokenMint,
141
+ unstakingPeriod,
142
+ volumeWindow,
143
+ nonce
144
+ );
145
+
146
+ if (includeCuBudget) {
147
+ builder = builder.preInstructions([
148
+ ComputeBudgetProgram.setComputeUnitLimit({
149
+ units: computeUnits ?? this.computeUnits,
150
+ }),
151
+ ]);
152
+ }
153
+
154
+ const [configPda] = this.deriveStakingConfigPDA(tokenMint, nonce);
155
+ return { builder, configPda };
156
+ }
157
+
158
+ stake(
159
+ user: PublicKey,
160
+ tokenMint: PublicKey,
161
+ nonce: number,
162
+ amount: BN | number,
163
+ options?: SVaultTxOptions
164
+ ) {
165
+ const { includeCuBudget = true, computeUnits } = options ?? {};
166
+
167
+ let builder = stake(this.program, user, tokenMint, nonce, amount);
168
+
169
+ if (includeCuBudget) {
170
+ builder = builder.preInstructions([
171
+ ComputeBudgetProgram.setComputeUnitLimit({
172
+ units: computeUnits ?? this.computeUnits,
173
+ }),
174
+ ]);
175
+ }
176
+
177
+ return builder;
178
+ }
179
+
180
+ initiateUnstake(
181
+ user: PublicKey,
182
+ tokenMint: PublicKey,
183
+ nonce: number,
184
+ amount: BN | number,
185
+ options?: SVaultTxOptions
186
+ ) {
187
+ const { includeCuBudget = true, computeUnits } = options ?? {};
188
+
189
+ let builder = initiateUnstake(this.program, user, tokenMint, nonce, amount);
190
+
191
+ if (includeCuBudget) {
192
+ builder = builder.preInstructions([
193
+ ComputeBudgetProgram.setComputeUnitLimit({
194
+ units: computeUnits ?? this.computeUnits,
195
+ }),
196
+ ]);
197
+ }
198
+
199
+ return builder;
200
+ }
201
+
202
+ withdraw(
203
+ user: PublicKey,
204
+ tokenMint: PublicKey,
205
+ nonce: number,
206
+ options?: SVaultTxOptions
207
+ ) {
208
+ const { includeCuBudget = true, computeUnits } = options ?? {};
209
+
210
+ let builder = withdraw(this.program, user, tokenMint, nonce);
211
+
212
+ if (includeCuBudget) {
213
+ builder = builder.preInstructions([
214
+ ComputeBudgetProgram.setComputeUnitLimit({
215
+ units: computeUnits ?? this.computeUnits,
216
+ }),
217
+ ]);
218
+ }
219
+
220
+ return builder;
221
+ }
222
+
223
+ postRewards(
224
+ admin: PublicKey,
225
+ tokenMint: PublicKey,
226
+ nonce: number,
227
+ merkleRoot: number[],
228
+ totalAmount: BN | number,
229
+ options?: SVaultTxOptions
230
+ ) {
231
+ const { includeCuBudget = true, computeUnits } = options ?? {};
232
+
233
+ let builder = postRewards(
234
+ this.program,
235
+ admin,
236
+ tokenMint,
237
+ nonce,
238
+ merkleRoot,
239
+ totalAmount
240
+ );
241
+
242
+ if (includeCuBudget) {
243
+ builder = builder.preInstructions([
244
+ ComputeBudgetProgram.setComputeUnitLimit({
245
+ units: computeUnits ?? this.computeUnits,
246
+ }),
247
+ ]);
248
+ }
249
+
250
+ return builder;
251
+ }
252
+
253
+ claimRewards(
254
+ user: PublicKey,
255
+ tokenMint: PublicKey,
256
+ nonce: number,
257
+ cumulativeAmount: BN | number,
258
+ proof: number[][],
259
+ options?: SVaultTxOptions
260
+ ) {
261
+ const { includeCuBudget = true, computeUnits } = options ?? {};
262
+
263
+ let builder = claimRewards(
264
+ this.program,
265
+ user,
266
+ tokenMint,
267
+ nonce,
268
+ cumulativeAmount,
269
+ proof
270
+ );
271
+
272
+ if (includeCuBudget) {
273
+ builder = builder.preInstructions([
274
+ ComputeBudgetProgram.setComputeUnitLimit({
275
+ units: computeUnits ?? this.computeUnits,
276
+ }),
277
+ ]);
278
+ }
279
+
280
+ return builder;
281
+ }
282
+
283
+ setConfig(
284
+ admin: PublicKey,
285
+ tokenMint: PublicKey,
286
+ nonce: number,
287
+ params: { unstakingPeriod?: BN | number; volumeWindow?: BN | number },
288
+ options?: SVaultTxOptions
289
+ ) {
290
+ const { includeCuBudget = true, computeUnits } = options ?? {};
291
+
292
+ let builder = setConfig(
293
+ this.program,
294
+ admin,
295
+ tokenMint,
296
+ nonce,
297
+ params.unstakingPeriod ?? null,
298
+ params.volumeWindow ?? null
299
+ );
300
+
301
+ if (includeCuBudget) {
302
+ builder = builder.preInstructions([
303
+ ComputeBudgetProgram.setComputeUnitLimit({
304
+ units: computeUnits ?? this.computeUnits,
305
+ }),
306
+ ]);
307
+ }
308
+
309
+ return builder;
310
+ }
311
+
312
+ slash(
313
+ admin: PublicKey,
314
+ tokenMint: PublicKey,
315
+ nonce: number,
316
+ userToSlash: PublicKey,
317
+ basisPoints: number,
318
+ options?: SVaultTxOptions
319
+ ) {
320
+ const { includeCuBudget = true, computeUnits } = options ?? {};
321
+
322
+ // Derive user stake PDA from user pubkey
323
+ const [configPda] = this.deriveStakingConfigPDA(tokenMint, nonce);
324
+ const [userStakePda] = this.deriveUserStakePDA(configPda, userToSlash);
325
+
326
+ let builder = slash(
327
+ this.program,
328
+ admin,
329
+ tokenMint,
330
+ nonce,
331
+ userStakePda,
332
+ basisPoints
333
+ );
334
+
335
+ if (includeCuBudget) {
336
+ builder = builder.preInstructions([
337
+ ComputeBudgetProgram.setComputeUnitLimit({
338
+ units: computeUnits ?? this.computeUnits,
339
+ }),
340
+ ]);
341
+ }
342
+
343
+ return builder;
344
+ }
345
+
346
+ addDelegate(
347
+ staker: PublicKey,
348
+ delegateWallet: PublicKey,
349
+ tokenMint: PublicKey,
350
+ nonce: number,
351
+ options?: SVaultTxOptions
352
+ ) {
353
+ const { includeCuBudget = true, computeUnits } = options ?? {};
354
+
355
+ let builder = addDelegate(
356
+ this.program,
357
+ staker,
358
+ delegateWallet,
359
+ tokenMint,
360
+ nonce
361
+ );
362
+
363
+ if (includeCuBudget) {
364
+ builder = builder.preInstructions([
365
+ ComputeBudgetProgram.setComputeUnitLimit({
366
+ units: computeUnits ?? this.computeUnits,
367
+ }),
368
+ ]);
369
+ }
370
+
371
+ return builder;
372
+ }
373
+
374
+ removeDelegate(
375
+ staker: PublicKey,
376
+ delegateWallet: PublicKey,
377
+ tokenMint: PublicKey,
378
+ nonce: number,
379
+ options?: SVaultTxOptions
380
+ ) {
381
+ const { includeCuBudget = true, computeUnits } = options ?? {};
382
+
383
+ let builder = removeDelegate(
384
+ this.program,
385
+ staker,
386
+ delegateWallet,
387
+ tokenMint,
388
+ nonce
389
+ );
390
+
391
+ if (includeCuBudget) {
392
+ builder = builder.preInstructions([
393
+ ComputeBudgetProgram.setComputeUnitLimit({
394
+ units: computeUnits ?? this.computeUnits,
395
+ }),
396
+ ]);
397
+ }
398
+
399
+ return builder;
400
+ }
401
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Constants for the SVault program.
3
+ * Parsed from the generated IDL to stay in sync with the Rust program.
4
+ */
5
+
6
+ import { PublicKey } from "@solana/web3.js";
7
+ import { SvaultIDL } from "../generated/idls";
8
+ import { parseIdlBytes, getIdlConstant } from "../utils";
9
+
10
+ /* Program ID */
11
+
12
+ export const PROGRAM_ID = new PublicKey(SvaultIDL.address);
13
+
14
+ /* PDA Seeds */
15
+
16
+ export const STAKING_CONFIG_SEED = parseIdlBytes(getIdlConstant(SvaultIDL, "STAKING_CONFIG_SEED"));
17
+ export const USER_STAKE_SEED = parseIdlBytes(getIdlConstant(SvaultIDL, "USER_STAKE_SEED"));
18
+ export const STAKE_VAULT_SEED = parseIdlBytes(getIdlConstant(SvaultIDL, "STAKE_VAULT_SEED"));
19
+ export const REWARD_VAULT_SEED = parseIdlBytes(getIdlConstant(SvaultIDL, "REWARD_VAULT_SEED"));
20
+
21
+ /* Time Constants */
22
+
23
+ export const SECONDS_PER_DAY = 86400;
@@ -0,0 +1,5 @@
1
+ export * from "./constants";
2
+ export * from "./types";
3
+ export * from "./utils";
4
+ export * from "./instructions";
5
+ export * from "./client";