@ignitionfi/fogo-stake-pool 1.0.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.
@@ -0,0 +1,2898 @@
1
+ 'use strict';
2
+
3
+ var splToken = require('@solana/spl-token');
4
+ var web3_js = require('@solana/web3.js');
5
+ var BN = require('bn.js');
6
+ var node_buffer = require('node:buffer');
7
+ var BufferLayout = require('@solana/buffer-layout');
8
+ var bufferLayout = require('buffer-layout');
9
+
10
+ function _interopNamespaceDefault(e) {
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var BufferLayout__namespace = /*#__PURE__*/_interopNamespaceDefault(BufferLayout);
28
+
29
+ /**
30
+ * A `StructFailure` represents a single specific failure in validation.
31
+ */
32
+ /**
33
+ * `StructError` objects are thrown (or returned) when validation fails.
34
+ *
35
+ * Validation logic is design to exit early for maximum performance. The error
36
+ * represents the first error encountered during validation. For more detail,
37
+ * the `error.failures` property is a generator function that can be run to
38
+ * continue validation and receive all the failures in the data.
39
+ */
40
+ class StructError extends TypeError {
41
+ constructor(failure, failures) {
42
+ let cached;
43
+ const { message, explanation, ...rest } = failure;
44
+ const { path } = failure;
45
+ const msg = path.length === 0 ? message : `At path: ${path.join('.')} -- ${message}`;
46
+ super(explanation ?? msg);
47
+ if (explanation != null)
48
+ this.cause = msg;
49
+ Object.assign(this, rest);
50
+ this.name = this.constructor.name;
51
+ this.failures = () => {
52
+ return (cached ?? (cached = [failure, ...failures()]));
53
+ };
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Check if a value is an iterator.
59
+ */
60
+ function isIterable(x) {
61
+ return isObject(x) && typeof x[Symbol.iterator] === 'function';
62
+ }
63
+ /**
64
+ * Check if a value is a plain object.
65
+ */
66
+ function isObject(x) {
67
+ return typeof x === 'object' && x != null;
68
+ }
69
+ /**
70
+ * Check if a value is a non-array object.
71
+ */
72
+ function isNonArrayObject(x) {
73
+ return isObject(x) && !Array.isArray(x);
74
+ }
75
+ /**
76
+ * Return a value as a printable string.
77
+ */
78
+ function print(value) {
79
+ if (typeof value === 'symbol') {
80
+ return value.toString();
81
+ }
82
+ return typeof value === 'string' ? JSON.stringify(value) : `${value}`;
83
+ }
84
+ /**
85
+ * Shifts (removes and returns) the first value from the `input` iterator.
86
+ * Like `Array.prototype.shift()` but for an `Iterator`.
87
+ */
88
+ function shiftIterator(input) {
89
+ const { done, value } = input.next();
90
+ return done ? undefined : value;
91
+ }
92
+ /**
93
+ * Convert a single validation result to a failure.
94
+ */
95
+ function toFailure(result, context, struct, value) {
96
+ if (result === true) {
97
+ return;
98
+ }
99
+ else if (result === false) {
100
+ result = {};
101
+ }
102
+ else if (typeof result === 'string') {
103
+ result = { message: result };
104
+ }
105
+ const { path, branch } = context;
106
+ const { type } = struct;
107
+ const { refinement, message = `Expected a value of type \`${type}\`${refinement ? ` with refinement \`${refinement}\`` : ''}, but received: \`${print(value)}\``, } = result;
108
+ return {
109
+ value,
110
+ type,
111
+ refinement,
112
+ key: path[path.length - 1],
113
+ path,
114
+ branch,
115
+ ...result,
116
+ message,
117
+ };
118
+ }
119
+ /**
120
+ * Convert a validation result to an iterable of failures.
121
+ */
122
+ function* toFailures(result, context, struct, value) {
123
+ if (!isIterable(result)) {
124
+ result = [result];
125
+ }
126
+ for (const r of result) {
127
+ const failure = toFailure(r, context, struct, value);
128
+ if (failure) {
129
+ yield failure;
130
+ }
131
+ }
132
+ }
133
+ /**
134
+ * Check a value against a struct, traversing deeply into nested values, and
135
+ * returning an iterator of failures or success.
136
+ */
137
+ function* run(value, struct, options = {}) {
138
+ const { path = [], branch = [value], coerce = false, mask = false } = options;
139
+ const ctx = { path, branch, mask };
140
+ if (coerce) {
141
+ value = struct.coercer(value, ctx);
142
+ }
143
+ let status = 'valid';
144
+ for (const failure of struct.validator(value, ctx)) {
145
+ failure.explanation = options.message;
146
+ status = 'not_valid';
147
+ yield [failure, undefined];
148
+ }
149
+ for (let [k, v, s] of struct.entries(value, ctx)) {
150
+ const ts = run(v, s, {
151
+ path: k === undefined ? path : [...path, k],
152
+ branch: k === undefined ? branch : [...branch, v],
153
+ coerce,
154
+ mask,
155
+ message: options.message,
156
+ });
157
+ for (const t of ts) {
158
+ if (t[0]) {
159
+ status = t[0].refinement != null ? 'not_refined' : 'not_valid';
160
+ yield [t[0], undefined];
161
+ }
162
+ else if (coerce) {
163
+ v = t[1];
164
+ if (k === undefined) {
165
+ value = v;
166
+ }
167
+ else if (value instanceof Map) {
168
+ value.set(k, v);
169
+ }
170
+ else if (value instanceof Set) {
171
+ value.add(v);
172
+ }
173
+ else if (isObject(value)) {
174
+ if (v !== undefined || k in value)
175
+ value[k] = v;
176
+ }
177
+ }
178
+ }
179
+ }
180
+ if (status !== 'not_valid') {
181
+ for (const failure of struct.refiner(value, ctx)) {
182
+ failure.explanation = options.message;
183
+ status = 'not_refined';
184
+ yield [failure, undefined];
185
+ }
186
+ }
187
+ if (status === 'valid') {
188
+ yield [undefined, value];
189
+ }
190
+ }
191
+
192
+ /**
193
+ * `Struct` objects encapsulate the validation logic for a specific type of
194
+ * values. Once constructed, you use the `assert`, `is` or `validate` helpers to
195
+ * validate unknown input data against the struct.
196
+ */
197
+ class Struct {
198
+ constructor(props) {
199
+ const { type, schema, validator, refiner, coercer = (value) => value, entries = function* () { }, } = props;
200
+ this.type = type;
201
+ this.schema = schema;
202
+ this.entries = entries;
203
+ this.coercer = coercer;
204
+ if (validator) {
205
+ this.validator = (value, context) => {
206
+ const result = validator(value, context);
207
+ return toFailures(result, context, this, value);
208
+ };
209
+ }
210
+ else {
211
+ this.validator = () => [];
212
+ }
213
+ if (refiner) {
214
+ this.refiner = (value, context) => {
215
+ const result = refiner(value, context);
216
+ return toFailures(result, context, this, value);
217
+ };
218
+ }
219
+ else {
220
+ this.refiner = () => [];
221
+ }
222
+ }
223
+ /**
224
+ * Assert that a value passes the struct's validation, throwing if it doesn't.
225
+ */
226
+ assert(value, message) {
227
+ return assert(value, this, message);
228
+ }
229
+ /**
230
+ * Create a value with the struct's coercion logic, then validate it.
231
+ */
232
+ create(value, message) {
233
+ return create(value, this, message);
234
+ }
235
+ /**
236
+ * Check if a value passes the struct's validation.
237
+ */
238
+ is(value) {
239
+ return is(value, this);
240
+ }
241
+ /**
242
+ * Mask a value, coercing and validating it, but returning only the subset of
243
+ * properties defined by the struct's schema. Masking applies recursively to
244
+ * props of `object` structs only.
245
+ */
246
+ mask(value, message) {
247
+ return mask(value, this, message);
248
+ }
249
+ /**
250
+ * Validate a value with the struct's validation logic, returning a tuple
251
+ * representing the result.
252
+ *
253
+ * You may optionally pass `true` for the `coerce` argument to coerce
254
+ * the value before attempting to validate it. If you do, the result will
255
+ * contain the coerced result when successful. Also, `mask` will turn on
256
+ * masking of the unknown `object` props recursively if passed.
257
+ */
258
+ validate(value, options = {}) {
259
+ return validate(value, this, options);
260
+ }
261
+ }
262
+ /**
263
+ * Assert that a value passes a struct, throwing if it doesn't.
264
+ */
265
+ function assert(value, struct, message) {
266
+ const result = validate(value, struct, { message });
267
+ if (result[0]) {
268
+ throw result[0];
269
+ }
270
+ }
271
+ /**
272
+ * Create a value with the coercion logic of struct and validate it.
273
+ */
274
+ function create(value, struct, message) {
275
+ const result = validate(value, struct, { coerce: true, message });
276
+ if (result[0]) {
277
+ throw result[0];
278
+ }
279
+ else {
280
+ return result[1];
281
+ }
282
+ }
283
+ /**
284
+ * Mask a value, returning only the subset of properties defined by a struct.
285
+ */
286
+ function mask(value, struct, message) {
287
+ const result = validate(value, struct, { coerce: true, mask: true, message });
288
+ if (result[0]) {
289
+ throw result[0];
290
+ }
291
+ else {
292
+ return result[1];
293
+ }
294
+ }
295
+ /**
296
+ * Check if a value passes a struct.
297
+ */
298
+ function is(value, struct) {
299
+ const result = validate(value, struct);
300
+ return !result[0];
301
+ }
302
+ /**
303
+ * Validate a value against a struct, returning an error if invalid, or the
304
+ * value (with potential coercion) if valid.
305
+ */
306
+ function validate(value, struct, options = {}) {
307
+ const tuples = run(value, struct, options);
308
+ const tuple = shiftIterator(tuples);
309
+ if (tuple[0]) {
310
+ const error = new StructError(tuple[0], function* () {
311
+ for (const t of tuples) {
312
+ if (t[0]) {
313
+ yield t[0];
314
+ }
315
+ }
316
+ });
317
+ return [error, undefined];
318
+ }
319
+ else {
320
+ const v = tuple[1];
321
+ return [undefined, v];
322
+ }
323
+ }
324
+ /**
325
+ * Define a new struct type with a custom validation function.
326
+ */
327
+ function define(name, validator) {
328
+ return new Struct({ type: name, schema: null, validator });
329
+ }
330
+ function enums(values) {
331
+ const schema = {};
332
+ const description = values.map((v) => print(v)).join();
333
+ for (const key of values) {
334
+ schema[key] = key;
335
+ }
336
+ return new Struct({
337
+ type: 'enums',
338
+ schema,
339
+ validator(value) {
340
+ return (values.includes(value) ||
341
+ `Expected one of \`${description}\`, but received: ${print(value)}`);
342
+ },
343
+ });
344
+ }
345
+ /**
346
+ * Ensure that a value is an instance of a specific class.
347
+ */
348
+ function instance(Class) {
349
+ return define('instance', (value) => {
350
+ return (value instanceof Class ||
351
+ `Expected a \`${Class.name}\` instance, but received: ${print(value)}`);
352
+ });
353
+ }
354
+ /**
355
+ * Augment an existing struct to allow `null` values.
356
+ */
357
+ function nullable(struct) {
358
+ return new Struct({
359
+ ...struct,
360
+ validator: (value, ctx) => value === null || struct.validator(value, ctx),
361
+ refiner: (value, ctx) => value === null || struct.refiner(value, ctx),
362
+ });
363
+ }
364
+ /**
365
+ * Ensure that a value is a number.
366
+ */
367
+ function number() {
368
+ return define('number', (value) => {
369
+ return ((typeof value === 'number' && !isNaN(value)) ||
370
+ `Expected a number, but received: ${print(value)}`);
371
+ });
372
+ }
373
+ /**
374
+ * Augment a struct to allow `undefined` values.
375
+ */
376
+ function optional(struct) {
377
+ return new Struct({
378
+ ...struct,
379
+ validator: (value, ctx) => value === undefined || struct.validator(value, ctx),
380
+ refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx),
381
+ });
382
+ }
383
+ /**
384
+ * Ensure that a value is a string.
385
+ */
386
+ function string() {
387
+ return define('string', (value) => {
388
+ return (typeof value === 'string' ||
389
+ `Expected a string, but received: ${print(value)}`);
390
+ });
391
+ }
392
+ /**
393
+ * Ensure that a value has a set of known properties of specific types.
394
+ *
395
+ * Note: Unrecognized properties are allowed and untouched. This is similar to
396
+ * how TypeScript's structural typing works.
397
+ */
398
+ function type(schema) {
399
+ const keys = Object.keys(schema);
400
+ return new Struct({
401
+ type: 'type',
402
+ schema,
403
+ *entries(value) {
404
+ if (isObject(value)) {
405
+ for (const k of keys) {
406
+ yield [k, value[k], schema[k]];
407
+ }
408
+ }
409
+ },
410
+ validator(value) {
411
+ return (isNonArrayObject(value) ||
412
+ `Expected an object, but received: ${print(value)}`);
413
+ },
414
+ coercer(value) {
415
+ return isNonArrayObject(value) ? { ...value } : value;
416
+ },
417
+ });
418
+ }
419
+
420
+ /**
421
+ * Augment a `Struct` to add an additional coercion step to its input.
422
+ *
423
+ * This allows you to transform input data before validating it, to increase the
424
+ * likelihood that it passes validation—for example for default values, parsing
425
+ * different formats, etc.
426
+ *
427
+ * Note: You must use `create(value, Struct)` on the value to have the coercion
428
+ * take effect! Using simply `assert()` or `is()` will not use coercion.
429
+ */
430
+ function coerce(struct, condition, coercer) {
431
+ return new Struct({
432
+ ...struct,
433
+ coercer: (value, ctx) => {
434
+ return is(value, condition)
435
+ ? struct.coercer(coercer(value, ctx), ctx)
436
+ : struct.coercer(value, ctx);
437
+ },
438
+ });
439
+ }
440
+
441
+ // Public key that identifies the metadata program.
442
+ const METADATA_PROGRAM_ID = new web3_js.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
443
+ const METADATA_MAX_NAME_LENGTH = 32;
444
+ const METADATA_MAX_SYMBOL_LENGTH = 10;
445
+ const METADATA_MAX_URI_LENGTH = 200;
446
+ // Public key that identifies the SPL Stake Pool program.
447
+ const STAKE_POOL_PROGRAM_ID = new web3_js.PublicKey('SP1s4uFeTAX9jsXXmwyDs1gxYYf7cdDZ8qHUHVxE1yr');
448
+ // Public key that identifies the SPL Stake Pool program deployed to devnet.
449
+ const DEVNET_STAKE_POOL_PROGRAM_ID = new web3_js.PublicKey('DPoo15wWDqpPJJtS2MUZ49aRxqz5ZaaJCJP4z8bLuib');
450
+ // Maximum number of validators to update during UpdateValidatorListBalance.
451
+ const MAX_VALIDATORS_TO_UPDATE = 4;
452
+ // Seed for ephemeral stake account
453
+ const EPHEMERAL_STAKE_SEED_PREFIX = node_buffer.Buffer.from('ephemeral');
454
+ // Seed used to derive transient stake accounts.
455
+ const TRANSIENT_STAKE_SEED_PREFIX = node_buffer.Buffer.from('transient');
456
+ // Seed for user stake account created during session withdrawal
457
+ const USER_STAKE_SEED_PREFIX = node_buffer.Buffer.from('user_stake');
458
+ // Minimum amount of staked SOL required in a validator stake account to allow
459
+ // for merges without a mismatch on credits observed
460
+ const MINIMUM_ACTIVE_STAKE = web3_js.LAMPORTS_PER_SOL;
461
+
462
+ /**
463
+ * Populate a buffer of instruction data using an InstructionType
464
+ * @internal
465
+ */
466
+ function encodeData(type, fields) {
467
+ const allocLength = type.layout.span;
468
+ const data = node_buffer.Buffer.alloc(allocLength);
469
+ const layoutFields = Object.assign({ instruction: type.index }, fields);
470
+ type.layout.encode(layoutFields, data);
471
+ return data;
472
+ }
473
+ /**
474
+ * Decode instruction data buffer using an InstructionType
475
+ * @internal
476
+ */
477
+ function decodeData(type, buffer) {
478
+ let data;
479
+ try {
480
+ data = type.layout.decode(buffer);
481
+ }
482
+ catch (err) {
483
+ throw new Error(`invalid instruction; ${err}`);
484
+ }
485
+ if (data.instruction !== type.index) {
486
+ throw new Error(`invalid instruction; instruction index mismatch ${data.instruction} != ${type.index}`);
487
+ }
488
+ return data;
489
+ }
490
+
491
+ function solToLamports(amount) {
492
+ if (isNaN(amount)) {
493
+ return Number(0);
494
+ }
495
+ return Number(amount * web3_js.LAMPORTS_PER_SOL);
496
+ }
497
+ function lamportsToSol(lamports) {
498
+ if (typeof lamports === 'number') {
499
+ return Math.abs(lamports) / web3_js.LAMPORTS_PER_SOL;
500
+ }
501
+ if (typeof lamports === 'bigint') {
502
+ return Math.abs(Number(lamports)) / web3_js.LAMPORTS_PER_SOL;
503
+ }
504
+ let signMultiplier = 1;
505
+ if (lamports.isNeg()) {
506
+ signMultiplier = -1;
507
+ }
508
+ const absLamports = lamports.abs();
509
+ const lamportsString = absLamports.toString(10).padStart(10, '0');
510
+ const splitIndex = lamportsString.length - 9;
511
+ const solString = `${lamportsString.slice(0, splitIndex)}.${lamportsString.slice(splitIndex)}`;
512
+ return signMultiplier * Number.parseFloat(solString);
513
+ }
514
+
515
+ /**
516
+ * Generates the wSOL transient program address for the stake pool
517
+ */
518
+ function findWsolTransientProgramAddress(programId, userPubkey) {
519
+ const [publicKey] = web3_js.PublicKey.findProgramAddressSync([node_buffer.Buffer.from('transient_wsol'), userPubkey.toBuffer()], programId);
520
+ return publicKey;
521
+ }
522
+ /**
523
+ * Generates the withdraw authority program address for the stake pool
524
+ */
525
+ async function findWithdrawAuthorityProgramAddress(programId, stakePoolAddress) {
526
+ const [publicKey] = web3_js.PublicKey.findProgramAddressSync([stakePoolAddress.toBuffer(), node_buffer.Buffer.from('withdraw')], programId);
527
+ return publicKey;
528
+ }
529
+ /**
530
+ * Generates the stake program address for a validator's vote account
531
+ */
532
+ async function findStakeProgramAddress(programId, voteAccountAddress, stakePoolAddress, seed) {
533
+ const [publicKey] = web3_js.PublicKey.findProgramAddressSync([
534
+ voteAccountAddress.toBuffer(),
535
+ stakePoolAddress.toBuffer(),
536
+ seed ? new BN(seed).toArrayLike(node_buffer.Buffer, 'le', 4) : node_buffer.Buffer.alloc(0),
537
+ ], programId);
538
+ return publicKey;
539
+ }
540
+ /**
541
+ * Generates the stake program address for a validator's vote account
542
+ */
543
+ async function findTransientStakeProgramAddress(programId, voteAccountAddress, stakePoolAddress, seed) {
544
+ const [publicKey] = web3_js.PublicKey.findProgramAddressSync([
545
+ TRANSIENT_STAKE_SEED_PREFIX,
546
+ voteAccountAddress.toBuffer(),
547
+ stakePoolAddress.toBuffer(),
548
+ seed.toArrayLike(node_buffer.Buffer, 'le', 8),
549
+ ], programId);
550
+ return publicKey;
551
+ }
552
+ /**
553
+ * Generates the ephemeral program address for stake pool redelegation
554
+ */
555
+ async function findEphemeralStakeProgramAddress(programId, stakePoolAddress, seed) {
556
+ const [publicKey] = web3_js.PublicKey.findProgramAddressSync([EPHEMERAL_STAKE_SEED_PREFIX, stakePoolAddress.toBuffer(), seed.toArrayLike(node_buffer.Buffer, 'le', 8)], programId);
557
+ return publicKey;
558
+ }
559
+ /**
560
+ * Generates the metadata program address for the stake pool
561
+ */
562
+ function findMetadataAddress(stakePoolMintAddress) {
563
+ const [publicKey] = web3_js.PublicKey.findProgramAddressSync([node_buffer.Buffer.from('metadata'), METADATA_PROGRAM_ID.toBuffer(), stakePoolMintAddress.toBuffer()], METADATA_PROGRAM_ID);
564
+ return publicKey;
565
+ }
566
+ /**
567
+ * Generates the user stake account PDA for session-based withdrawals.
568
+ * The PDA is derived from the user's wallet and a unique seed.
569
+ */
570
+ function findUserStakeProgramAddress(programId, userWallet, seed) {
571
+ const seedBN = typeof seed === 'number' ? new BN(seed) : seed;
572
+ const [publicKey] = web3_js.PublicKey.findProgramAddressSync([
573
+ USER_STAKE_SEED_PREFIX,
574
+ userWallet.toBuffer(),
575
+ seedBN.toArrayLike(node_buffer.Buffer, 'le', 8),
576
+ ], programId);
577
+ return publicKey;
578
+ }
579
+
580
+ class BNLayout extends bufferLayout.Layout {
581
+ constructor(span, signed, property) {
582
+ super(span, property);
583
+ this.blob = bufferLayout.blob(span);
584
+ this.signed = signed;
585
+ }
586
+ decode(b, offset = 0) {
587
+ const num = new BN(this.blob.decode(b, offset), 10, 'le');
588
+ if (this.signed) {
589
+ return num.fromTwos(this.span * 8).clone();
590
+ }
591
+ return num;
592
+ }
593
+ encode(src, b, offset = 0) {
594
+ if (this.signed) {
595
+ src = src.toTwos(this.span * 8);
596
+ }
597
+ return this.blob.encode(src.toArrayLike(Buffer, 'le', this.span), b, offset);
598
+ }
599
+ }
600
+ function u64(property) {
601
+ return new BNLayout(8, false, property);
602
+ }
603
+ class WrappedLayout extends bufferLayout.Layout {
604
+ constructor(layout, decoder, encoder, property) {
605
+ super(layout.span, property);
606
+ this.layout = layout;
607
+ this.decoder = decoder;
608
+ this.encoder = encoder;
609
+ }
610
+ decode(b, offset) {
611
+ return this.decoder(this.layout.decode(b, offset));
612
+ }
613
+ encode(src, b, offset) {
614
+ return this.layout.encode(this.encoder(src), b, offset);
615
+ }
616
+ getSpan(b, offset) {
617
+ return this.layout.getSpan(b, offset);
618
+ }
619
+ }
620
+ function publicKey(property) {
621
+ return new WrappedLayout(bufferLayout.blob(32), (b) => new web3_js.PublicKey(b), (key) => key.toBuffer(), property);
622
+ }
623
+ class OptionLayout extends bufferLayout.Layout {
624
+ constructor(layout, property) {
625
+ super(-1, property);
626
+ this.layout = layout;
627
+ this.discriminator = bufferLayout.u8();
628
+ }
629
+ encode(src, b, offset = 0) {
630
+ if (src === null || src === undefined) {
631
+ return this.discriminator.encode(0, b, offset);
632
+ }
633
+ this.discriminator.encode(1, b, offset);
634
+ return this.layout.encode(src, b, offset + 1) + 1;
635
+ }
636
+ decode(b, offset = 0) {
637
+ const discriminator = this.discriminator.decode(b, offset);
638
+ if (discriminator === 0) {
639
+ return null;
640
+ }
641
+ else if (discriminator === 1) {
642
+ return this.layout.decode(b, offset + 1);
643
+ }
644
+ throw new Error(`Invalid option ${this.property}`);
645
+ }
646
+ getSpan(b, offset = 0) {
647
+ const discriminator = this.discriminator.decode(b, offset);
648
+ if (discriminator === 0) {
649
+ return 1;
650
+ }
651
+ else if (discriminator === 1) {
652
+ return this.layout.getSpan(b, offset + 1) + 1;
653
+ }
654
+ throw new Error(`Invalid option ${this.property}`);
655
+ }
656
+ }
657
+ function option(layout, property) {
658
+ return new OptionLayout(layout, property);
659
+ }
660
+ function vec(elementLayout, property) {
661
+ const length = bufferLayout.u32('length');
662
+ const layout = bufferLayout.struct([
663
+ length,
664
+ bufferLayout.seq(elementLayout, bufferLayout.offset(length, -length.span), 'values'),
665
+ ]);
666
+ return new WrappedLayout(layout, ({ values }) => values, values => ({ values }), property);
667
+ }
668
+
669
+ const feeFields = [u64('denominator'), u64('numerator')];
670
+ var AccountType;
671
+ (function (AccountType) {
672
+ AccountType[AccountType["Uninitialized"] = 0] = "Uninitialized";
673
+ AccountType[AccountType["StakePool"] = 1] = "StakePool";
674
+ AccountType[AccountType["ValidatorList"] = 2] = "ValidatorList";
675
+ })(AccountType || (AccountType = {}));
676
+ const BigNumFromString = coerce(instance(BN), string(), (value) => {
677
+ if (typeof value === 'string') {
678
+ return new BN(value, 10);
679
+ }
680
+ throw new Error('invalid big num');
681
+ });
682
+ const PublicKeyFromString = coerce(instance(web3_js.PublicKey), string(), value => new web3_js.PublicKey(value));
683
+ class FutureEpochLayout extends bufferLayout.Layout {
684
+ constructor(layout, property) {
685
+ super(-1, property);
686
+ this.layout = layout;
687
+ this.discriminator = bufferLayout.u8();
688
+ }
689
+ encode(src, b, offset = 0) {
690
+ if (src === null || src === undefined) {
691
+ return this.discriminator.encode(0, b, offset);
692
+ }
693
+ // This isn't right, but we don't typically encode outside of tests
694
+ this.discriminator.encode(2, b, offset);
695
+ return this.layout.encode(src, b, offset + 1) + 1;
696
+ }
697
+ decode(b, offset = 0) {
698
+ const discriminator = this.discriminator.decode(b, offset);
699
+ if (discriminator === 0) {
700
+ return null;
701
+ }
702
+ else if (discriminator === 1 || discriminator === 2) {
703
+ return this.layout.decode(b, offset + 1);
704
+ }
705
+ throw new Error(`Invalid future epoch ${this.property}`);
706
+ }
707
+ getSpan(b, offset = 0) {
708
+ const discriminator = this.discriminator.decode(b, offset);
709
+ if (discriminator === 0) {
710
+ return 1;
711
+ }
712
+ else if (discriminator === 1 || discriminator === 2) {
713
+ return this.layout.getSpan(b, offset + 1) + 1;
714
+ }
715
+ throw new Error(`Invalid future epoch ${this.property}`);
716
+ }
717
+ }
718
+ function futureEpoch(layout, property) {
719
+ return new FutureEpochLayout(layout, property);
720
+ }
721
+ const StakeAccountType = enums(['uninitialized', 'initialized', 'delegated', 'rewardsPool']);
722
+ const StakeMeta = type({
723
+ rentExemptReserve: BigNumFromString,
724
+ authorized: type({
725
+ staker: PublicKeyFromString,
726
+ withdrawer: PublicKeyFromString,
727
+ }),
728
+ lockup: type({
729
+ unixTimestamp: number(),
730
+ epoch: number(),
731
+ custodian: PublicKeyFromString,
732
+ }),
733
+ });
734
+ const StakeAccountInfo = type({
735
+ meta: StakeMeta,
736
+ stake: nullable(type({
737
+ delegation: type({
738
+ voter: PublicKeyFromString,
739
+ stake: BigNumFromString,
740
+ activationEpoch: BigNumFromString,
741
+ deactivationEpoch: BigNumFromString,
742
+ warmupCooldownRate: number(),
743
+ }),
744
+ creditsObserved: number(),
745
+ })),
746
+ });
747
+ const StakeAccount = type({
748
+ type: StakeAccountType,
749
+ info: optional(StakeAccountInfo),
750
+ });
751
+ const StakePoolLayout = bufferLayout.struct([
752
+ bufferLayout.u8('accountType'),
753
+ publicKey('manager'),
754
+ publicKey('staker'),
755
+ publicKey('stakeDepositAuthority'),
756
+ bufferLayout.u8('stakeWithdrawBumpSeed'),
757
+ publicKey('validatorList'),
758
+ publicKey('reserveStake'),
759
+ publicKey('poolMint'),
760
+ publicKey('managerFeeAccount'),
761
+ publicKey('tokenProgramId'),
762
+ u64('totalLamports'),
763
+ u64('poolTokenSupply'),
764
+ u64('lastUpdateEpoch'),
765
+ bufferLayout.struct([u64('unixTimestamp'), u64('epoch'), publicKey('custodian')], 'lockup'),
766
+ bufferLayout.struct(feeFields, 'epochFee'),
767
+ futureEpoch(bufferLayout.struct(feeFields), 'nextEpochFee'),
768
+ option(publicKey(), 'preferredDepositValidatorVoteAddress'),
769
+ option(publicKey(), 'preferredWithdrawValidatorVoteAddress'),
770
+ bufferLayout.struct(feeFields, 'stakeDepositFee'),
771
+ bufferLayout.struct(feeFields, 'stakeWithdrawalFee'),
772
+ futureEpoch(bufferLayout.struct(feeFields), 'nextStakeWithdrawalFee'),
773
+ bufferLayout.u8('stakeReferralFee'),
774
+ option(publicKey(), 'solDepositAuthority'),
775
+ bufferLayout.struct(feeFields, 'solDepositFee'),
776
+ bufferLayout.u8('solReferralFee'),
777
+ option(publicKey(), 'solWithdrawAuthority'),
778
+ bufferLayout.struct(feeFields, 'solWithdrawalFee'),
779
+ futureEpoch(bufferLayout.struct(feeFields), 'nextSolWithdrawalFee'),
780
+ u64('lastEpochPoolTokenSupply'),
781
+ u64('lastEpochTotalLamports'),
782
+ ]);
783
+ var ValidatorStakeInfoStatus;
784
+ (function (ValidatorStakeInfoStatus) {
785
+ ValidatorStakeInfoStatus[ValidatorStakeInfoStatus["Active"] = 0] = "Active";
786
+ ValidatorStakeInfoStatus[ValidatorStakeInfoStatus["DeactivatingTransient"] = 1] = "DeactivatingTransient";
787
+ ValidatorStakeInfoStatus[ValidatorStakeInfoStatus["ReadyForRemoval"] = 2] = "ReadyForRemoval";
788
+ })(ValidatorStakeInfoStatus || (ValidatorStakeInfoStatus = {}));
789
+ const ValidatorStakeInfoLayout = bufferLayout.struct([
790
+ /// Amount of active stake delegated to this validator
791
+ /// Note that if `last_update_epoch` does not match the current epoch then
792
+ /// this field may not be accurate
793
+ u64('activeStakeLamports'),
794
+ /// Amount of transient stake delegated to this validator
795
+ /// Note that if `last_update_epoch` does not match the current epoch then
796
+ /// this field may not be accurate
797
+ u64('transientStakeLamports'),
798
+ /// Last epoch the active and transient stake lamports fields were updated
799
+ u64('lastUpdateEpoch'),
800
+ /// Start of the validator transient account seed suffixes
801
+ u64('transientSeedSuffixStart'),
802
+ /// End of the validator transient account seed suffixes
803
+ u64('transientSeedSuffixEnd'),
804
+ /// Status of the validator stake account
805
+ bufferLayout.u8('status'),
806
+ /// Validator vote account address
807
+ publicKey('voteAccountAddress'),
808
+ ]);
809
+ const ValidatorListLayout = bufferLayout.struct([
810
+ bufferLayout.u8('accountType'),
811
+ bufferLayout.u32('maxValidators'),
812
+ vec(ValidatorStakeInfoLayout, 'validators'),
813
+ ]);
814
+
815
+ async function getValidatorListAccount(connection, pubkey) {
816
+ const account = await connection.getAccountInfo(pubkey);
817
+ if (!account) {
818
+ throw new Error('Invalid validator list account');
819
+ }
820
+ return {
821
+ pubkey,
822
+ account: {
823
+ data: ValidatorListLayout.decode(account === null || account === void 0 ? void 0 : account.data),
824
+ executable: account.executable,
825
+ lamports: account.lamports,
826
+ owner: account.owner,
827
+ },
828
+ };
829
+ }
830
+ async function prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, amount, compareFn, skipFee) {
831
+ var _a, _b;
832
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
833
+ const validatorListAcc = await connection.getAccountInfo(stakePool.validatorList);
834
+ const validatorList = ValidatorListLayout.decode(validatorListAcc === null || validatorListAcc === void 0 ? void 0 : validatorListAcc.data);
835
+ if (!(validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators) || (validatorList === null || validatorList === void 0 ? void 0 : validatorList.validators.length) === 0) {
836
+ throw new Error('No accounts found');
837
+ }
838
+ const minBalanceForRentExemption = await connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space);
839
+ const minBalance = new BN(minBalanceForRentExemption + MINIMUM_ACTIVE_STAKE);
840
+ let accounts = [];
841
+ // Prepare accounts
842
+ for (const validator of validatorList.validators) {
843
+ if (validator.status !== ValidatorStakeInfoStatus.Active) {
844
+ continue;
845
+ }
846
+ const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
847
+ // For active stake accounts, subtract the minimum balance that must remain
848
+ // to allow for merges and maintain rent exemption
849
+ const availableActiveLamports = validator.activeStakeLamports.sub(minBalance);
850
+ if (availableActiveLamports.gt(new BN(0))) {
851
+ const isPreferred = (_a = stakePool === null || stakePool === void 0 ? void 0 : stakePool.preferredWithdrawValidatorVoteAddress) === null || _a === void 0 ? void 0 : _a.equals(validator.voteAccountAddress);
852
+ accounts.push({
853
+ type: isPreferred ? 'preferred' : 'active',
854
+ voteAddress: validator.voteAccountAddress,
855
+ stakeAddress: stakeAccountAddress,
856
+ lamports: availableActiveLamports,
857
+ });
858
+ }
859
+ const transientStakeLamports = validator.transientStakeLamports.sub(minBalance);
860
+ if (transientStakeLamports.gt(new BN(0))) {
861
+ const transientStakeAccountAddress = await findTransientStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress, validator.transientSeedSuffixStart);
862
+ accounts.push({
863
+ type: 'transient',
864
+ voteAddress: validator.voteAccountAddress,
865
+ stakeAddress: transientStakeAccountAddress,
866
+ lamports: transientStakeLamports,
867
+ });
868
+ }
869
+ }
870
+ // Sort from highest to lowest balance
871
+ accounts = accounts.sort(compareFn || ((a, b) => b.lamports.sub(a.lamports).toNumber()));
872
+ const reserveStake = await connection.getAccountInfo(stakePool.reserveStake);
873
+ const reserveStakeBalance = new BN(((_b = reserveStake === null || reserveStake === void 0 ? void 0 : reserveStake.lamports) !== null && _b !== void 0 ? _b : 0) - minBalanceForRentExemption);
874
+ if (reserveStakeBalance.gt(new BN(0))) {
875
+ accounts.push({
876
+ type: 'reserve',
877
+ stakeAddress: stakePool.reserveStake,
878
+ lamports: reserveStakeBalance,
879
+ });
880
+ }
881
+ // Prepare the list of accounts to withdraw from
882
+ const withdrawFrom = [];
883
+ let remainingAmount = new BN(amount);
884
+ const fee = stakePool.stakeWithdrawalFee;
885
+ const inverseFee = {
886
+ numerator: fee.denominator.sub(fee.numerator),
887
+ denominator: fee.denominator,
888
+ };
889
+ for (const type of ['preferred', 'active', 'transient', 'reserve']) {
890
+ const filteredAccounts = accounts.filter(a => a.type === type);
891
+ for (const { stakeAddress, voteAddress, lamports } of filteredAccounts) {
892
+ if (lamports.lte(minBalance) && type === 'transient') {
893
+ continue;
894
+ }
895
+ let availableForWithdrawal = calcPoolTokensForDeposit(stakePool, lamports);
896
+ if (!skipFee && !inverseFee.numerator.isZero()) {
897
+ availableForWithdrawal = availableForWithdrawal
898
+ .mul(inverseFee.denominator)
899
+ .div(inverseFee.numerator);
900
+ }
901
+ const poolAmount = BN.min(availableForWithdrawal, remainingAmount);
902
+ if (poolAmount.lte(new BN(0))) {
903
+ continue;
904
+ }
905
+ // Those accounts will be withdrawn completely with `claim` instruction
906
+ withdrawFrom.push({ stakeAddress, voteAddress, poolAmount });
907
+ remainingAmount = remainingAmount.sub(poolAmount);
908
+ if (remainingAmount.isZero()) {
909
+ break;
910
+ }
911
+ }
912
+ if (remainingAmount.isZero()) {
913
+ break;
914
+ }
915
+ }
916
+ // Not enough stake to withdraw the specified amount
917
+ if (remainingAmount.gt(new BN(0))) {
918
+ throw new Error(`No stake accounts found in this pool with enough balance to withdraw ${lamportsToSol(amount)} pool tokens.`);
919
+ }
920
+ return withdrawFrom;
921
+ }
922
+ /**
923
+ * Calculate the pool tokens that should be minted for a deposit of `stakeLamports`
924
+ */
925
+ function calcPoolTokensForDeposit(stakePool, stakeLamports) {
926
+ if (stakePool.poolTokenSupply.isZero() || stakePool.totalLamports.isZero()) {
927
+ return stakeLamports;
928
+ }
929
+ const numerator = stakeLamports.mul(stakePool.poolTokenSupply);
930
+ return numerator.div(stakePool.totalLamports);
931
+ }
932
+ /**
933
+ * Calculate lamports amount on withdrawal
934
+ */
935
+ function calcLamportsWithdrawAmount(stakePool, poolTokens) {
936
+ const numerator = poolTokens.mul(stakePool.totalLamports);
937
+ const denominator = stakePool.poolTokenSupply;
938
+ if (numerator.lt(denominator)) {
939
+ return new BN(0);
940
+ }
941
+ return numerator.div(denominator);
942
+ }
943
+ function newStakeAccount(feePayer, instructions, lamports) {
944
+ // Account for tokens not specified, creating one
945
+ const stakeReceiverKeypair = web3_js.Keypair.generate();
946
+ console.log(`Creating account to receive stake ${stakeReceiverKeypair.publicKey}`);
947
+ instructions.push(
948
+ // Creating new account
949
+ web3_js.SystemProgram.createAccount({
950
+ fromPubkey: feePayer,
951
+ newAccountPubkey: stakeReceiverKeypair.publicKey,
952
+ lamports,
953
+ space: web3_js.StakeProgram.space,
954
+ programId: web3_js.StakeProgram.programId,
955
+ }));
956
+ return stakeReceiverKeypair;
957
+ }
958
+
959
+ function arrayChunk(array, size) {
960
+ const result = [];
961
+ for (let i = 0; i < array.length; i += size) {
962
+ result.push(array.slice(i, i + size));
963
+ }
964
+ return result;
965
+ }
966
+
967
+ // 'UpdateTokenMetadata' and 'CreateTokenMetadata' have dynamic layouts
968
+ const MOVE_STAKE_LAYOUT = BufferLayout__namespace.struct([
969
+ BufferLayout__namespace.u8('instruction'),
970
+ BufferLayout__namespace.ns64('lamports'),
971
+ BufferLayout__namespace.ns64('transientStakeSeed'),
972
+ ]);
973
+ const UPDATE_VALIDATOR_LIST_BALANCE_LAYOUT = BufferLayout__namespace.struct([
974
+ BufferLayout__namespace.u8('instruction'),
975
+ BufferLayout__namespace.u32('startIndex'),
976
+ BufferLayout__namespace.u8('noMerge'),
977
+ ]);
978
+ function tokenMetadataLayout(instruction, nameLength, symbolLength, uriLength) {
979
+ if (nameLength > METADATA_MAX_NAME_LENGTH) {
980
+ // eslint-disable-next-line no-throw-literal
981
+ throw 'maximum token name length is 32 characters';
982
+ }
983
+ if (symbolLength > METADATA_MAX_SYMBOL_LENGTH) {
984
+ // eslint-disable-next-line no-throw-literal
985
+ throw 'maximum token symbol length is 10 characters';
986
+ }
987
+ if (uriLength > METADATA_MAX_URI_LENGTH) {
988
+ // eslint-disable-next-line no-throw-literal
989
+ throw 'maximum token uri length is 200 characters';
990
+ }
991
+ return {
992
+ index: instruction,
993
+ layout: BufferLayout__namespace.struct([
994
+ BufferLayout__namespace.u8('instruction'),
995
+ BufferLayout__namespace.u32('nameLen'),
996
+ BufferLayout__namespace.blob(nameLength, 'name'),
997
+ BufferLayout__namespace.u32('symbolLen'),
998
+ BufferLayout__namespace.blob(symbolLength, 'symbol'),
999
+ BufferLayout__namespace.u32('uriLen'),
1000
+ BufferLayout__namespace.blob(uriLength, 'uri'),
1001
+ ]),
1002
+ };
1003
+ }
1004
+ /**
1005
+ * An enumeration of valid stake InstructionType's
1006
+ * @internal
1007
+ */
1008
+ const STAKE_POOL_INSTRUCTION_LAYOUTS = Object.freeze({
1009
+ AddValidatorToPool: {
1010
+ index: 1,
1011
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction'), BufferLayout__namespace.u32('seed')]),
1012
+ },
1013
+ RemoveValidatorFromPool: {
1014
+ index: 2,
1015
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction')]),
1016
+ },
1017
+ DecreaseValidatorStake: {
1018
+ index: 3,
1019
+ layout: MOVE_STAKE_LAYOUT,
1020
+ },
1021
+ IncreaseValidatorStake: {
1022
+ index: 4,
1023
+ layout: MOVE_STAKE_LAYOUT,
1024
+ },
1025
+ UpdateValidatorListBalance: {
1026
+ index: 6,
1027
+ layout: UPDATE_VALIDATOR_LIST_BALANCE_LAYOUT,
1028
+ },
1029
+ UpdateStakePoolBalance: {
1030
+ index: 7,
1031
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction')]),
1032
+ },
1033
+ CleanupRemovedValidatorEntries: {
1034
+ index: 8,
1035
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction')]),
1036
+ },
1037
+ DepositStake: {
1038
+ index: 9,
1039
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction')]),
1040
+ },
1041
+ /// Withdraw the token from the pool at the current ratio.
1042
+ WithdrawStake: {
1043
+ index: 10,
1044
+ layout: BufferLayout__namespace.struct([
1045
+ BufferLayout__namespace.u8('instruction'),
1046
+ BufferLayout__namespace.ns64('poolTokens'),
1047
+ ]),
1048
+ },
1049
+ /// Deposit SOL directly into the pool's reserve account. The output is a "pool" token
1050
+ /// representing ownership into the pool. Inputs are converted to the current ratio.
1051
+ DepositSol: {
1052
+ index: 14,
1053
+ layout: BufferLayout__namespace.struct([
1054
+ BufferLayout__namespace.u8('instruction'),
1055
+ BufferLayout__namespace.ns64('lamports'),
1056
+ ]),
1057
+ },
1058
+ /// Withdraw SOL directly from the pool's reserve account. Fails if the
1059
+ /// reserve does not have enough SOL.
1060
+ WithdrawSol: {
1061
+ index: 16,
1062
+ layout: BufferLayout__namespace.struct([
1063
+ BufferLayout__namespace.u8('instruction'),
1064
+ BufferLayout__namespace.ns64('poolTokens'),
1065
+ ]),
1066
+ },
1067
+ IncreaseAdditionalValidatorStake: {
1068
+ index: 19,
1069
+ layout: BufferLayout__namespace.struct([
1070
+ BufferLayout__namespace.u8('instruction'),
1071
+ BufferLayout__namespace.ns64('lamports'),
1072
+ BufferLayout__namespace.ns64('transientStakeSeed'),
1073
+ BufferLayout__namespace.ns64('ephemeralStakeSeed'),
1074
+ ]),
1075
+ },
1076
+ DecreaseAdditionalValidatorStake: {
1077
+ index: 20,
1078
+ layout: BufferLayout__namespace.struct([
1079
+ BufferLayout__namespace.u8('instruction'),
1080
+ BufferLayout__namespace.ns64('lamports'),
1081
+ BufferLayout__namespace.ns64('transientStakeSeed'),
1082
+ BufferLayout__namespace.ns64('ephemeralStakeSeed'),
1083
+ ]),
1084
+ },
1085
+ DecreaseValidatorStakeWithReserve: {
1086
+ index: 21,
1087
+ layout: MOVE_STAKE_LAYOUT,
1088
+ },
1089
+ Redelegate: {
1090
+ index: 22,
1091
+ layout: BufferLayout__namespace.struct([BufferLayout__namespace.u8('instruction')]),
1092
+ },
1093
+ DepositStakeWithSlippage: {
1094
+ index: 23,
1095
+ layout: BufferLayout__namespace.struct([
1096
+ BufferLayout__namespace.u8('instruction'),
1097
+ BufferLayout__namespace.ns64('lamports'),
1098
+ ]),
1099
+ },
1100
+ WithdrawStakeWithSlippage: {
1101
+ index: 24,
1102
+ layout: BufferLayout__namespace.struct([
1103
+ BufferLayout__namespace.u8('instruction'),
1104
+ BufferLayout__namespace.ns64('poolTokensIn'),
1105
+ BufferLayout__namespace.ns64('minimumLamportsOut'),
1106
+ ]),
1107
+ },
1108
+ DepositSolWithSlippage: {
1109
+ index: 25,
1110
+ layout: BufferLayout__namespace.struct([
1111
+ BufferLayout__namespace.u8('instruction'),
1112
+ BufferLayout__namespace.ns64('lamports'),
1113
+ ]),
1114
+ },
1115
+ WithdrawSolWithSlippage: {
1116
+ index: 26,
1117
+ layout: BufferLayout__namespace.struct([
1118
+ BufferLayout__namespace.u8('instruction'),
1119
+ BufferLayout__namespace.ns64('lamports'),
1120
+ ]),
1121
+ },
1122
+ DepositWsolWithSession: {
1123
+ index: 27,
1124
+ layout: BufferLayout__namespace.struct([
1125
+ BufferLayout__namespace.u8('instruction'),
1126
+ BufferLayout__namespace.ns64('lamportsIn'),
1127
+ BufferLayout__namespace.ns64('minimumPoolTokensOut'),
1128
+ ]),
1129
+ },
1130
+ WithdrawWsolWithSession: {
1131
+ index: 28,
1132
+ layout: BufferLayout__namespace.struct([
1133
+ BufferLayout__namespace.u8('instruction'),
1134
+ BufferLayout__namespace.ns64('poolTokensIn'),
1135
+ BufferLayout__namespace.ns64('minimumLamportsOut'),
1136
+ ]),
1137
+ },
1138
+ WithdrawStakeWithSession: {
1139
+ index: 29,
1140
+ layout: BufferLayout__namespace.struct([
1141
+ BufferLayout__namespace.u8('instruction'),
1142
+ BufferLayout__namespace.ns64('poolTokensIn'),
1143
+ BufferLayout__namespace.ns64('minimumLamportsOut'),
1144
+ BufferLayout__namespace.ns64('userStakeSeed'),
1145
+ ]),
1146
+ },
1147
+ WithdrawFromStakeAccountWithSession: {
1148
+ index: 30,
1149
+ layout: BufferLayout__namespace.struct([
1150
+ BufferLayout__namespace.u8('instruction'),
1151
+ BufferLayout__namespace.ns64('lamports'),
1152
+ BufferLayout__namespace.ns64('userStakeSeed'),
1153
+ ]),
1154
+ },
1155
+ });
1156
+ /**
1157
+ * Stake Pool Instruction class
1158
+ */
1159
+ class StakePoolInstruction {
1160
+ /**
1161
+ * Creates instruction to add a validator into the stake pool.
1162
+ */
1163
+ static addValidatorToPool(params) {
1164
+ const { programId, stakePool, staker, reserveStake, withdrawAuthority, validatorList, validatorStake, validatorVote, seed, } = params;
1165
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.AddValidatorToPool;
1166
+ const data = encodeData(type, { seed: seed !== null && seed !== void 0 ? seed : 0 });
1167
+ const keys = [
1168
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1169
+ { pubkey: staker, isSigner: true, isWritable: false },
1170
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1171
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1172
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1173
+ { pubkey: validatorStake, isSigner: false, isWritable: true },
1174
+ { pubkey: validatorVote, isSigner: false, isWritable: false },
1175
+ { pubkey: web3_js.SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
1176
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1177
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1178
+ { pubkey: web3_js.STAKE_CONFIG_ID, isSigner: false, isWritable: false },
1179
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1180
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1181
+ ];
1182
+ return new web3_js.TransactionInstruction({
1183
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1184
+ keys,
1185
+ data,
1186
+ });
1187
+ }
1188
+ /**
1189
+ * Creates instruction to remove a validator from the stake pool.
1190
+ */
1191
+ static removeValidatorFromPool(params) {
1192
+ const { programId, stakePool, staker, withdrawAuthority, validatorList, validatorStake, transientStake, } = params;
1193
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.RemoveValidatorFromPool;
1194
+ const data = encodeData(type);
1195
+ const keys = [
1196
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1197
+ { pubkey: staker, isSigner: true, isWritable: false },
1198
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1199
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1200
+ { pubkey: validatorStake, isSigner: false, isWritable: true },
1201
+ { pubkey: transientStake, isSigner: false, isWritable: true },
1202
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1203
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1204
+ ];
1205
+ return new web3_js.TransactionInstruction({
1206
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1207
+ keys,
1208
+ data,
1209
+ });
1210
+ }
1211
+ /**
1212
+ * Creates instruction to update a set of validators in the stake pool.
1213
+ */
1214
+ static updateValidatorListBalance(params) {
1215
+ const { programId, stakePool, withdrawAuthority, validatorList, reserveStake, startIndex, noMerge, validatorAndTransientStakePairs, } = params;
1216
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.UpdateValidatorListBalance;
1217
+ const data = encodeData(type, { startIndex, noMerge: noMerge ? 1 : 0 });
1218
+ const keys = [
1219
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1220
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1221
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1222
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1223
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1224
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1225
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1226
+ ...validatorAndTransientStakePairs.map(pubkey => ({
1227
+ pubkey,
1228
+ isSigner: false,
1229
+ isWritable: true,
1230
+ })),
1231
+ ];
1232
+ return new web3_js.TransactionInstruction({
1233
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1234
+ keys,
1235
+ data,
1236
+ });
1237
+ }
1238
+ /**
1239
+ * Creates instruction to update the overall stake pool balance.
1240
+ */
1241
+ static updateStakePoolBalance(params) {
1242
+ const { programId, stakePool, withdrawAuthority, validatorList, reserveStake, managerFeeAccount, poolMint, } = params;
1243
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.UpdateStakePoolBalance;
1244
+ const data = encodeData(type);
1245
+ const keys = [
1246
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1247
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1248
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1249
+ { pubkey: reserveStake, isSigner: false, isWritable: false },
1250
+ { pubkey: managerFeeAccount, isSigner: false, isWritable: true },
1251
+ { pubkey: poolMint, isSigner: false, isWritable: true },
1252
+ { pubkey: splToken.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1253
+ ];
1254
+ return new web3_js.TransactionInstruction({
1255
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1256
+ keys,
1257
+ data,
1258
+ });
1259
+ }
1260
+ /**
1261
+ * Creates instruction to cleanup removed validator entries.
1262
+ */
1263
+ static cleanupRemovedValidatorEntries(params) {
1264
+ const { programId, stakePool, validatorList } = params;
1265
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.CleanupRemovedValidatorEntries;
1266
+ const data = encodeData(type);
1267
+ const keys = [
1268
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1269
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1270
+ ];
1271
+ return new web3_js.TransactionInstruction({
1272
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1273
+ keys,
1274
+ data,
1275
+ });
1276
+ }
1277
+ /**
1278
+ * Creates `IncreaseValidatorStake` instruction (rebalance from reserve account to
1279
+ * transient account)
1280
+ */
1281
+ static increaseValidatorStake(params) {
1282
+ const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, transientStake, validatorStake, validatorVote, lamports, transientStakeSeed, } = params;
1283
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.IncreaseValidatorStake;
1284
+ const data = encodeData(type, { lamports, transientStakeSeed });
1285
+ const keys = [
1286
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1287
+ { pubkey: staker, isSigner: true, isWritable: false },
1288
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1289
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1290
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1291
+ { pubkey: transientStake, isSigner: false, isWritable: true },
1292
+ { pubkey: validatorStake, isSigner: false, isWritable: false },
1293
+ { pubkey: validatorVote, isSigner: false, isWritable: false },
1294
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1295
+ { pubkey: web3_js.SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
1296
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1297
+ { pubkey: web3_js.STAKE_CONFIG_ID, isSigner: false, isWritable: false },
1298
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1299
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1300
+ ];
1301
+ return new web3_js.TransactionInstruction({
1302
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1303
+ keys,
1304
+ data,
1305
+ });
1306
+ }
1307
+ /**
1308
+ * Creates `IncreaseAdditionalValidatorStake` instruction (rebalance from reserve account to
1309
+ * transient account)
1310
+ */
1311
+ static increaseAdditionalValidatorStake(params) {
1312
+ const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, transientStake, validatorStake, validatorVote, lamports, transientStakeSeed, ephemeralStake, ephemeralStakeSeed, } = params;
1313
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.IncreaseAdditionalValidatorStake;
1314
+ const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed });
1315
+ const keys = [
1316
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1317
+ { pubkey: staker, isSigner: true, isWritable: false },
1318
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1319
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1320
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1321
+ { pubkey: ephemeralStake, isSigner: false, isWritable: true },
1322
+ { pubkey: transientStake, isSigner: false, isWritable: true },
1323
+ { pubkey: validatorStake, isSigner: false, isWritable: false },
1324
+ { pubkey: validatorVote, isSigner: false, isWritable: false },
1325
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1326
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1327
+ { pubkey: web3_js.STAKE_CONFIG_ID, isSigner: false, isWritable: false },
1328
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1329
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1330
+ ];
1331
+ return new web3_js.TransactionInstruction({
1332
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1333
+ keys,
1334
+ data,
1335
+ });
1336
+ }
1337
+ /**
1338
+ * Creates `DecreaseValidatorStake` instruction (rebalance from validator account to
1339
+ * transient account)
1340
+ */
1341
+ static decreaseValidatorStake(params) {
1342
+ const { programId, stakePool, staker, withdrawAuthority, validatorList, validatorStake, transientStake, lamports, transientStakeSeed, } = params;
1343
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseValidatorStake;
1344
+ const data = encodeData(type, { lamports, transientStakeSeed });
1345
+ const keys = [
1346
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1347
+ { pubkey: staker, isSigner: true, isWritable: false },
1348
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1349
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1350
+ { pubkey: validatorStake, isSigner: false, isWritable: true },
1351
+ { pubkey: transientStake, isSigner: false, isWritable: true },
1352
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1353
+ { pubkey: web3_js.SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
1354
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1355
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1356
+ ];
1357
+ return new web3_js.TransactionInstruction({
1358
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1359
+ keys,
1360
+ data,
1361
+ });
1362
+ }
1363
+ /**
1364
+ * Creates `DecreaseValidatorStakeWithReserve` instruction (rebalance from
1365
+ * validator account to transient account)
1366
+ */
1367
+ static decreaseValidatorStakeWithReserve(params) {
1368
+ const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, validatorStake, transientStake, lamports, transientStakeSeed, } = params;
1369
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseValidatorStakeWithReserve;
1370
+ const data = encodeData(type, { lamports, transientStakeSeed });
1371
+ const keys = [
1372
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1373
+ { pubkey: staker, isSigner: true, isWritable: false },
1374
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1375
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1376
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1377
+ { pubkey: validatorStake, isSigner: false, isWritable: true },
1378
+ { pubkey: transientStake, isSigner: false, isWritable: true },
1379
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1380
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1381
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1382
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1383
+ ];
1384
+ return new web3_js.TransactionInstruction({
1385
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1386
+ keys,
1387
+ data,
1388
+ });
1389
+ }
1390
+ /**
1391
+ * Creates `DecreaseAdditionalValidatorStake` instruction (rebalance from
1392
+ * validator account to transient account)
1393
+ */
1394
+ static decreaseAdditionalValidatorStake(params) {
1395
+ const { programId, stakePool, staker, withdrawAuthority, validatorList, reserveStake, validatorStake, transientStake, lamports, transientStakeSeed, ephemeralStakeSeed, ephemeralStake, } = params;
1396
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseAdditionalValidatorStake;
1397
+ const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed });
1398
+ const keys = [
1399
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1400
+ { pubkey: staker, isSigner: true, isWritable: false },
1401
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1402
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1403
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1404
+ { pubkey: validatorStake, isSigner: false, isWritable: true },
1405
+ { pubkey: ephemeralStake, isSigner: false, isWritable: true },
1406
+ { pubkey: transientStake, isSigner: false, isWritable: true },
1407
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1408
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1409
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1410
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1411
+ ];
1412
+ return new web3_js.TransactionInstruction({
1413
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1414
+ keys,
1415
+ data,
1416
+ });
1417
+ }
1418
+ /**
1419
+ * Creates a transaction instruction to deposit a stake account into a stake pool.
1420
+ */
1421
+ static depositStake(params) {
1422
+ const { programId, stakePool, validatorList, depositAuthority, withdrawAuthority, depositStake, validatorStake, reserveStake, destinationPoolAccount, managerFeeAccount, referralPoolAccount, poolMint, } = params;
1423
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DepositStake;
1424
+ const data = encodeData(type);
1425
+ const keys = [
1426
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1427
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1428
+ { pubkey: depositAuthority, isSigner: false, isWritable: false },
1429
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1430
+ { pubkey: depositStake, isSigner: false, isWritable: true },
1431
+ { pubkey: validatorStake, isSigner: false, isWritable: true },
1432
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1433
+ { pubkey: destinationPoolAccount, isSigner: false, isWritable: true },
1434
+ { pubkey: managerFeeAccount, isSigner: false, isWritable: true },
1435
+ { pubkey: referralPoolAccount, isSigner: false, isWritable: true },
1436
+ { pubkey: poolMint, isSigner: false, isWritable: true },
1437
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1438
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1439
+ { pubkey: splToken.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1440
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1441
+ ];
1442
+ return new web3_js.TransactionInstruction({
1443
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1444
+ keys,
1445
+ data,
1446
+ });
1447
+ }
1448
+ /**
1449
+ * Creates a transaction instruction to deposit SOL into a stake pool.
1450
+ */
1451
+ static depositSol(params) {
1452
+ const { programId, stakePool, withdrawAuthority, depositAuthority, reserveStake, fundingAccount, destinationPoolAccount, managerFeeAccount, referralPoolAccount, poolMint, lamports, } = params;
1453
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DepositSol;
1454
+ const data = encodeData(type, { lamports });
1455
+ const keys = [
1456
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1457
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1458
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1459
+ { pubkey: fundingAccount, isSigner: true, isWritable: true },
1460
+ { pubkey: destinationPoolAccount, isSigner: false, isWritable: true },
1461
+ { pubkey: managerFeeAccount, isSigner: false, isWritable: true },
1462
+ { pubkey: referralPoolAccount, isSigner: false, isWritable: true },
1463
+ { pubkey: poolMint, isSigner: false, isWritable: true },
1464
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1465
+ { pubkey: splToken.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1466
+ ];
1467
+ if (depositAuthority) {
1468
+ keys.push({
1469
+ pubkey: depositAuthority,
1470
+ isSigner: true,
1471
+ isWritable: false,
1472
+ });
1473
+ }
1474
+ return new web3_js.TransactionInstruction({
1475
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1476
+ keys,
1477
+ data,
1478
+ });
1479
+ }
1480
+ /**
1481
+ * Creates a transaction instruction to deposit WSOL into a stake pool.
1482
+ */
1483
+ static depositWsolWithSession(params) {
1484
+ var _a;
1485
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DepositWsolWithSession;
1486
+ const data = encodeData(type, {
1487
+ lamportsIn: params.lamportsIn,
1488
+ minimumPoolTokensOut: params.minimumPoolTokensOut,
1489
+ });
1490
+ const keys = [
1491
+ { pubkey: params.stakePool, isSigner: false, isWritable: true },
1492
+ { pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
1493
+ { pubkey: params.reserveStake, isSigner: false, isWritable: true },
1494
+ { pubkey: params.fundingAccount, isSigner: true, isWritable: true },
1495
+ { pubkey: params.destinationPoolAccount, isSigner: false, isWritable: true },
1496
+ { pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
1497
+ { pubkey: params.referralPoolAccount, isSigner: false, isWritable: true },
1498
+ { pubkey: params.poolMint, isSigner: false, isWritable: true },
1499
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1500
+ { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
1501
+ // wsol specific accounts
1502
+ { pubkey: params.wsolMint, isSigner: false, isWritable: false },
1503
+ { pubkey: params.wsolTokenAccount, isSigner: false, isWritable: true },
1504
+ { pubkey: params.wsolTransientAccount, isSigner: false, isWritable: true },
1505
+ { pubkey: params.programSigner, isSigner: false, isWritable: true },
1506
+ { pubkey: (_a = params.payer) !== null && _a !== void 0 ? _a : params.fundingAccount, isSigner: true, isWritable: true },
1507
+ { pubkey: params.userWallet, isSigner: false, isWritable: false },
1508
+ ];
1509
+ if (params.depositAuthority) {
1510
+ keys.push({
1511
+ pubkey: params.depositAuthority,
1512
+ isSigner: true,
1513
+ isWritable: false,
1514
+ });
1515
+ }
1516
+ // Associated Token Program must be last - only needed in transaction for CPI routing
1517
+ keys.push({ pubkey: splToken.ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false });
1518
+ return new web3_js.TransactionInstruction({
1519
+ programId: params.programId,
1520
+ keys,
1521
+ data,
1522
+ });
1523
+ }
1524
+ /**
1525
+ * Creates a transaction instruction to withdraw active stake from a stake pool.
1526
+ */
1527
+ static withdrawStake(params) {
1528
+ const { programId, stakePool, validatorList, withdrawAuthority, validatorStake, destinationStake, destinationStakeAuthority, sourceTransferAuthority, sourcePoolAccount, managerFeeAccount, poolMint, poolTokens, } = params;
1529
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStake;
1530
+ const data = encodeData(type, { poolTokens });
1531
+ const keys = [
1532
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1533
+ { pubkey: validatorList, isSigner: false, isWritable: true },
1534
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1535
+ { pubkey: validatorStake, isSigner: false, isWritable: true },
1536
+ { pubkey: destinationStake, isSigner: false, isWritable: true },
1537
+ { pubkey: destinationStakeAuthority, isSigner: false, isWritable: false },
1538
+ { pubkey: sourceTransferAuthority, isSigner: true, isWritable: false },
1539
+ { pubkey: sourcePoolAccount, isSigner: false, isWritable: true },
1540
+ { pubkey: managerFeeAccount, isSigner: false, isWritable: true },
1541
+ { pubkey: poolMint, isSigner: false, isWritable: true },
1542
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1543
+ { pubkey: splToken.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1544
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1545
+ ];
1546
+ return new web3_js.TransactionInstruction({
1547
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1548
+ keys,
1549
+ data,
1550
+ });
1551
+ }
1552
+ /**
1553
+ * Creates a transaction instruction to withdraw SOL from a stake pool.
1554
+ */
1555
+ static withdrawSol(params) {
1556
+ const { programId, stakePool, withdrawAuthority, sourceTransferAuthority, sourcePoolAccount, reserveStake, destinationSystemAccount, managerFeeAccount, solWithdrawAuthority, poolMint, poolTokens, } = params;
1557
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawSol;
1558
+ const data = encodeData(type, { poolTokens });
1559
+ const keys = [
1560
+ { pubkey: stakePool, isSigner: false, isWritable: true },
1561
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1562
+ { pubkey: sourceTransferAuthority, isSigner: true, isWritable: false },
1563
+ { pubkey: sourcePoolAccount, isSigner: false, isWritable: true },
1564
+ { pubkey: reserveStake, isSigner: false, isWritable: true },
1565
+ { pubkey: destinationSystemAccount, isSigner: false, isWritable: true },
1566
+ { pubkey: managerFeeAccount, isSigner: false, isWritable: true },
1567
+ { pubkey: poolMint, isSigner: false, isWritable: true },
1568
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1569
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1570
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1571
+ { pubkey: splToken.TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
1572
+ ];
1573
+ if (solWithdrawAuthority) {
1574
+ keys.push({
1575
+ pubkey: solWithdrawAuthority,
1576
+ isSigner: true,
1577
+ isWritable: false,
1578
+ });
1579
+ }
1580
+ return new web3_js.TransactionInstruction({
1581
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1582
+ keys,
1583
+ data,
1584
+ });
1585
+ }
1586
+ /**
1587
+ * Creates a transaction instruction to withdraw WSOL from a stake pool using a session.
1588
+ * Rent for ATA creation (if needed) is paid from the withdrawal amount.
1589
+ */
1590
+ static withdrawWsolWithSession(params) {
1591
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawWsolWithSession;
1592
+ const data = encodeData(type, {
1593
+ poolTokensIn: params.poolTokensIn,
1594
+ minimumLamportsOut: params.minimumLamportsOut,
1595
+ });
1596
+ const keys = [
1597
+ { pubkey: params.stakePool, isSigner: false, isWritable: true },
1598
+ { pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
1599
+ { pubkey: params.userTransferAuthority, isSigner: true, isWritable: true },
1600
+ { pubkey: params.poolTokensFrom, isSigner: false, isWritable: true },
1601
+ { pubkey: params.reserveStake, isSigner: false, isWritable: true },
1602
+ { pubkey: params.userWsolAccount, isSigner: false, isWritable: true },
1603
+ { pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
1604
+ { pubkey: params.poolMint, isSigner: false, isWritable: true },
1605
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1606
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1607
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1608
+ { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
1609
+ { pubkey: params.wsolMint, isSigner: false, isWritable: false },
1610
+ { pubkey: params.programSigner, isSigner: false, isWritable: true },
1611
+ { pubkey: params.userWallet, isSigner: false, isWritable: false },
1612
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1613
+ ];
1614
+ if (params.solWithdrawAuthority) {
1615
+ keys.push({
1616
+ pubkey: params.solWithdrawAuthority,
1617
+ isSigner: true,
1618
+ isWritable: false,
1619
+ });
1620
+ }
1621
+ // Associated Token Program must be last - only needed in transaction for CPI routing
1622
+ keys.push({ pubkey: splToken.ASSOCIATED_TOKEN_PROGRAM_ID, isSigner: false, isWritable: false });
1623
+ return new web3_js.TransactionInstruction({
1624
+ programId: params.programId,
1625
+ keys,
1626
+ data,
1627
+ });
1628
+ }
1629
+ /**
1630
+ * Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
1631
+ * The stake account is created as a PDA and rent is paid by the payer (typically paymaster).
1632
+ */
1633
+ static withdrawStakeWithSession(params) {
1634
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession;
1635
+ const data = encodeData(type, {
1636
+ poolTokensIn: params.poolTokensIn,
1637
+ minimumLamportsOut: params.minimumLamportsOut,
1638
+ userStakeSeed: params.userStakeSeed,
1639
+ });
1640
+ const keys = [
1641
+ { pubkey: params.stakePool, isSigner: false, isWritable: true },
1642
+ { pubkey: params.validatorList, isSigner: false, isWritable: true },
1643
+ { pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
1644
+ { pubkey: params.stakeToSplit, isSigner: false, isWritable: true },
1645
+ { pubkey: params.stakeToReceive, isSigner: false, isWritable: true },
1646
+ { pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info (signer_or_session)
1647
+ { pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (not used in session path)
1648
+ { pubkey: params.burnFromPool, isSigner: false, isWritable: true },
1649
+ { pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
1650
+ { pubkey: params.poolMint, isSigner: false, isWritable: true },
1651
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1652
+ { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
1653
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1654
+ { pubkey: params.programSigner, isSigner: false, isWritable: false },
1655
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1656
+ { pubkey: params.payer, isSigner: true, isWritable: true },
1657
+ ];
1658
+ return new web3_js.TransactionInstruction({
1659
+ programId: params.programId,
1660
+ keys,
1661
+ data,
1662
+ });
1663
+ }
1664
+ /**
1665
+ * Creates a transaction instruction to withdraw SOL from a deactivated user stake account using a Fogo session.
1666
+ * The stake account must be fully deactivated (inactive).
1667
+ */
1668
+ static withdrawFromStakeAccountWithSession(params) {
1669
+ const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawFromStakeAccountWithSession;
1670
+ const data = encodeData(type, {
1671
+ lamports: params.lamports,
1672
+ userStakeSeed: params.userStakeSeed,
1673
+ });
1674
+ const keys = [
1675
+ { pubkey: params.userStakeAccount, isSigner: false, isWritable: true },
1676
+ { pubkey: params.userWallet, isSigner: false, isWritable: true },
1677
+ { pubkey: web3_js.SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1678
+ { pubkey: web3_js.SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1679
+ { pubkey: params.sessionSigner, isSigner: true, isWritable: false },
1680
+ { pubkey: web3_js.StakeProgram.programId, isSigner: false, isWritable: false },
1681
+ ];
1682
+ return new web3_js.TransactionInstruction({
1683
+ programId: params.programId,
1684
+ keys,
1685
+ data,
1686
+ });
1687
+ }
1688
+ /**
1689
+ * Creates an instruction to create metadata
1690
+ * using the mpl token metadata program for the pool token
1691
+ */
1692
+ static createTokenMetadata(params) {
1693
+ const { programId, stakePool, withdrawAuthority, tokenMetadata, manager, payer, poolMint, name, symbol, uri, } = params;
1694
+ const keys = [
1695
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1696
+ { pubkey: manager, isSigner: true, isWritable: false },
1697
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1698
+ { pubkey: poolMint, isSigner: false, isWritable: false },
1699
+ { pubkey: payer, isSigner: true, isWritable: true },
1700
+ { pubkey: tokenMetadata, isSigner: false, isWritable: true },
1701
+ { pubkey: METADATA_PROGRAM_ID, isSigner: false, isWritable: false },
1702
+ { pubkey: web3_js.SystemProgram.programId, isSigner: false, isWritable: false },
1703
+ { pubkey: web3_js.SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
1704
+ ];
1705
+ const type = tokenMetadataLayout(17, name.length, symbol.length, uri.length);
1706
+ const data = encodeData(type, {
1707
+ nameLen: name.length,
1708
+ name: Buffer.from(name),
1709
+ symbolLen: symbol.length,
1710
+ symbol: Buffer.from(symbol),
1711
+ uriLen: uri.length,
1712
+ uri: Buffer.from(uri),
1713
+ });
1714
+ return new web3_js.TransactionInstruction({
1715
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1716
+ keys,
1717
+ data,
1718
+ });
1719
+ }
1720
+ /**
1721
+ * Creates an instruction to update metadata
1722
+ * in the mpl token metadata program account for the pool token
1723
+ */
1724
+ static updateTokenMetadata(params) {
1725
+ const { programId, stakePool, withdrawAuthority, tokenMetadata, manager, name, symbol, uri } = params;
1726
+ const keys = [
1727
+ { pubkey: stakePool, isSigner: false, isWritable: false },
1728
+ { pubkey: manager, isSigner: true, isWritable: false },
1729
+ { pubkey: withdrawAuthority, isSigner: false, isWritable: false },
1730
+ { pubkey: tokenMetadata, isSigner: false, isWritable: true },
1731
+ { pubkey: METADATA_PROGRAM_ID, isSigner: false, isWritable: false },
1732
+ ];
1733
+ const type = tokenMetadataLayout(18, name.length, symbol.length, uri.length);
1734
+ const data = encodeData(type, {
1735
+ nameLen: name.length,
1736
+ name: Buffer.from(name),
1737
+ symbolLen: symbol.length,
1738
+ symbol: Buffer.from(symbol),
1739
+ uriLen: uri.length,
1740
+ uri: Buffer.from(uri),
1741
+ });
1742
+ return new web3_js.TransactionInstruction({
1743
+ programId: programId !== null && programId !== void 0 ? programId : STAKE_POOL_PROGRAM_ID,
1744
+ keys,
1745
+ data,
1746
+ });
1747
+ }
1748
+ /**
1749
+ * Decode a deposit stake pool instruction and retrieve the instruction params.
1750
+ */
1751
+ static decodeDepositStake(instruction) {
1752
+ this.checkProgramId(instruction.programId);
1753
+ this.checkKeyLength(instruction.keys, 11);
1754
+ decodeData(STAKE_POOL_INSTRUCTION_LAYOUTS.DepositStake, instruction.data);
1755
+ return {
1756
+ programId: instruction.programId,
1757
+ stakePool: instruction.keys[0].pubkey,
1758
+ validatorList: instruction.keys[1].pubkey,
1759
+ depositAuthority: instruction.keys[2].pubkey,
1760
+ withdrawAuthority: instruction.keys[3].pubkey,
1761
+ depositStake: instruction.keys[4].pubkey,
1762
+ validatorStake: instruction.keys[5].pubkey,
1763
+ reserveStake: instruction.keys[6].pubkey,
1764
+ destinationPoolAccount: instruction.keys[7].pubkey,
1765
+ managerFeeAccount: instruction.keys[8].pubkey,
1766
+ referralPoolAccount: instruction.keys[9].pubkey,
1767
+ poolMint: instruction.keys[10].pubkey,
1768
+ };
1769
+ }
1770
+ /**
1771
+ * Decode a deposit sol instruction and retrieve the instruction params.
1772
+ */
1773
+ static decodeDepositSol(instruction) {
1774
+ this.checkProgramId(instruction.programId);
1775
+ this.checkKeyLength(instruction.keys, 9);
1776
+ const { amount } = decodeData(STAKE_POOL_INSTRUCTION_LAYOUTS.DepositSol, instruction.data);
1777
+ return {
1778
+ programId: instruction.programId,
1779
+ stakePool: instruction.keys[0].pubkey,
1780
+ depositAuthority: instruction.keys[1].pubkey,
1781
+ withdrawAuthority: instruction.keys[2].pubkey,
1782
+ reserveStake: instruction.keys[3].pubkey,
1783
+ fundingAccount: instruction.keys[4].pubkey,
1784
+ destinationPoolAccount: instruction.keys[5].pubkey,
1785
+ managerFeeAccount: instruction.keys[6].pubkey,
1786
+ referralPoolAccount: instruction.keys[7].pubkey,
1787
+ poolMint: instruction.keys[8].pubkey,
1788
+ lamports: amount,
1789
+ };
1790
+ }
1791
+ /**
1792
+ * @internal
1793
+ */
1794
+ static checkProgramId(programId) {
1795
+ if (!programId.equals(STAKE_POOL_PROGRAM_ID)
1796
+ && !programId.equals(DEVNET_STAKE_POOL_PROGRAM_ID)) {
1797
+ throw new Error('Invalid instruction; programId is not the stake pool program');
1798
+ }
1799
+ }
1800
+ /**
1801
+ * @internal
1802
+ */
1803
+ static checkKeyLength(keys, expectedLength) {
1804
+ if (keys.length < expectedLength) {
1805
+ throw new Error(`Invalid instruction; found ${keys.length} keys, expected at least ${expectedLength}`);
1806
+ }
1807
+ }
1808
+ }
1809
+
1810
+ function getStakePoolProgramId(rpcEndpoint) {
1811
+ if (rpcEndpoint.includes('devnet')) {
1812
+ return DEVNET_STAKE_POOL_PROGRAM_ID;
1813
+ }
1814
+ else {
1815
+ return STAKE_POOL_PROGRAM_ID;
1816
+ }
1817
+ }
1818
+ /**
1819
+ * Retrieves and deserializes a StakePool account using a web3js connection and the stake pool address.
1820
+ * @param connection An active web3js connection.
1821
+ * @param stakePoolAddress The public key (address) of the stake pool account.
1822
+ */
1823
+ async function getStakePoolAccount(connection, stakePoolAddress) {
1824
+ const account = await connection.getAccountInfo(stakePoolAddress);
1825
+ if (!account) {
1826
+ throw new Error('Invalid stake pool account');
1827
+ }
1828
+ return {
1829
+ pubkey: stakePoolAddress,
1830
+ account: {
1831
+ data: StakePoolLayout.decode(account.data),
1832
+ executable: account.executable,
1833
+ lamports: account.lamports,
1834
+ owner: account.owner,
1835
+ },
1836
+ };
1837
+ }
1838
+ /**
1839
+ * Retrieves and deserializes a Stake account using a web3js connection and the stake address.
1840
+ * @param connection An active web3js connection.
1841
+ * @param stakeAccount The public key (address) of the stake account.
1842
+ */
1843
+ async function getStakeAccount(connection, stakeAccount) {
1844
+ const result = (await connection.getParsedAccountInfo(stakeAccount)).value;
1845
+ if (!result || !('parsed' in result.data)) {
1846
+ throw new Error('Invalid stake account');
1847
+ }
1848
+ const program = result.data.program;
1849
+ if (program !== 'stake') {
1850
+ throw new Error('Not a stake account');
1851
+ }
1852
+ return create(result.data.parsed, StakeAccount);
1853
+ }
1854
+ /**
1855
+ * Retrieves all StakePool and ValidatorList accounts that are running a particular StakePool program.
1856
+ * @param connection An active web3js connection.
1857
+ * @param stakePoolProgramAddress The public key (address) of the StakePool program.
1858
+ */
1859
+ async function getStakePoolAccounts(connection, stakePoolProgramAddress) {
1860
+ const response = await connection.getProgramAccounts(stakePoolProgramAddress);
1861
+ return response
1862
+ .map((a) => {
1863
+ try {
1864
+ if (a.account.data.readUInt8() === 1) {
1865
+ const data = StakePoolLayout.decode(a.account.data);
1866
+ return {
1867
+ pubkey: a.pubkey,
1868
+ account: {
1869
+ data,
1870
+ executable: a.account.executable,
1871
+ lamports: a.account.lamports,
1872
+ owner: a.account.owner,
1873
+ },
1874
+ };
1875
+ }
1876
+ else if (a.account.data.readUInt8() === 2) {
1877
+ const data = ValidatorListLayout.decode(a.account.data);
1878
+ return {
1879
+ pubkey: a.pubkey,
1880
+ account: {
1881
+ data,
1882
+ executable: a.account.executable,
1883
+ lamports: a.account.lamports,
1884
+ owner: a.account.owner,
1885
+ },
1886
+ };
1887
+ }
1888
+ else {
1889
+ console.error(`Could not decode. StakePoolAccount Enum is ${a.account.data.readUInt8()}, expected 1 or 2!`);
1890
+ return undefined;
1891
+ }
1892
+ }
1893
+ catch (error) {
1894
+ console.error('Could not decode account. Error:', error);
1895
+ return undefined;
1896
+ }
1897
+ })
1898
+ .filter(a => a !== undefined);
1899
+ }
1900
+ /**
1901
+ * Creates instructions required to deposit stake to stake pool.
1902
+ */
1903
+ async function depositStake(connection, stakePoolAddress, authorizedPubkey, validatorVote, depositStake, poolTokenReceiverAccount) {
1904
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
1905
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
1906
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
1907
+ const validatorStake = await findStakeProgramAddress(stakePoolProgramId, validatorVote, stakePoolAddress);
1908
+ const instructions = [];
1909
+ const signers = [];
1910
+ const poolMint = stakePool.account.data.poolMint;
1911
+ // Create token account if not specified
1912
+ if (!poolTokenReceiverAccount) {
1913
+ const associatedAddress = splToken.getAssociatedTokenAddressSync(poolMint, authorizedPubkey);
1914
+ instructions.push(splToken.createAssociatedTokenAccountIdempotentInstruction(authorizedPubkey, associatedAddress, authorizedPubkey, poolMint));
1915
+ poolTokenReceiverAccount = associatedAddress;
1916
+ }
1917
+ instructions.push(...web3_js.StakeProgram.authorize({
1918
+ stakePubkey: depositStake,
1919
+ authorizedPubkey,
1920
+ newAuthorizedPubkey: stakePool.account.data.stakeDepositAuthority,
1921
+ stakeAuthorizationType: web3_js.StakeAuthorizationLayout.Staker,
1922
+ }).instructions);
1923
+ instructions.push(...web3_js.StakeProgram.authorize({
1924
+ stakePubkey: depositStake,
1925
+ authorizedPubkey,
1926
+ newAuthorizedPubkey: stakePool.account.data.stakeDepositAuthority,
1927
+ stakeAuthorizationType: web3_js.StakeAuthorizationLayout.Withdrawer,
1928
+ }).instructions);
1929
+ instructions.push(StakePoolInstruction.depositStake({
1930
+ programId: stakePoolProgramId,
1931
+ stakePool: stakePoolAddress,
1932
+ validatorList: stakePool.account.data.validatorList,
1933
+ depositAuthority: stakePool.account.data.stakeDepositAuthority,
1934
+ reserveStake: stakePool.account.data.reserveStake,
1935
+ managerFeeAccount: stakePool.account.data.managerFeeAccount,
1936
+ referralPoolAccount: poolTokenReceiverAccount,
1937
+ destinationPoolAccount: poolTokenReceiverAccount,
1938
+ withdrawAuthority,
1939
+ depositStake,
1940
+ validatorStake,
1941
+ poolMint,
1942
+ }));
1943
+ return {
1944
+ instructions,
1945
+ signers,
1946
+ };
1947
+ }
1948
+ /**
1949
+ * Creates instructions required to deposit sol to stake pool.
1950
+ */
1951
+ async function depositWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, lamports, minimumPoolTokensOut = 0, destinationTokenAccount, referrerTokenAccount, depositAuthority, payer,
1952
+ /**
1953
+ * Skip WSOL balance validation. Set to true when adding wrap instructions
1954
+ * in the same transaction that will fund the WSOL account before deposit.
1955
+ */
1956
+ skipBalanceCheck = false) {
1957
+ const wsolTokenAccount = splToken.getAssociatedTokenAddressSync(splToken.NATIVE_MINT, userPubkey);
1958
+ if (!skipBalanceCheck) {
1959
+ const tokenAccountInfo = await connection.getTokenAccountBalance(wsolTokenAccount, 'confirmed');
1960
+ const wsolBalance = tokenAccountInfo
1961
+ ? parseInt(tokenAccountInfo.value.amount)
1962
+ : 0;
1963
+ if (wsolBalance < lamports) {
1964
+ throw new Error(`Not enough WSOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(wsolBalance)} WSOL.`);
1965
+ }
1966
+ }
1967
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
1968
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
1969
+ const stakePool = stakePoolAccount.account.data;
1970
+ const instructions = [];
1971
+ // The program handles ATA creation internally using funds from the user's deposit
1972
+ // This prevents rent drain attacks where paymaster pays for ATA and user reclaims rent
1973
+ if (!destinationTokenAccount) {
1974
+ destinationTokenAccount = splToken.getAssociatedTokenAddressSync(stakePool.poolMint, userPubkey);
1975
+ }
1976
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
1977
+ const [programSigner] = web3_js.PublicKey.findProgramAddressSync([Buffer.from('fogo_session_program_signer')], stakePoolProgramId);
1978
+ const wsolTransientAccount = findWsolTransientProgramAddress(stakePoolProgramId, userPubkey);
1979
+ instructions.push(StakePoolInstruction.depositWsolWithSession({
1980
+ programId: stakePoolProgramId,
1981
+ stakePool: stakePoolAddress,
1982
+ reserveStake: stakePool.reserveStake,
1983
+ fundingAccount: signerOrSession,
1984
+ destinationPoolAccount: destinationTokenAccount,
1985
+ managerFeeAccount: stakePool.managerFeeAccount,
1986
+ referralPoolAccount: referrerTokenAccount !== null && referrerTokenAccount !== void 0 ? referrerTokenAccount : destinationTokenAccount,
1987
+ poolMint: stakePool.poolMint,
1988
+ lamportsIn: lamports,
1989
+ minimumPoolTokensOut,
1990
+ withdrawAuthority,
1991
+ depositAuthority,
1992
+ wsolMint: splToken.NATIVE_MINT,
1993
+ wsolTokenAccount,
1994
+ wsolTransientAccount,
1995
+ tokenProgramId: stakePool.tokenProgramId,
1996
+ programSigner,
1997
+ payer,
1998
+ userWallet: userPubkey,
1999
+ }));
2000
+ return {
2001
+ instructions,
2002
+ signers: [],
2003
+ };
2004
+ }
2005
+ /**
2006
+ * Creates instructions required to deposit sol to stake pool.
2007
+ */
2008
+ async function depositSol(connection, stakePoolAddress, from, lamports, destinationTokenAccount, referrerTokenAccount, depositAuthority) {
2009
+ const fromBalance = await connection.getBalance(from, 'confirmed');
2010
+ if (fromBalance < lamports) {
2011
+ throw new Error(`Not enough SOL to deposit into pool. Maximum deposit amount is ${lamportsToSol(fromBalance)} SOL.`);
2012
+ }
2013
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2014
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2015
+ const stakePool = stakePoolAccount.account.data;
2016
+ // Ephemeral SOL account just to do the transfer
2017
+ const userSolTransfer = new web3_js.Keypair();
2018
+ const signers = [userSolTransfer];
2019
+ const instructions = [];
2020
+ // Create the ephemeral SOL account
2021
+ instructions.push(web3_js.SystemProgram.transfer({
2022
+ fromPubkey: from,
2023
+ toPubkey: userSolTransfer.publicKey,
2024
+ lamports,
2025
+ }));
2026
+ // Create token account if not specified
2027
+ if (!destinationTokenAccount) {
2028
+ const associatedAddress = splToken.getAssociatedTokenAddressSync(stakePool.poolMint, from);
2029
+ instructions.push(splToken.createAssociatedTokenAccountIdempotentInstruction(from, associatedAddress, from, stakePool.poolMint));
2030
+ destinationTokenAccount = associatedAddress;
2031
+ }
2032
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2033
+ instructions.push(StakePoolInstruction.depositSol({
2034
+ programId: stakePoolProgramId,
2035
+ stakePool: stakePoolAddress,
2036
+ reserveStake: stakePool.reserveStake,
2037
+ fundingAccount: userSolTransfer.publicKey,
2038
+ destinationPoolAccount: destinationTokenAccount,
2039
+ managerFeeAccount: stakePool.managerFeeAccount,
2040
+ referralPoolAccount: referrerTokenAccount !== null && referrerTokenAccount !== void 0 ? referrerTokenAccount : destinationTokenAccount,
2041
+ poolMint: stakePool.poolMint,
2042
+ lamports,
2043
+ withdrawAuthority,
2044
+ depositAuthority,
2045
+ }));
2046
+ return {
2047
+ instructions,
2048
+ signers,
2049
+ };
2050
+ }
2051
+ /**
2052
+ * Creates instructions required to withdraw stake from a stake pool.
2053
+ */
2054
+ async function withdrawStake(connection, stakePoolAddress, tokenOwner, amount, useReserve = false, voteAccountAddress, stakeReceiver, poolTokenAccount, validatorComparator) {
2055
+ var _c, _d, _e, _f;
2056
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
2057
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2058
+ const poolAmount = new BN(solToLamports(amount));
2059
+ if (!poolTokenAccount) {
2060
+ poolTokenAccount = splToken.getAssociatedTokenAddressSync(stakePool.account.data.poolMint, tokenOwner);
2061
+ }
2062
+ const tokenAccount = await splToken.getAccount(connection, poolTokenAccount);
2063
+ // Check withdrawFrom balance
2064
+ if (tokenAccount.amount < poolAmount.toNumber()) {
2065
+ throw new Error(`Not enough token balance to withdraw ${lamportsToSol(poolAmount)} pool tokens.
2066
+ Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`);
2067
+ }
2068
+ const stakeAccountRentExemption = await connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space);
2069
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2070
+ let stakeReceiverAccount = null;
2071
+ if (stakeReceiver) {
2072
+ stakeReceiverAccount = await getStakeAccount(connection, stakeReceiver);
2073
+ }
2074
+ const withdrawAccounts = [];
2075
+ if (useReserve) {
2076
+ withdrawAccounts.push({
2077
+ stakeAddress: stakePool.account.data.reserveStake,
2078
+ voteAddress: undefined,
2079
+ poolAmount,
2080
+ });
2081
+ }
2082
+ else if (stakeReceiverAccount
2083
+ && (stakeReceiverAccount === null || stakeReceiverAccount === void 0 ? void 0 : stakeReceiverAccount.type) === 'delegated') {
2084
+ const voteAccount = (_d = (_c = stakeReceiverAccount.info) === null || _c === void 0 ? void 0 : _c.stake) === null || _d === void 0 ? void 0 : _d.delegation.voter;
2085
+ if (!voteAccount) {
2086
+ throw new Error(`Invalid stake receiver ${stakeReceiver} delegation`);
2087
+ }
2088
+ const validatorListAccount = await connection.getAccountInfo(stakePool.account.data.validatorList);
2089
+ const validatorList = ValidatorListLayout.decode(validatorListAccount === null || validatorListAccount === void 0 ? void 0 : validatorListAccount.data);
2090
+ const isValidVoter = validatorList.validators.find(val => val.voteAccountAddress.equals(voteAccount));
2091
+ if (voteAccountAddress && voteAccountAddress !== voteAccount) {
2092
+ throw new Error(`Provided withdrawal vote account ${voteAccountAddress} does not match delegation on stake receiver account ${voteAccount},
2093
+ remove this flag or provide a different stake account delegated to ${voteAccountAddress}`);
2094
+ }
2095
+ if (isValidVoter) {
2096
+ const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, voteAccount, stakePoolAddress);
2097
+ const stakeAccount = await connection.getAccountInfo(stakeAccountAddress);
2098
+ if (!stakeAccount) {
2099
+ throw new Error(`Preferred withdraw valdator's stake account is invalid`);
2100
+ }
2101
+ const availableForWithdrawal = calcLamportsWithdrawAmount(stakePool.account.data, new BN(stakeAccount.lamports
2102
+ - MINIMUM_ACTIVE_STAKE
2103
+ - stakeAccountRentExemption));
2104
+ if (availableForWithdrawal.lt(poolAmount)) {
2105
+ throw new Error(`Not enough lamports available for withdrawal from ${stakeAccountAddress},
2106
+ ${poolAmount} asked, ${availableForWithdrawal} available.`);
2107
+ }
2108
+ withdrawAccounts.push({
2109
+ stakeAddress: stakeAccountAddress,
2110
+ voteAddress: voteAccount,
2111
+ poolAmount,
2112
+ });
2113
+ }
2114
+ else {
2115
+ throw new Error(`Provided stake account is delegated to a vote account ${voteAccount} which does not exist in the stake pool`);
2116
+ }
2117
+ }
2118
+ else if (voteAccountAddress) {
2119
+ const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, voteAccountAddress, stakePoolAddress);
2120
+ const stakeAccount = await connection.getAccountInfo(stakeAccountAddress);
2121
+ if (!stakeAccount) {
2122
+ throw new Error('Invalid Stake Account');
2123
+ }
2124
+ const availableLamports = new BN(stakeAccount.lamports - MINIMUM_ACTIVE_STAKE - stakeAccountRentExemption);
2125
+ if (availableLamports.lt(new BN(0))) {
2126
+ throw new Error('Invalid Stake Account');
2127
+ }
2128
+ const availableForWithdrawal = calcLamportsWithdrawAmount(stakePool.account.data, availableLamports);
2129
+ if (availableForWithdrawal.lt(poolAmount)) {
2130
+ // noinspection ExceptionCaughtLocallyJS
2131
+ throw new Error(`Not enough lamports available for withdrawal from ${stakeAccountAddress},
2132
+ ${poolAmount} asked, ${availableForWithdrawal} available.`);
2133
+ }
2134
+ withdrawAccounts.push({
2135
+ stakeAddress: stakeAccountAddress,
2136
+ voteAddress: voteAccountAddress,
2137
+ poolAmount,
2138
+ });
2139
+ }
2140
+ else {
2141
+ // Get the list of accounts to withdraw from
2142
+ withdrawAccounts.push(...(await prepareWithdrawAccounts(connection, stakePool.account.data, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.account.data.managerFeeAccount))));
2143
+ }
2144
+ // Construct transaction to withdraw from withdrawAccounts account list
2145
+ const instructions = [];
2146
+ const userTransferAuthority = web3_js.Keypair.generate();
2147
+ const signers = [userTransferAuthority];
2148
+ instructions.push(splToken.createApproveInstruction(poolTokenAccount, userTransferAuthority.publicKey, tokenOwner, poolAmount.toNumber()));
2149
+ let totalRentFreeBalances = 0;
2150
+ // Max 5 accounts to prevent an error: "Transaction too large"
2151
+ const maxWithdrawAccounts = 5;
2152
+ let i = 0;
2153
+ // Go through prepared accounts and withdraw/claim them
2154
+ for (const withdrawAccount of withdrawAccounts) {
2155
+ if (i > maxWithdrawAccounts) {
2156
+ break;
2157
+ }
2158
+ // Convert pool tokens amount to lamports
2159
+ const solWithdrawAmount = calcLamportsWithdrawAmount(stakePool.account.data, withdrawAccount.poolAmount);
2160
+ let infoMsg = `Withdrawing ◎${solWithdrawAmount},
2161
+ from stake account ${(_e = withdrawAccount.stakeAddress) === null || _e === void 0 ? void 0 : _e.toBase58()}`;
2162
+ if (withdrawAccount.voteAddress) {
2163
+ infoMsg = `${infoMsg}, delegated to ${(_f = withdrawAccount.voteAddress) === null || _f === void 0 ? void 0 : _f.toBase58()}`;
2164
+ }
2165
+ console.info(infoMsg);
2166
+ let stakeToReceive;
2167
+ if (!stakeReceiver
2168
+ || (stakeReceiverAccount && stakeReceiverAccount.type === 'delegated')) {
2169
+ const stakeKeypair = newStakeAccount(tokenOwner, instructions, stakeAccountRentExemption);
2170
+ signers.push(stakeKeypair);
2171
+ totalRentFreeBalances += stakeAccountRentExemption;
2172
+ stakeToReceive = stakeKeypair.publicKey;
2173
+ }
2174
+ else {
2175
+ stakeToReceive = stakeReceiver;
2176
+ }
2177
+ instructions.push(StakePoolInstruction.withdrawStake({
2178
+ programId: stakePoolProgramId,
2179
+ stakePool: stakePoolAddress,
2180
+ validatorList: stakePool.account.data.validatorList,
2181
+ validatorStake: withdrawAccount.stakeAddress,
2182
+ destinationStake: stakeToReceive,
2183
+ destinationStakeAuthority: tokenOwner,
2184
+ sourceTransferAuthority: userTransferAuthority.publicKey,
2185
+ sourcePoolAccount: poolTokenAccount,
2186
+ managerFeeAccount: stakePool.account.data.managerFeeAccount,
2187
+ poolMint: stakePool.account.data.poolMint,
2188
+ poolTokens: withdrawAccount.poolAmount.toNumber(),
2189
+ withdrawAuthority,
2190
+ }));
2191
+ i++;
2192
+ }
2193
+ if (stakeReceiver
2194
+ && stakeReceiverAccount
2195
+ && stakeReceiverAccount.type === 'delegated') {
2196
+ signers.forEach((newStakeKeypair) => {
2197
+ instructions.concat(web3_js.StakeProgram.merge({
2198
+ stakePubkey: stakeReceiver,
2199
+ sourceStakePubKey: newStakeKeypair.publicKey,
2200
+ authorizedPubkey: tokenOwner,
2201
+ }).instructions);
2202
+ });
2203
+ }
2204
+ return {
2205
+ instructions,
2206
+ signers,
2207
+ stakeReceiver,
2208
+ totalRentFreeBalances,
2209
+ };
2210
+ }
2211
+ /**
2212
+ * Creates instructions required to withdraw SOL directly from a stake pool.
2213
+ */
2214
+ async function withdrawSol(connection, stakePoolAddress, tokenOwner, solReceiver, amount, solWithdrawAuthority) {
2215
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
2216
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2217
+ const poolAmount = solToLamports(amount);
2218
+ const poolTokenAccount = splToken.getAssociatedTokenAddressSync(stakePool.account.data.poolMint, tokenOwner);
2219
+ const tokenAccount = await splToken.getAccount(connection, poolTokenAccount);
2220
+ // Check withdrawFrom balance
2221
+ if (tokenAccount.amount < poolAmount) {
2222
+ throw new Error(`Not enough token balance to withdraw ${lamportsToSol(poolAmount)} pool tokens.
2223
+ Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`);
2224
+ }
2225
+ // Construct transaction to withdraw from withdrawAccounts account list
2226
+ const instructions = [];
2227
+ const userTransferAuthority = web3_js.Keypair.generate();
2228
+ const signers = [userTransferAuthority];
2229
+ instructions.push(splToken.createApproveInstruction(poolTokenAccount, userTransferAuthority.publicKey, tokenOwner, poolAmount));
2230
+ const poolWithdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2231
+ if (solWithdrawAuthority) {
2232
+ const expectedSolWithdrawAuthority = stakePool.account.data.solWithdrawAuthority;
2233
+ if (!expectedSolWithdrawAuthority) {
2234
+ throw new Error('SOL withdraw authority specified in arguments but stake pool has none');
2235
+ }
2236
+ if (solWithdrawAuthority.toBase58() !== expectedSolWithdrawAuthority.toBase58()) {
2237
+ throw new Error(`Invalid deposit withdraw specified, expected ${expectedSolWithdrawAuthority.toBase58()}, received ${solWithdrawAuthority.toBase58()}`);
2238
+ }
2239
+ }
2240
+ const withdrawTransaction = StakePoolInstruction.withdrawSol({
2241
+ programId: stakePoolProgramId,
2242
+ stakePool: stakePoolAddress,
2243
+ withdrawAuthority: poolWithdrawAuthority,
2244
+ reserveStake: stakePool.account.data.reserveStake,
2245
+ sourcePoolAccount: poolTokenAccount,
2246
+ sourceTransferAuthority: userTransferAuthority.publicKey,
2247
+ destinationSystemAccount: solReceiver,
2248
+ managerFeeAccount: stakePool.account.data.managerFeeAccount,
2249
+ poolMint: stakePool.account.data.poolMint,
2250
+ poolTokens: poolAmount,
2251
+ solWithdrawAuthority,
2252
+ });
2253
+ instructions.push(withdrawTransaction);
2254
+ return {
2255
+ instructions,
2256
+ signers,
2257
+ };
2258
+ }
2259
+ /**
2260
+ * Creates instructions required to withdraw wSOL from a stake pool.
2261
+ * Rent for ATA creation (if needed) is paid from the withdrawal amount.
2262
+ */
2263
+ async function withdrawWsolWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, amount, minimumLamportsOut = 0, solWithdrawAuthority) {
2264
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2265
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2266
+ const stakePool = stakePoolAccount.account.data;
2267
+ const poolTokens = solToLamports(amount);
2268
+ const poolTokenAccount = splToken.getAssociatedTokenAddressSync(stakePool.poolMint, userPubkey);
2269
+ const tokenAccount = await splToken.getAccount(connection, poolTokenAccount);
2270
+ if (tokenAccount.amount < poolTokens) {
2271
+ throw new Error(`Not enough token balance to withdraw ${amount} pool tokens.
2272
+ Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`);
2273
+ }
2274
+ const userWsolAccount = splToken.getAssociatedTokenAddressSync(splToken.NATIVE_MINT, userPubkey);
2275
+ const instructions = [];
2276
+ const signers = [];
2277
+ // The program handles wSOL ATA creation internally
2278
+ // This prevents rent drain attacks where paymaster pays for ATA and user reclaims rent
2279
+ const [programSigner] = web3_js.PublicKey.findProgramAddressSync([Buffer.from('fogo_session_program_signer')], stakePoolProgramId);
2280
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2281
+ instructions.push(StakePoolInstruction.withdrawWsolWithSession({
2282
+ programId: stakePoolProgramId,
2283
+ stakePool: stakePoolAddress,
2284
+ withdrawAuthority,
2285
+ userTransferAuthority: signerOrSession,
2286
+ poolTokensFrom: poolTokenAccount,
2287
+ reserveStake: stakePool.reserveStake,
2288
+ userWsolAccount,
2289
+ managerFeeAccount: stakePool.managerFeeAccount,
2290
+ poolMint: stakePool.poolMint,
2291
+ tokenProgramId: stakePool.tokenProgramId,
2292
+ solWithdrawAuthority,
2293
+ wsolMint: splToken.NATIVE_MINT,
2294
+ programSigner,
2295
+ userWallet: userPubkey,
2296
+ poolTokensIn: poolTokens,
2297
+ minimumLamportsOut,
2298
+ }));
2299
+ return {
2300
+ instructions,
2301
+ signers,
2302
+ };
2303
+ }
2304
+ /**
2305
+ * Finds the next available seed for creating a user stake PDA.
2306
+ * Scans from startSeed until an unused PDA is found.
2307
+ *
2308
+ * @param connection - Solana connection
2309
+ * @param programId - The stake pool program ID
2310
+ * @param userPubkey - User's wallet (used for PDA derivation)
2311
+ * @param startSeed - Starting seed to search from (default: 0)
2312
+ * @param maxSeed - Maximum seed to check before giving up (default: 1000)
2313
+ * @returns The next available seed
2314
+ * @throws Error if no available seed found within maxSeed
2315
+ */
2316
+ async function findNextUserStakeSeed(connection, programId, userPubkey, startSeed = 0, maxSeed = 1000) {
2317
+ for (let seed = startSeed; seed < startSeed + maxSeed; seed++) {
2318
+ const pda = findUserStakeProgramAddress(programId, userPubkey, seed);
2319
+ const account = await connection.getAccountInfo(pda);
2320
+ if (!account) {
2321
+ return seed;
2322
+ }
2323
+ }
2324
+ throw new Error(`No available user stake seed found between ${startSeed} and ${startSeed + maxSeed - 1}`);
2325
+ }
2326
+ /**
2327
+ * Fetches all user stake accounts created via WithdrawStakeWithSession.
2328
+ * These are PDAs derived from [b"user_stake", user_wallet, seed].
2329
+ *
2330
+ * @param connection - Solana connection
2331
+ * @param programId - The stake pool program ID
2332
+ * @param userPubkey - User's wallet address
2333
+ * @param maxSeed - Maximum seed to check (default: 100)
2334
+ * @returns Array of user stake accounts with their details
2335
+ */
2336
+ async function getUserStakeAccounts(connection, programId, userPubkey, maxSeed = 100) {
2337
+ var _c, _d;
2338
+ const stakeAccounts = [];
2339
+ const currentEpoch = (await connection.getEpochInfo()).epoch;
2340
+ for (let seed = 0; seed < maxSeed; seed++) {
2341
+ const pda = findUserStakeProgramAddress(programId, userPubkey, seed);
2342
+ const accountInfo = await connection.getAccountInfo(pda);
2343
+ if (!accountInfo) {
2344
+ continue; // Skip empty slots, there might be gaps
2345
+ }
2346
+ // Check if owned by stake program
2347
+ if (!accountInfo.owner.equals(web3_js.StakeProgram.programId)) {
2348
+ continue;
2349
+ }
2350
+ // Parse stake account data
2351
+ const stakeAccount = {
2352
+ pubkey: pda,
2353
+ seed,
2354
+ lamports: accountInfo.lamports,
2355
+ state: 'inactive',
2356
+ };
2357
+ try {
2358
+ // Parse the stake account to get delegation info
2359
+ const parsedAccount = await connection.getParsedAccountInfo(pda);
2360
+ if (parsedAccount.value && 'parsed' in parsedAccount.value.data) {
2361
+ const parsed = parsedAccount.value.data.parsed;
2362
+ if (parsed.type === 'delegated' && ((_d = (_c = parsed.info) === null || _c === void 0 ? void 0 : _c.stake) === null || _d === void 0 ? void 0 : _d.delegation)) {
2363
+ const delegation = parsed.info.stake.delegation;
2364
+ stakeAccount.voter = new web3_js.PublicKey(delegation.voter);
2365
+ stakeAccount.activationEpoch = Number(delegation.activationEpoch);
2366
+ stakeAccount.deactivationEpoch = Number(delegation.deactivationEpoch);
2367
+ // Determine state based on epochs
2368
+ const activationEpoch = stakeAccount.activationEpoch;
2369
+ const deactivationEpoch = stakeAccount.deactivationEpoch;
2370
+ if (deactivationEpoch !== undefined && deactivationEpoch < Number.MAX_SAFE_INTEGER && deactivationEpoch <= currentEpoch) {
2371
+ stakeAccount.state = 'inactive';
2372
+ }
2373
+ else if (deactivationEpoch !== undefined && deactivationEpoch < Number.MAX_SAFE_INTEGER) {
2374
+ stakeAccount.state = 'deactivating';
2375
+ }
2376
+ else if (activationEpoch !== undefined && activationEpoch <= currentEpoch) {
2377
+ stakeAccount.state = 'active';
2378
+ }
2379
+ else {
2380
+ stakeAccount.state = 'activating';
2381
+ }
2382
+ }
2383
+ }
2384
+ }
2385
+ catch {
2386
+ // If parsing fails, keep default 'inactive' state
2387
+ }
2388
+ stakeAccounts.push(stakeAccount);
2389
+ }
2390
+ return stakeAccounts;
2391
+ }
2392
+ /**
2393
+ * Withdraws stake from a stake pool using a Fogo session.
2394
+ *
2395
+ * The on-chain program creates stake account PDAs. The rent for these accounts
2396
+ * is paid by the payer (typically the paymaster), not deducted from the user's withdrawal.
2397
+ *
2398
+ * @param connection - Solana connection
2399
+ * @param stakePoolAddress - The stake pool to withdraw from
2400
+ * @param signerOrSession - The session signer public key
2401
+ * @param userPubkey - User's wallet (used for PDA derivation and token ownership)
2402
+ * @param payer - Payer for stake account rent (typically paymaster)
2403
+ * @param amount - Amount of pool tokens to withdraw
2404
+ * @param userStakeSeedStart - Starting seed for user stake PDA derivation (default: 0)
2405
+ * @param useReserve - Whether to withdraw from reserve (default: false)
2406
+ * @param voteAccountAddress - Optional specific validator to withdraw from
2407
+ * @param minimumLamportsOut - Minimum lamports to receive (slippage protection)
2408
+ * @param validatorComparator - Optional comparator for validator selection
2409
+ */
2410
+ async function withdrawStakeWithSession(connection, stakePoolAddress, signerOrSession, userPubkey, payer, amount, userStakeSeedStart = 0, useReserve = false, voteAccountAddress, minimumLamportsOut = 0, validatorComparator) {
2411
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2412
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2413
+ const stakePool = stakePoolAccount.account.data;
2414
+ const poolTokens = solToLamports(amount);
2415
+ const poolAmount = new BN(poolTokens);
2416
+ const poolTokenAccount = splToken.getAssociatedTokenAddressSync(stakePool.poolMint, userPubkey);
2417
+ const tokenAccount = await splToken.getAccount(connection, poolTokenAccount);
2418
+ if (tokenAccount.amount < poolTokens) {
2419
+ throw new Error(`Not enough token balance to withdraw ${amount} pool tokens.
2420
+ Maximum withdraw amount is ${lamportsToSol(tokenAccount.amount)} pool tokens.`);
2421
+ }
2422
+ const [programSigner] = web3_js.PublicKey.findProgramAddressSync([Buffer.from('fogo_session_program_signer')], stakePoolProgramId);
2423
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2424
+ const stakeAccountRentExemption = await connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space);
2425
+ // Determine which stake accounts to withdraw from
2426
+ const withdrawAccounts = [];
2427
+ if (useReserve) {
2428
+ withdrawAccounts.push({
2429
+ stakeAddress: stakePool.reserveStake,
2430
+ voteAddress: undefined,
2431
+ poolAmount,
2432
+ });
2433
+ }
2434
+ else if (voteAccountAddress) {
2435
+ const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, voteAccountAddress, stakePoolAddress);
2436
+ const stakeAccount = await connection.getAccountInfo(stakeAccountAddress);
2437
+ if (!stakeAccount) {
2438
+ throw new Error(`Validator stake account not found for vote address ${voteAccountAddress.toBase58()}`);
2439
+ }
2440
+ const availableLamports = new BN(stakeAccount.lamports - MINIMUM_ACTIVE_STAKE - stakeAccountRentExemption);
2441
+ if (availableLamports.lt(new BN(0))) {
2442
+ throw new Error('Invalid Stake Account');
2443
+ }
2444
+ const availableForWithdrawal = calcLamportsWithdrawAmount(stakePool, availableLamports);
2445
+ if (availableForWithdrawal.lt(poolAmount)) {
2446
+ throw new Error(`Not enough lamports available for withdrawal from ${stakeAccountAddress},
2447
+ ${poolAmount} asked, ${availableForWithdrawal} available.`);
2448
+ }
2449
+ withdrawAccounts.push({
2450
+ stakeAddress: stakeAccountAddress,
2451
+ voteAddress: voteAccountAddress,
2452
+ poolAmount,
2453
+ });
2454
+ }
2455
+ else {
2456
+ // Get the list of accounts to withdraw from automatically
2457
+ withdrawAccounts.push(...(await prepareWithdrawAccounts(connection, stakePool, stakePoolAddress, poolAmount, validatorComparator, poolTokenAccount.equals(stakePool.managerFeeAccount))));
2458
+ }
2459
+ const instructions = [];
2460
+ const stakeAccountPubkeys = [];
2461
+ const userStakeSeeds = [];
2462
+ // Max 5 accounts to prevent an error: "Transaction too large"
2463
+ const maxWithdrawAccounts = 5;
2464
+ let i = 0;
2465
+ for (const withdrawAccount of withdrawAccounts) {
2466
+ if (i >= maxWithdrawAccounts) {
2467
+ break;
2468
+ }
2469
+ // Derive the stake account PDA for this withdrawal
2470
+ const userStakeSeed = userStakeSeedStart + i;
2471
+ const stakeReceiverPubkey = findUserStakeProgramAddress(stakePoolProgramId, userPubkey, userStakeSeed);
2472
+ stakeAccountPubkeys.push(stakeReceiverPubkey);
2473
+ userStakeSeeds.push(userStakeSeed);
2474
+ // The on-chain program creates the stake account PDA and rent is paid by payer.
2475
+ instructions.push(StakePoolInstruction.withdrawStakeWithSession({
2476
+ programId: stakePoolProgramId,
2477
+ stakePool: stakePoolAddress,
2478
+ validatorList: stakePool.validatorList,
2479
+ withdrawAuthority,
2480
+ stakeToSplit: withdrawAccount.stakeAddress,
2481
+ stakeToReceive: stakeReceiverPubkey,
2482
+ sessionSigner: signerOrSession,
2483
+ burnFromPool: poolTokenAccount,
2484
+ managerFeeAccount: stakePool.managerFeeAccount,
2485
+ poolMint: stakePool.poolMint,
2486
+ tokenProgramId: stakePool.tokenProgramId,
2487
+ programSigner,
2488
+ payer,
2489
+ poolTokensIn: withdrawAccount.poolAmount.toNumber(),
2490
+ minimumLamportsOut,
2491
+ userStakeSeed,
2492
+ }));
2493
+ i++;
2494
+ }
2495
+ return {
2496
+ instructions,
2497
+ stakeAccountPubkeys,
2498
+ userStakeSeeds,
2499
+ };
2500
+ }
2501
+ async function addValidatorToPool(connection, stakePoolAddress, validatorVote, seed) {
2502
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2503
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2504
+ const stakePool = stakePoolAccount.account.data;
2505
+ const { reserveStake, staker, validatorList } = stakePool;
2506
+ const validatorListAccount = await getValidatorListAccount(connection, validatorList);
2507
+ const validatorInfo = validatorListAccount.account.data.validators.find(v => v.voteAccountAddress.toBase58() === validatorVote.toBase58());
2508
+ if (validatorInfo) {
2509
+ throw new Error('Vote account is already in validator list');
2510
+ }
2511
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2512
+ const validatorStake = await findStakeProgramAddress(stakePoolProgramId, validatorVote, stakePoolAddress, seed);
2513
+ const instructions = [
2514
+ StakePoolInstruction.addValidatorToPool({
2515
+ programId: stakePoolProgramId,
2516
+ stakePool: stakePoolAddress,
2517
+ staker,
2518
+ reserveStake,
2519
+ withdrawAuthority,
2520
+ validatorList,
2521
+ validatorStake,
2522
+ validatorVote,
2523
+ }),
2524
+ ];
2525
+ return {
2526
+ instructions,
2527
+ };
2528
+ }
2529
+ async function removeValidatorFromPool(connection, stakePoolAddress, validatorVote, seed) {
2530
+ const stakePoolAccount = await getStakePoolAccount(connection, stakePoolAddress);
2531
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2532
+ const stakePool = stakePoolAccount.account.data;
2533
+ const { staker, validatorList } = stakePool;
2534
+ const validatorListAccount = await getValidatorListAccount(connection, validatorList);
2535
+ const validatorInfo = validatorListAccount.account.data.validators.find(v => v.voteAccountAddress.toBase58() === validatorVote.toBase58());
2536
+ if (!validatorInfo) {
2537
+ throw new Error('Vote account is not already in validator list');
2538
+ }
2539
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2540
+ const validatorStake = await findStakeProgramAddress(stakePoolProgramId, validatorVote, stakePoolAddress, seed);
2541
+ const transientStakeSeed = validatorInfo.transientSeedSuffixStart;
2542
+ const transientStake = await findTransientStakeProgramAddress(stakePoolProgramId, validatorInfo.voteAccountAddress, stakePoolAddress, transientStakeSeed);
2543
+ const instructions = [
2544
+ StakePoolInstruction.removeValidatorFromPool({
2545
+ programId: stakePoolProgramId,
2546
+ stakePool: stakePoolAddress,
2547
+ staker,
2548
+ withdrawAuthority,
2549
+ validatorList,
2550
+ validatorStake,
2551
+ transientStake,
2552
+ }),
2553
+ ];
2554
+ return {
2555
+ instructions,
2556
+ };
2557
+ }
2558
+ /**
2559
+ * Creates instructions required to increase validator stake.
2560
+ */
2561
+ async function increaseValidatorStake(connection, stakePoolAddress, validatorVote, lamports, ephemeralStakeSeed) {
2562
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
2563
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2564
+ const validatorList = await getValidatorListAccount(connection, stakePool.account.data.validatorList);
2565
+ const validatorInfo = validatorList.account.data.validators.find(v => v.voteAccountAddress.toBase58() === validatorVote.toBase58());
2566
+ if (!validatorInfo) {
2567
+ throw new Error('Vote account not found in validator list');
2568
+ }
2569
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2570
+ // Bump transient seed suffix by one to avoid reuse when not using the increaseAdditionalStake instruction
2571
+ const transientStakeSeed = ephemeralStakeSeed === undefined
2572
+ ? validatorInfo.transientSeedSuffixStart.addn(1)
2573
+ : validatorInfo.transientSeedSuffixStart;
2574
+ const transientStake = await findTransientStakeProgramAddress(stakePoolProgramId, validatorInfo.voteAccountAddress, stakePoolAddress, transientStakeSeed);
2575
+ const validatorStake = await findStakeProgramAddress(stakePoolProgramId, validatorInfo.voteAccountAddress, stakePoolAddress);
2576
+ const instructions = [];
2577
+ if (ephemeralStakeSeed !== undefined) {
2578
+ const ephemeralStake = await findEphemeralStakeProgramAddress(stakePoolProgramId, stakePoolAddress, new BN(ephemeralStakeSeed));
2579
+ instructions.push(StakePoolInstruction.increaseAdditionalValidatorStake({
2580
+ programId: stakePoolProgramId,
2581
+ stakePool: stakePoolAddress,
2582
+ staker: stakePool.account.data.staker,
2583
+ validatorList: stakePool.account.data.validatorList,
2584
+ reserveStake: stakePool.account.data.reserveStake,
2585
+ transientStakeSeed: transientStakeSeed.toNumber(),
2586
+ withdrawAuthority,
2587
+ transientStake,
2588
+ validatorStake,
2589
+ validatorVote,
2590
+ lamports,
2591
+ ephemeralStake,
2592
+ ephemeralStakeSeed,
2593
+ }));
2594
+ }
2595
+ else {
2596
+ instructions.push(StakePoolInstruction.increaseValidatorStake({
2597
+ programId: stakePoolProgramId,
2598
+ stakePool: stakePoolAddress,
2599
+ staker: stakePool.account.data.staker,
2600
+ validatorList: stakePool.account.data.validatorList,
2601
+ reserveStake: stakePool.account.data.reserveStake,
2602
+ transientStakeSeed: transientStakeSeed.toNumber(),
2603
+ withdrawAuthority,
2604
+ transientStake,
2605
+ validatorStake,
2606
+ validatorVote,
2607
+ lamports,
2608
+ }));
2609
+ }
2610
+ return {
2611
+ instructions,
2612
+ };
2613
+ }
2614
+ /**
2615
+ * Creates instructions required to decrease validator stake.
2616
+ */
2617
+ async function decreaseValidatorStake(connection, stakePoolAddress, validatorVote, lamports, ephemeralStakeSeed) {
2618
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
2619
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2620
+ const validatorList = await getValidatorListAccount(connection, stakePool.account.data.validatorList);
2621
+ const validatorInfo = validatorList.account.data.validators.find(v => v.voteAccountAddress.toBase58() === validatorVote.toBase58());
2622
+ if (!validatorInfo) {
2623
+ throw new Error('Vote account not found in validator list');
2624
+ }
2625
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2626
+ const validatorStake = await findStakeProgramAddress(stakePoolProgramId, validatorInfo.voteAccountAddress, stakePoolAddress);
2627
+ // Bump transient seed suffix by one to avoid reuse when not using the decreaseAdditionalStake instruction
2628
+ const transientStakeSeed = ephemeralStakeSeed === undefined
2629
+ ? validatorInfo.transientSeedSuffixStart.addn(1)
2630
+ : validatorInfo.transientSeedSuffixStart;
2631
+ const transientStake = await findTransientStakeProgramAddress(stakePoolProgramId, validatorInfo.voteAccountAddress, stakePoolAddress, transientStakeSeed);
2632
+ const instructions = [];
2633
+ if (ephemeralStakeSeed !== undefined) {
2634
+ const ephemeralStake = await findEphemeralStakeProgramAddress(stakePoolProgramId, stakePoolAddress, new BN(ephemeralStakeSeed));
2635
+ instructions.push(StakePoolInstruction.decreaseAdditionalValidatorStake({
2636
+ programId: stakePoolProgramId,
2637
+ stakePool: stakePoolAddress,
2638
+ staker: stakePool.account.data.staker,
2639
+ validatorList: stakePool.account.data.validatorList,
2640
+ reserveStake: stakePool.account.data.reserveStake,
2641
+ transientStakeSeed: transientStakeSeed.toNumber(),
2642
+ withdrawAuthority,
2643
+ validatorStake,
2644
+ transientStake,
2645
+ lamports,
2646
+ ephemeralStake,
2647
+ ephemeralStakeSeed,
2648
+ }));
2649
+ }
2650
+ else {
2651
+ instructions.push(StakePoolInstruction.decreaseValidatorStakeWithReserve({
2652
+ programId: stakePoolProgramId,
2653
+ stakePool: stakePoolAddress,
2654
+ staker: stakePool.account.data.staker,
2655
+ validatorList: stakePool.account.data.validatorList,
2656
+ reserveStake: stakePool.account.data.reserveStake,
2657
+ transientStakeSeed: transientStakeSeed.toNumber(),
2658
+ withdrawAuthority,
2659
+ validatorStake,
2660
+ transientStake,
2661
+ lamports,
2662
+ }));
2663
+ }
2664
+ return {
2665
+ instructions,
2666
+ };
2667
+ }
2668
+ /**
2669
+ * Creates instructions required to completely update a stake pool after epoch change.
2670
+ */
2671
+ async function updateStakePool(connection, stakePool, noMerge = false) {
2672
+ const stakePoolAddress = stakePool.pubkey;
2673
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2674
+ const validatorList = await getValidatorListAccount(connection, stakePool.account.data.validatorList);
2675
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2676
+ const updateListInstructions = [];
2677
+ const instructions = [];
2678
+ let startIndex = 0;
2679
+ const validatorChunks = arrayChunk(validatorList.account.data.validators, MAX_VALIDATORS_TO_UPDATE);
2680
+ for (const validatorChunk of validatorChunks) {
2681
+ const validatorAndTransientStakePairs = [];
2682
+ for (const validator of validatorChunk) {
2683
+ const validatorStake = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
2684
+ validatorAndTransientStakePairs.push(validatorStake);
2685
+ const transientStake = await findTransientStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress, validator.transientSeedSuffixStart);
2686
+ validatorAndTransientStakePairs.push(transientStake);
2687
+ }
2688
+ updateListInstructions.push(StakePoolInstruction.updateValidatorListBalance({
2689
+ programId: stakePoolProgramId,
2690
+ stakePool: stakePoolAddress,
2691
+ validatorList: stakePool.account.data.validatorList,
2692
+ reserveStake: stakePool.account.data.reserveStake,
2693
+ validatorAndTransientStakePairs,
2694
+ withdrawAuthority,
2695
+ startIndex,
2696
+ noMerge,
2697
+ }));
2698
+ startIndex += MAX_VALIDATORS_TO_UPDATE;
2699
+ }
2700
+ instructions.push(StakePoolInstruction.updateStakePoolBalance({
2701
+ programId: stakePoolProgramId,
2702
+ stakePool: stakePoolAddress,
2703
+ validatorList: stakePool.account.data.validatorList,
2704
+ reserveStake: stakePool.account.data.reserveStake,
2705
+ managerFeeAccount: stakePool.account.data.managerFeeAccount,
2706
+ poolMint: stakePool.account.data.poolMint,
2707
+ withdrawAuthority,
2708
+ }));
2709
+ instructions.push(StakePoolInstruction.cleanupRemovedValidatorEntries({
2710
+ programId: stakePoolProgramId,
2711
+ stakePool: stakePoolAddress,
2712
+ validatorList: stakePool.account.data.validatorList,
2713
+ }));
2714
+ return {
2715
+ updateListInstructions,
2716
+ finalInstructions: instructions,
2717
+ };
2718
+ }
2719
+ /**
2720
+ * Retrieves detailed information about the StakePool.
2721
+ */
2722
+ async function stakePoolInfo(connection, stakePoolAddress) {
2723
+ var _c, _d;
2724
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
2725
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2726
+ const reserveAccountStakeAddress = stakePool.account.data.reserveStake;
2727
+ const totalLamports = stakePool.account.data.totalLamports;
2728
+ const lastUpdateEpoch = stakePool.account.data.lastUpdateEpoch;
2729
+ const validatorList = await getValidatorListAccount(connection, stakePool.account.data.validatorList);
2730
+ const maxNumberOfValidators = validatorList.account.data.maxValidators;
2731
+ const currentNumberOfValidators = validatorList.account.data.validators.length;
2732
+ const epochInfo = await connection.getEpochInfo();
2733
+ const reserveStake = await connection.getAccountInfo(reserveAccountStakeAddress);
2734
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2735
+ const minimumReserveStakeBalance = await connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space);
2736
+ const stakeAccounts = await Promise.all(validatorList.account.data.validators.map(async (validator) => {
2737
+ const stakeAccountAddress = await findStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress);
2738
+ const transientStakeAccountAddress = await findTransientStakeProgramAddress(stakePoolProgramId, validator.voteAccountAddress, stakePoolAddress, validator.transientSeedSuffixStart);
2739
+ const updateRequired = !validator.lastUpdateEpoch.eqn(epochInfo.epoch);
2740
+ return {
2741
+ voteAccountAddress: validator.voteAccountAddress.toBase58(),
2742
+ stakeAccountAddress: stakeAccountAddress.toBase58(),
2743
+ validatorActiveStakeLamports: validator.activeStakeLamports.toString(),
2744
+ validatorLastUpdateEpoch: validator.lastUpdateEpoch.toString(),
2745
+ validatorLamports: validator.activeStakeLamports
2746
+ .add(validator.transientStakeLamports)
2747
+ .toString(),
2748
+ validatorTransientStakeAccountAddress: transientStakeAccountAddress.toBase58(),
2749
+ validatorTransientStakeLamports: validator.transientStakeLamports.toString(),
2750
+ updateRequired,
2751
+ };
2752
+ }));
2753
+ const totalPoolTokens = lamportsToSol(stakePool.account.data.poolTokenSupply);
2754
+ const updateRequired = !lastUpdateEpoch.eqn(epochInfo.epoch);
2755
+ return {
2756
+ address: stakePoolAddress.toBase58(),
2757
+ poolWithdrawAuthority: withdrawAuthority.toBase58(),
2758
+ manager: stakePool.account.data.manager.toBase58(),
2759
+ staker: stakePool.account.data.staker.toBase58(),
2760
+ stakeDepositAuthority: stakePool.account.data.stakeDepositAuthority.toBase58(),
2761
+ stakeWithdrawBumpSeed: stakePool.account.data.stakeWithdrawBumpSeed,
2762
+ maxValidators: maxNumberOfValidators,
2763
+ validatorList: validatorList.account.data.validators.map((validator) => {
2764
+ return {
2765
+ activeStakeLamports: validator.activeStakeLamports.toString(),
2766
+ transientStakeLamports: validator.transientStakeLamports.toString(),
2767
+ lastUpdateEpoch: validator.lastUpdateEpoch.toString(),
2768
+ transientSeedSuffixStart: validator.transientSeedSuffixStart.toString(),
2769
+ transientSeedSuffixEnd: validator.transientSeedSuffixEnd.toString(),
2770
+ status: validator.status.toString(),
2771
+ voteAccountAddress: validator.voteAccountAddress.toString(),
2772
+ };
2773
+ }), // CliStakePoolValidator
2774
+ validatorListStorageAccount: stakePool.account.data.validatorList.toBase58(),
2775
+ reserveStake: stakePool.account.data.reserveStake.toBase58(),
2776
+ poolMint: stakePool.account.data.poolMint.toBase58(),
2777
+ managerFeeAccount: stakePool.account.data.managerFeeAccount.toBase58(),
2778
+ tokenProgramId: stakePool.account.data.tokenProgramId.toBase58(),
2779
+ totalLamports: stakePool.account.data.totalLamports.toString(),
2780
+ poolTokenSupply: stakePool.account.data.poolTokenSupply.toString(),
2781
+ lastUpdateEpoch: stakePool.account.data.lastUpdateEpoch.toString(),
2782
+ lockup: stakePool.account.data.lockup, // pub lockup: CliStakePoolLockup
2783
+ epochFee: stakePool.account.data.epochFee,
2784
+ nextEpochFee: stakePool.account.data.nextEpochFee,
2785
+ preferredDepositValidatorVoteAddress: stakePool.account.data.preferredDepositValidatorVoteAddress,
2786
+ preferredWithdrawValidatorVoteAddress: stakePool.account.data.preferredWithdrawValidatorVoteAddress,
2787
+ stakeDepositFee: stakePool.account.data.stakeDepositFee,
2788
+ stakeWithdrawalFee: stakePool.account.data.stakeWithdrawalFee,
2789
+ // CliStakePool the same
2790
+ nextStakeWithdrawalFee: stakePool.account.data.nextStakeWithdrawalFee,
2791
+ stakeReferralFee: stakePool.account.data.stakeReferralFee,
2792
+ solDepositAuthority: (_c = stakePool.account.data.solDepositAuthority) === null || _c === void 0 ? void 0 : _c.toBase58(),
2793
+ solDepositFee: stakePool.account.data.solDepositFee,
2794
+ solReferralFee: stakePool.account.data.solReferralFee,
2795
+ solWithdrawAuthority: (_d = stakePool.account.data.solWithdrawAuthority) === null || _d === void 0 ? void 0 : _d.toBase58(),
2796
+ solWithdrawalFee: stakePool.account.data.solWithdrawalFee,
2797
+ nextSolWithdrawalFee: stakePool.account.data.nextSolWithdrawalFee,
2798
+ lastEpochPoolTokenSupply: stakePool.account.data.lastEpochPoolTokenSupply.toString(),
2799
+ lastEpochTotalLamports: stakePool.account.data.lastEpochTotalLamports.toString(),
2800
+ details: {
2801
+ reserveStakeLamports: reserveStake === null || reserveStake === void 0 ? void 0 : reserveStake.lamports,
2802
+ reserveAccountStakeAddress: reserveAccountStakeAddress.toBase58(),
2803
+ minimumReserveStakeBalance,
2804
+ stakeAccounts,
2805
+ totalLamports,
2806
+ totalPoolTokens,
2807
+ currentNumberOfValidators,
2808
+ maxNumberOfValidators,
2809
+ updateRequired,
2810
+ }, // CliStakePoolDetails
2811
+ };
2812
+ }
2813
+ /**
2814
+ * Creates instructions required to create pool token metadata.
2815
+ */
2816
+ async function createPoolTokenMetadata(connection, stakePoolAddress, payer, name, symbol, uri) {
2817
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
2818
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2819
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2820
+ const tokenMetadata = findMetadataAddress(stakePool.account.data.poolMint);
2821
+ const manager = stakePool.account.data.manager;
2822
+ const instructions = [];
2823
+ instructions.push(StakePoolInstruction.createTokenMetadata({
2824
+ programId: stakePoolProgramId,
2825
+ stakePool: stakePoolAddress,
2826
+ poolMint: stakePool.account.data.poolMint,
2827
+ payer,
2828
+ manager,
2829
+ tokenMetadata,
2830
+ withdrawAuthority,
2831
+ name,
2832
+ symbol,
2833
+ uri,
2834
+ }));
2835
+ return {
2836
+ instructions,
2837
+ };
2838
+ }
2839
+ /**
2840
+ * Creates instructions required to update pool token metadata.
2841
+ */
2842
+ async function updatePoolTokenMetadata(connection, stakePoolAddress, name, symbol, uri) {
2843
+ const stakePool = await getStakePoolAccount(connection, stakePoolAddress);
2844
+ const stakePoolProgramId = getStakePoolProgramId(connection.rpcEndpoint);
2845
+ const withdrawAuthority = await findWithdrawAuthorityProgramAddress(stakePoolProgramId, stakePoolAddress);
2846
+ const tokenMetadata = findMetadataAddress(stakePool.account.data.poolMint);
2847
+ const instructions = [];
2848
+ instructions.push(StakePoolInstruction.updateTokenMetadata({
2849
+ programId: stakePoolProgramId,
2850
+ stakePool: stakePoolAddress,
2851
+ manager: stakePool.account.data.manager,
2852
+ tokenMetadata,
2853
+ withdrawAuthority,
2854
+ name,
2855
+ symbol,
2856
+ uri,
2857
+ }));
2858
+ return {
2859
+ instructions,
2860
+ };
2861
+ }
2862
+
2863
+ exports.DEVNET_STAKE_POOL_PROGRAM_ID = DEVNET_STAKE_POOL_PROGRAM_ID;
2864
+ exports.STAKE_POOL_INSTRUCTION_LAYOUTS = STAKE_POOL_INSTRUCTION_LAYOUTS;
2865
+ exports.STAKE_POOL_PROGRAM_ID = STAKE_POOL_PROGRAM_ID;
2866
+ exports.StakePoolInstruction = StakePoolInstruction;
2867
+ exports.StakePoolLayout = StakePoolLayout;
2868
+ exports.ValidatorListLayout = ValidatorListLayout;
2869
+ exports.ValidatorStakeInfoLayout = ValidatorStakeInfoLayout;
2870
+ exports.addValidatorToPool = addValidatorToPool;
2871
+ exports.createPoolTokenMetadata = createPoolTokenMetadata;
2872
+ exports.decreaseValidatorStake = decreaseValidatorStake;
2873
+ exports.depositSol = depositSol;
2874
+ exports.depositStake = depositStake;
2875
+ exports.depositWsolWithSession = depositWsolWithSession;
2876
+ exports.findEphemeralStakeProgramAddress = findEphemeralStakeProgramAddress;
2877
+ exports.findNextUserStakeSeed = findNextUserStakeSeed;
2878
+ exports.findStakeProgramAddress = findStakeProgramAddress;
2879
+ exports.findTransientStakeProgramAddress = findTransientStakeProgramAddress;
2880
+ exports.findUserStakeProgramAddress = findUserStakeProgramAddress;
2881
+ exports.findWithdrawAuthorityProgramAddress = findWithdrawAuthorityProgramAddress;
2882
+ exports.findWsolTransientProgramAddress = findWsolTransientProgramAddress;
2883
+ exports.getStakeAccount = getStakeAccount;
2884
+ exports.getStakePoolAccount = getStakePoolAccount;
2885
+ exports.getStakePoolAccounts = getStakePoolAccounts;
2886
+ exports.getStakePoolProgramId = getStakePoolProgramId;
2887
+ exports.getUserStakeAccounts = getUserStakeAccounts;
2888
+ exports.increaseValidatorStake = increaseValidatorStake;
2889
+ exports.removeValidatorFromPool = removeValidatorFromPool;
2890
+ exports.stakePoolInfo = stakePoolInfo;
2891
+ exports.tokenMetadataLayout = tokenMetadataLayout;
2892
+ exports.updatePoolTokenMetadata = updatePoolTokenMetadata;
2893
+ exports.updateStakePool = updateStakePool;
2894
+ exports.withdrawSol = withdrawSol;
2895
+ exports.withdrawStake = withdrawStake;
2896
+ exports.withdrawStakeWithSession = withdrawStakeWithSession;
2897
+ exports.withdrawWsolWithSession = withdrawWsolWithSession;
2898
+ //# sourceMappingURL=index.cjs.js.map