@ignitionfi/fogo-stake-pool 1.0.2 → 1.1.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.
@@ -10,6 +10,8 @@ import {
10
10
  SYSVAR_STAKE_HISTORY_PUBKEY,
11
11
  TransactionInstruction,
12
12
  } from '@solana/web3.js'
13
+ import BN from 'bn.js'
14
+ import { u64Instruction as u64 } from './codecs'
13
15
  import {
14
16
  DEVNET_STAKE_POOL_PROGRAM_ID,
15
17
  METADATA_MAX_NAME_LENGTH,
@@ -20,6 +22,58 @@ import {
20
22
  } from './constants'
21
23
  import { decodeData, encodeData, InstructionType } from './utils'
22
24
 
25
+ /**
26
+ * Type for amounts that can be converted to BN.
27
+ * Accepts number, bigint, BN, or string representations.
28
+ */
29
+ export type AmountInput = number | bigint | BN | string
30
+
31
+ /**
32
+ * Converts various numeric types to BN for safe large number handling.
33
+ * @internal
34
+ */
35
+ function toBN(value: AmountInput): BN {
36
+ if (BN.isBN(value)) {
37
+ return value
38
+ }
39
+
40
+ if (typeof value === 'bigint') {
41
+ return new BN(value.toString())
42
+ }
43
+
44
+ if (typeof value === 'string') {
45
+ // Validate string is a valid non-negative integer
46
+ const trimmed = value.trim()
47
+ if (!/^\d+$/.test(trimmed)) {
48
+ throw new Error(`Invalid amount string: "${value}". Must be a non-negative integer.`)
49
+ }
50
+ return new BN(trimmed)
51
+ }
52
+
53
+ if (typeof value === 'number') {
54
+ if (!Number.isFinite(value)) {
55
+ throw new Error('Invalid amount: must be a finite number')
56
+ }
57
+ if (value < 0) {
58
+ throw new Error('Invalid amount: must be non-negative')
59
+ }
60
+ if (!Number.isInteger(value)) {
61
+ throw new Error('Invalid amount: must be an integer (lamports)')
62
+ }
63
+ // CRITICAL: Numbers > MAX_SAFE_INTEGER have already lost precision
64
+ // We throw an error instead of silently corrupting data
65
+ if (value > Number.MAX_SAFE_INTEGER) {
66
+ throw new Error(
67
+ `Amount ${value} exceeds Number.MAX_SAFE_INTEGER (9,007,199,254,740,991). `
68
+ + `Use BigInt or BN for large values to avoid precision loss.`,
69
+ )
70
+ }
71
+ return new BN(value)
72
+ }
73
+
74
+ throw new Error(`Invalid amount type: ${typeof value}`)
75
+ }
76
+
23
77
  /**
24
78
  * An enumeration of valid StakePoolInstructionType's
25
79
  */
@@ -48,8 +102,8 @@ export type StakePoolInstructionType
48
102
 
49
103
  const MOVE_STAKE_LAYOUT = BufferLayout.struct<any>([
50
104
  BufferLayout.u8('instruction'),
51
- BufferLayout.ns64('lamports'),
52
- BufferLayout.ns64('transientStakeSeed'),
105
+ u64('lamports'),
106
+ u64('transientStakeSeed'),
53
107
  ])
54
108
 
55
109
  const UPDATE_VALIDATOR_LIST_BALANCE_LAYOUT = BufferLayout.struct<any>([
@@ -137,7 +191,7 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
137
191
  index: 10,
138
192
  layout: BufferLayout.struct<any>([
139
193
  BufferLayout.u8('instruction'),
140
- BufferLayout.ns64('poolTokens'),
194
+ u64('poolTokens'),
141
195
  ]),
142
196
  },
143
197
  /// Deposit SOL directly into the pool's reserve account. The output is a "pool" token
@@ -146,7 +200,7 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
146
200
  index: 14,
147
201
  layout: BufferLayout.struct<any>([
148
202
  BufferLayout.u8('instruction'),
149
- BufferLayout.ns64('lamports'),
203
+ u64('lamports'),
150
204
  ]),
151
205
  },
152
206
  /// Withdraw SOL directly from the pool's reserve account. Fails if the
@@ -155,25 +209,25 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
155
209
  index: 16,
156
210
  layout: BufferLayout.struct<any>([
157
211
  BufferLayout.u8('instruction'),
158
- BufferLayout.ns64('poolTokens'),
212
+ u64('poolTokens'),
159
213
  ]),
160
214
  },
161
215
  IncreaseAdditionalValidatorStake: {
162
216
  index: 19,
163
217
  layout: BufferLayout.struct<any>([
164
218
  BufferLayout.u8('instruction'),
165
- BufferLayout.ns64('lamports'),
166
- BufferLayout.ns64('transientStakeSeed'),
167
- BufferLayout.ns64('ephemeralStakeSeed'),
219
+ u64('lamports'),
220
+ u64('transientStakeSeed'),
221
+ u64('ephemeralStakeSeed'),
168
222
  ]),
169
223
  },
170
224
  DecreaseAdditionalValidatorStake: {
171
225
  index: 20,
172
226
  layout: BufferLayout.struct<any>([
173
227
  BufferLayout.u8('instruction'),
174
- BufferLayout.ns64('lamports'),
175
- BufferLayout.ns64('transientStakeSeed'),
176
- BufferLayout.ns64('ephemeralStakeSeed'),
228
+ u64('lamports'),
229
+ u64('transientStakeSeed'),
230
+ u64('ephemeralStakeSeed'),
177
231
  ]),
178
232
  },
179
233
  DecreaseValidatorStakeWithReserve: {
@@ -188,62 +242,62 @@ export const STAKE_POOL_INSTRUCTION_LAYOUTS: {
188
242
  index: 23,
189
243
  layout: BufferLayout.struct<any>([
190
244
  BufferLayout.u8('instruction'),
191
- BufferLayout.ns64('lamports'),
245
+ u64('lamports'),
192
246
  ]),
193
247
  },
194
248
  WithdrawStakeWithSlippage: {
195
249
  index: 24,
196
250
  layout: BufferLayout.struct<any>([
197
251
  BufferLayout.u8('instruction'),
198
- BufferLayout.ns64('poolTokensIn'),
199
- BufferLayout.ns64('minimumLamportsOut'),
252
+ u64('poolTokensIn'),
253
+ u64('minimumLamportsOut'),
200
254
  ]),
201
255
  },
202
256
  DepositSolWithSlippage: {
203
257
  index: 25,
204
258
  layout: BufferLayout.struct<any>([
205
259
  BufferLayout.u8('instruction'),
206
- BufferLayout.ns64('lamports'),
260
+ u64('lamports'),
207
261
  ]),
208
262
  },
209
263
  WithdrawSolWithSlippage: {
210
264
  index: 26,
211
265
  layout: BufferLayout.struct<any>([
212
266
  BufferLayout.u8('instruction'),
213
- BufferLayout.ns64('lamports'),
267
+ u64('lamports'),
214
268
  ]),
215
269
  },
216
270
  DepositWsolWithSession: {
217
271
  index: 27,
218
272
  layout: BufferLayout.struct<any>([
219
273
  BufferLayout.u8('instruction'),
220
- BufferLayout.ns64('lamportsIn'),
221
- BufferLayout.ns64('minimumPoolTokensOut'),
274
+ u64('lamportsIn'),
275
+ u64('minimumPoolTokensOut'),
222
276
  ]),
223
277
  },
224
278
  WithdrawWsolWithSession: {
225
279
  index: 28,
226
280
  layout: BufferLayout.struct<any>([
227
281
  BufferLayout.u8('instruction'),
228
- BufferLayout.ns64('poolTokensIn'),
229
- BufferLayout.ns64('minimumLamportsOut'),
282
+ u64('poolTokensIn'),
283
+ u64('minimumLamportsOut'),
230
284
  ]),
231
285
  },
232
286
  WithdrawStakeWithSession: {
233
287
  index: 29,
234
288
  layout: BufferLayout.struct<any>([
235
289
  BufferLayout.u8('instruction'),
236
- BufferLayout.ns64('poolTokensIn'),
237
- BufferLayout.ns64('minimumLamportsOut'),
238
- BufferLayout.ns64('userStakeSeed'),
290
+ u64('poolTokensIn'),
291
+ u64('minimumLamportsOut'),
292
+ u64('userStakeSeed'),
239
293
  ]),
240
294
  },
241
295
  WithdrawFromStakeAccountWithSession: {
242
296
  index: 30,
243
297
  layout: BufferLayout.struct<any>([
244
298
  BufferLayout.u8('instruction'),
245
- BufferLayout.ns64('lamports'),
246
- BufferLayout.ns64('userStakeSeed'),
299
+ u64('lamports'),
300
+ u64('userStakeSeed'),
247
301
  ]),
248
302
  },
249
303
  })
@@ -296,9 +350,9 @@ export type DecreaseValidatorStakeParams = {
296
350
  validatorStake: PublicKey
297
351
  transientStake: PublicKey
298
352
  // Amount of lamports to split into the transient stake account
299
- lamports: number
353
+ lamports: AmountInput
300
354
  // Seed to used to create the transient stake account
301
- transientStakeSeed: number
355
+ transientStakeSeed: AmountInput
302
356
  }
303
357
 
304
358
  export interface DecreaseValidatorStakeWithReserveParams extends DecreaseValidatorStakeParams {
@@ -308,7 +362,7 @@ export interface DecreaseValidatorStakeWithReserveParams extends DecreaseValidat
308
362
  export interface DecreaseAdditionalValidatorStakeParams extends DecreaseValidatorStakeParams {
309
363
  reserveStake: PublicKey
310
364
  ephemeralStake: PublicKey
311
- ephemeralStakeSeed: number
365
+ ephemeralStakeSeed: AmountInput
312
366
  }
313
367
 
314
368
  /**
@@ -325,14 +379,14 @@ export type IncreaseValidatorStakeParams = {
325
379
  validatorStake: PublicKey
326
380
  validatorVote: PublicKey
327
381
  // Amount of lamports to split into the transient stake account
328
- lamports: number
382
+ lamports: AmountInput
329
383
  // Seed to used to create the transient stake account
330
- transientStakeSeed: number
384
+ transientStakeSeed: AmountInput
331
385
  }
332
386
 
333
387
  export interface IncreaseAdditionalValidatorStakeParams extends IncreaseValidatorStakeParams {
334
388
  ephemeralStake: PublicKey
335
- ephemeralStakeSeed: number
389
+ ephemeralStakeSeed: AmountInput
336
390
  }
337
391
 
338
392
  /**
@@ -368,7 +422,7 @@ export type WithdrawStakeParams = {
368
422
  sourcePoolAccount: PublicKey
369
423
  managerFeeAccount: PublicKey
370
424
  poolMint: PublicKey
371
- poolTokens: number
425
+ poolTokens: AmountInput
372
426
  }
373
427
 
374
428
  /**
@@ -385,7 +439,7 @@ export type WithdrawSolParams = {
385
439
  solWithdrawAuthority?: PublicKey | undefined
386
440
  managerFeeAccount: PublicKey
387
441
  poolMint: PublicKey
388
- poolTokens: number
442
+ poolTokens: AmountInput
389
443
  }
390
444
 
391
445
  /**
@@ -405,8 +459,8 @@ export type WithdrawWsolWithSessionParams = {
405
459
  wsolMint: PublicKey
406
460
  programSigner: PublicKey
407
461
  userWallet: PublicKey
408
- poolTokensIn: number
409
- minimumLamportsOut: number
462
+ poolTokensIn: AmountInput
463
+ minimumLamportsOut: AmountInput
410
464
  solWithdrawAuthority?: PublicKey
411
465
  }
412
466
 
@@ -426,12 +480,12 @@ export type WithdrawStakeWithSessionParams = {
426
480
  tokenProgramId: PublicKey
427
481
  /** The program signer PDA derived from PROGRAM_SIGNER_SEED */
428
482
  programSigner: PublicKey
429
- /** The payer for stake account rent (typically the paymaster) */
430
- payer: PublicKey
431
- poolTokensIn: number
432
- minimumLamportsOut: number
483
+ /** Reserve stake account for rent funding */
484
+ reserveStake: PublicKey
485
+ poolTokensIn: AmountInput
486
+ minimumLamportsOut: AmountInput
433
487
  /** Seed used to derive the user stake PDA */
434
- userStakeSeed: number
488
+ userStakeSeed: AmountInput
435
489
  }
436
490
 
437
491
  export type WithdrawFromStakeAccountWithSessionParams = {
@@ -443,9 +497,9 @@ export type WithdrawFromStakeAccountWithSessionParams = {
443
497
  /** The session signer (user or session) */
444
498
  sessionSigner: PublicKey
445
499
  /** Seed used to derive the user stake PDA */
446
- userStakeSeed: number
447
- /** Lamports to withdraw (use Number.MAX_SAFE_INTEGER for full withdrawal) */
448
- lamports: number
500
+ userStakeSeed: AmountInput
501
+ /** Lamports to withdraw (use BigInt(Number.MAX_SAFE_INTEGER) for full withdrawal) */
502
+ lamports: AmountInput
449
503
  }
450
504
 
451
505
  /**
@@ -463,7 +517,7 @@ export type DepositSolParams = {
463
517
  managerFeeAccount: PublicKey
464
518
  referralPoolAccount: PublicKey
465
519
  poolMint: PublicKey
466
- lamports: number
520
+ lamports: AmountInput
467
521
  }
468
522
 
469
523
  export type CreateTokenMetadataParams = {
@@ -710,7 +764,10 @@ export class StakePoolInstruction {
710
764
  } = params
711
765
 
712
766
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.IncreaseValidatorStake
713
- const data = encodeData(type, { lamports, transientStakeSeed })
767
+ const data = encodeData(type, {
768
+ lamports: toBN(lamports),
769
+ transientStakeSeed: toBN(transientStakeSeed),
770
+ })
714
771
 
715
772
  const keys = [
716
773
  { pubkey: stakePool, isSigner: false, isWritable: false },
@@ -760,7 +817,11 @@ export class StakePoolInstruction {
760
817
  } = params
761
818
 
762
819
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.IncreaseAdditionalValidatorStake
763
- const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed })
820
+ const data = encodeData(type, {
821
+ lamports: toBN(lamports),
822
+ transientStakeSeed: toBN(transientStakeSeed),
823
+ ephemeralStakeSeed: toBN(ephemeralStakeSeed),
824
+ })
764
825
 
765
826
  const keys = [
766
827
  { pubkey: stakePool, isSigner: false, isWritable: false },
@@ -804,7 +865,10 @@ export class StakePoolInstruction {
804
865
  } = params
805
866
 
806
867
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseValidatorStake
807
- const data = encodeData(type, { lamports, transientStakeSeed })
868
+ const data = encodeData(type, {
869
+ lamports: toBN(lamports),
870
+ transientStakeSeed: toBN(transientStakeSeed),
871
+ })
808
872
 
809
873
  const keys = [
810
874
  { pubkey: stakePool, isSigner: false, isWritable: false },
@@ -847,7 +911,10 @@ export class StakePoolInstruction {
847
911
  } = params
848
912
 
849
913
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseValidatorStakeWithReserve
850
- const data = encodeData(type, { lamports, transientStakeSeed })
914
+ const data = encodeData(type, {
915
+ lamports: toBN(lamports),
916
+ transientStakeSeed: toBN(transientStakeSeed),
917
+ })
851
918
 
852
919
  const keys = [
853
920
  { pubkey: stakePool, isSigner: false, isWritable: false },
@@ -893,7 +960,11 @@ export class StakePoolInstruction {
893
960
  } = params
894
961
 
895
962
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DecreaseAdditionalValidatorStake
896
- const data = encodeData(type, { lamports, transientStakeSeed, ephemeralStakeSeed })
963
+ const data = encodeData(type, {
964
+ lamports: toBN(lamports),
965
+ transientStakeSeed: toBN(transientStakeSeed),
966
+ ephemeralStakeSeed: toBN(ephemeralStakeSeed),
967
+ })
897
968
 
898
969
  const keys = [
899
970
  { pubkey: stakePool, isSigner: false, isWritable: false },
@@ -983,7 +1054,7 @@ export class StakePoolInstruction {
983
1054
  } = params
984
1055
 
985
1056
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DepositSol
986
- const data = encodeData(type, { lamports })
1057
+ const data = encodeData(type, { lamports: toBN(lamports) })
987
1058
 
988
1059
  const keys = [
989
1060
  { pubkey: stakePool, isSigner: false, isWritable: true },
@@ -1024,14 +1095,14 @@ export class StakePoolInstruction {
1024
1095
  tokenProgramId: PublicKey
1025
1096
  programId: PublicKey
1026
1097
  userWallet: PublicKey
1027
- lamportsIn: number
1028
- minimumPoolTokensOut: number
1098
+ lamportsIn: AmountInput
1099
+ minimumPoolTokensOut: AmountInput
1029
1100
  payer?: PublicKey
1030
1101
  }): TransactionInstruction {
1031
1102
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.DepositWsolWithSession
1032
1103
  const data = encodeData(type, {
1033
- lamportsIn: params.lamportsIn,
1034
- minimumPoolTokensOut: params.minimumPoolTokensOut,
1104
+ lamportsIn: toBN(params.lamportsIn),
1105
+ minimumPoolTokensOut: toBN(params.minimumPoolTokensOut),
1035
1106
  })
1036
1107
 
1037
1108
  const keys = [
@@ -1093,7 +1164,7 @@ export class StakePoolInstruction {
1093
1164
  } = params
1094
1165
 
1095
1166
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStake
1096
- const data = encodeData(type, { poolTokens })
1167
+ const data = encodeData(type, { poolTokens: toBN(poolTokens) })
1097
1168
 
1098
1169
  const keys = [
1099
1170
  { pubkey: stakePool, isSigner: false, isWritable: true },
@@ -1137,7 +1208,7 @@ export class StakePoolInstruction {
1137
1208
  } = params
1138
1209
 
1139
1210
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawSol
1140
- const data = encodeData(type, { poolTokens })
1211
+ const data = encodeData(type, { poolTokens: toBN(poolTokens) })
1141
1212
 
1142
1213
  const keys = [
1143
1214
  { pubkey: stakePool, isSigner: false, isWritable: true },
@@ -1178,8 +1249,8 @@ export class StakePoolInstruction {
1178
1249
  ): TransactionInstruction {
1179
1250
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawWsolWithSession
1180
1251
  const data = encodeData(type, {
1181
- poolTokensIn: params.poolTokensIn,
1182
- minimumLamportsOut: params.minimumLamportsOut,
1252
+ poolTokensIn: toBN(params.poolTokensIn),
1253
+ minimumLamportsOut: toBN(params.minimumLamportsOut),
1183
1254
  })
1184
1255
 
1185
1256
  const keys = [
@@ -1222,14 +1293,14 @@ export class StakePoolInstruction {
1222
1293
 
1223
1294
  /**
1224
1295
  * Creates a transaction instruction to withdraw stake from a stake pool using a Fogo session.
1225
- * The stake account is created as a PDA and rent is paid by the payer (typically paymaster).
1296
+ * The stake account is created as a PDA and rent is funded from the reserve.
1226
1297
  */
1227
1298
  static withdrawStakeWithSession(params: WithdrawStakeWithSessionParams): TransactionInstruction {
1228
1299
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawStakeWithSession
1229
1300
  const data = encodeData(type, {
1230
- poolTokensIn: params.poolTokensIn,
1231
- minimumLamportsOut: params.minimumLamportsOut,
1232
- userStakeSeed: params.userStakeSeed,
1301
+ poolTokensIn: toBN(params.poolTokensIn),
1302
+ minimumLamportsOut: toBN(params.minimumLamportsOut),
1303
+ userStakeSeed: toBN(params.userStakeSeed),
1233
1304
  })
1234
1305
 
1235
1306
  const keys = [
@@ -1238,17 +1309,19 @@ export class StakePoolInstruction {
1238
1309
  { pubkey: params.withdrawAuthority, isSigner: false, isWritable: false },
1239
1310
  { pubkey: params.stakeToSplit, isSigner: false, isWritable: true },
1240
1311
  { pubkey: params.stakeToReceive, isSigner: false, isWritable: true },
1241
- { pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info (signer_or_session)
1242
- { pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (not used in session path)
1312
+ { pubkey: params.sessionSigner, isSigner: true, isWritable: false }, // user_stake_authority_info
1313
+ { pubkey: params.sessionSigner, isSigner: false, isWritable: false }, // user_transfer_authority_info (unused in session path)
1243
1314
  { pubkey: params.burnFromPool, isSigner: false, isWritable: true },
1244
1315
  { pubkey: params.managerFeeAccount, isSigner: false, isWritable: true },
1245
1316
  { pubkey: params.poolMint, isSigner: false, isWritable: true },
1246
1317
  { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
1247
1318
  { pubkey: params.tokenProgramId, isSigner: false, isWritable: false },
1248
1319
  { pubkey: StakeProgram.programId, isSigner: false, isWritable: false },
1320
+ // Session-specific accounts
1249
1321
  { pubkey: params.programSigner, isSigner: false, isWritable: false },
1250
1322
  { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
1251
- { pubkey: params.payer, isSigner: true, isWritable: true },
1323
+ { pubkey: params.reserveStake, isSigner: false, isWritable: true },
1324
+ { pubkey: SYSVAR_STAKE_HISTORY_PUBKEY, isSigner: false, isWritable: false },
1252
1325
  ]
1253
1326
 
1254
1327
  return new TransactionInstruction({
@@ -1265,8 +1338,8 @@ export class StakePoolInstruction {
1265
1338
  static withdrawFromStakeAccountWithSession(params: WithdrawFromStakeAccountWithSessionParams): TransactionInstruction {
1266
1339
  const type = STAKE_POOL_INSTRUCTION_LAYOUTS.WithdrawFromStakeAccountWithSession
1267
1340
  const data = encodeData(type, {
1268
- lamports: params.lamports,
1269
- userStakeSeed: params.userStakeSeed,
1341
+ lamports: toBN(params.lamports),
1342
+ userStakeSeed: toBN(params.userStakeSeed),
1270
1343
  })
1271
1344
 
1272
1345
  const keys = [