@ignitionfi/fogo-stake-pool 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ignitionfi/fogo-stake-pool",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Fogo Stake Pool SDK",
5
5
  "contributors": [
6
6
  "Anza Maintainers <maintainers@anza.xyz>",
@@ -45,8 +45,8 @@
45
45
  "superstruct": "^2.0.2"
46
46
  },
47
47
  "devDependencies": {
48
- "@rollup/plugin-alias": "^5.1.1",
49
- "@rollup/plugin-commonjs": "^28.0.1",
48
+ "@rollup/plugin-alias": "^6.0.0",
49
+ "@rollup/plugin-commonjs": "^29.0.0",
50
50
  "@rollup/plugin-json": "^6.1.0",
51
51
  "@rollup/plugin-multi-entry": "^6.0.0",
52
52
  "@rollup/plugin-node-resolve": "^16.0.0",
@@ -58,10 +58,10 @@
58
58
  "@types/node-fetch": "^2.6.12",
59
59
  "cross-env": "^10.0.0",
60
60
  "jest": "^30.0.3",
61
- "rimraf": "^6.0.1",
61
+ "rimraf": "^6.1.2",
62
62
  "rollup": "^4.28.0",
63
- "rollup-plugin-dts": "^6.1.1",
64
- "ts-jest": "^29.2.5"
63
+ "rollup-plugin-dts": "^6.3.0",
64
+ "ts-jest": "^29.4.6"
65
65
  },
66
66
  "jest": {
67
67
  "moduleFileExtensions": [
@@ -80,9 +80,9 @@
80
80
  "build": "tsc && cross-env NODE_ENV=production rollup -c",
81
81
  "dev": "tsc && cross-env NODE_ENV=production rollup -c -w",
82
82
  "build:program": "cargo build-sbf --manifest-path=../program/Cargo.toml",
83
- "format": "prettier --check src test",
84
- "format:fix": "prettier --write src test",
85
- "lint": "eslint --max-warnings 0 .",
83
+ "format": "eslint .",
84
+ "format:fix": "eslint . --fix",
85
+ "lint": "eslint .",
86
86
  "lint:fix": "eslint . --fix",
87
87
  "test": "jest",
88
88
  "clean": "rimraf ./dist"
package/src/constants.ts CHANGED
@@ -11,7 +11,7 @@ export const METADATA_MAX_URI_LENGTH = 200
11
11
  export const STAKE_POOL_PROGRAM_ID = new PublicKey('SP1s4uFeTAX9jsXXmwyDs1gxYYf7cdDZ8qHUHVxE1yr')
12
12
 
13
13
  // Public key that identifies the SPL Stake Pool program deployed to devnet.
14
- export const DEVNET_STAKE_POOL_PROGRAM_ID = new PublicKey('DPoo15wWDqpPJJtS2MUZ49aRxqz5ZaaJCJP4z8bLuib')
14
+ export const DEVNET_STAKE_POOL_PROGRAM_ID = STAKE_POOL_PROGRAM_ID
15
15
 
16
16
  // Maximum number of validators to update during UpdateValidatorListBalance.
17
17
  export const MAX_VALIDATORS_TO_UPDATE = 4
package/src/layouts.ts CHANGED
@@ -84,6 +84,7 @@ export function futureEpoch<T>(layout: Layout<T>, property?: string): LayoutCls<
84
84
  return new FutureEpochLayout<T>(layout, property)
85
85
  }
86
86
 
87
+ /* eslint-disable ts/no-redeclare -- Superstruct pattern: type + const with same name */
87
88
  export type StakeAccountType = Infer<typeof StakeAccountType>
88
89
  export const StakeAccountType = enums(['uninitialized', 'initialized', 'delegated', 'rewardsPool'])
89
90
 
@@ -123,6 +124,7 @@ export const StakeAccount = type({
123
124
  type: StakeAccountType,
124
125
  info: optional(StakeAccountInfo),
125
126
  })
127
+ /* eslint-enable ts/no-redeclare */
126
128
  export interface Lockup {
127
129
  unixTimestamp: BN
128
130
  epoch: BN
@@ -65,14 +65,13 @@ export async function prepareWithdrawAccounts(
65
65
  )
66
66
  const minBalance = new BN(minBalanceForRentExemption + MINIMUM_ACTIVE_STAKE)
67
67
 
68
- let accounts = [] as Array<{
69
- type: 'preferred' | 'active' | 'transient' | 'reserve'
70
- voteAddress?: PublicKey | undefined
68
+ // First, collect all stake account addresses we need to check
69
+ const accountsToFetch: Array<{
70
+ type: 'preferred' | 'active' | 'transient'
71
+ voteAddress: PublicKey
71
72
  stakeAddress: PublicKey
72
- lamports: BN
73
- }>
73
+ }> = []
74
74
 
75
- // Prepare accounts
76
75
  for (const validator of validatorList.validators) {
77
76
  if (validator.status !== ValidatorStakeInfoStatus.Active) {
78
77
  continue
@@ -84,34 +83,63 @@ export async function prepareWithdrawAccounts(
84
83
  stakePoolAddress,
85
84
  )
86
85
 
87
- // For active stake accounts, subtract the minimum balance that must remain
88
- // to allow for merges and maintain rent exemption
89
- const availableActiveLamports = validator.activeStakeLamports.sub(minBalance)
90
- if (availableActiveLamports.gt(new BN(0))) {
91
- const isPreferred = stakePool?.preferredWithdrawValidatorVoteAddress?.equals(
92
- validator.voteAccountAddress,
93
- )
94
- accounts.push({
86
+ const isPreferred = stakePool?.preferredWithdrawValidatorVoteAddress?.equals(
87
+ validator.voteAccountAddress,
88
+ )
89
+
90
+ // Add active stake account if validator list indicates it has stake
91
+ if (validator.activeStakeLamports.gt(new BN(0))) {
92
+ accountsToFetch.push({
95
93
  type: isPreferred ? 'preferred' : 'active',
96
94
  voteAddress: validator.voteAccountAddress,
97
95
  stakeAddress: stakeAccountAddress,
98
- lamports: availableActiveLamports,
99
96
  })
100
97
  }
101
98
 
102
- const transientStakeLamports = validator.transientStakeLamports.sub(minBalance)
103
- if (transientStakeLamports.gt(new BN(0))) {
99
+ // Add transient stake account if validator list indicates it has stake
100
+ if (validator.transientStakeLamports.gt(new BN(0))) {
104
101
  const transientStakeAccountAddress = await findTransientStakeProgramAddress(
105
102
  stakePoolProgramId,
106
103
  validator.voteAccountAddress,
107
104
  stakePoolAddress,
108
105
  validator.transientSeedSuffixStart,
109
106
  )
110
- accounts.push({
107
+ accountsToFetch.push({
111
108
  type: 'transient',
112
109
  voteAddress: validator.voteAccountAddress,
113
110
  stakeAddress: transientStakeAccountAddress,
114
- lamports: transientStakeLamports,
111
+ })
112
+ }
113
+ }
114
+
115
+ // Fetch all stake accounts + reserve in one batch call
116
+ const addressesToFetch = [
117
+ ...accountsToFetch.map(a => a.stakeAddress),
118
+ stakePool.reserveStake,
119
+ ]
120
+ const accountInfos = await connection.getMultipleAccountsInfo(addressesToFetch)
121
+
122
+ // Build accounts list using actual on-chain balances
123
+ let accounts: ValidatorAccount[] = []
124
+
125
+ for (let i = 0; i < accountsToFetch.length; i++) {
126
+ const { type, voteAddress, stakeAddress } = accountsToFetch[i]
127
+ const accountInfo = accountInfos[i]
128
+
129
+ if (!accountInfo) {
130
+ continue
131
+ }
132
+
133
+ // Use actual on-chain balance instead of validator list value
134
+ const actualLamports = new BN(accountInfo.lamports)
135
+ const availableLamports = actualLamports.sub(minBalance)
136
+
137
+ if (availableLamports.gt(new BN(0))) {
138
+ accounts.push({
139
+ type,
140
+ voteAddress,
141
+ stakeAddress,
142
+ lamports: availableLamports,
115
143
  })
116
144
  }
117
145
  }
@@ -119,8 +147,9 @@ export async function prepareWithdrawAccounts(
119
147
  // Sort from highest to lowest balance
120
148
  accounts = accounts.sort(compareFn || ((a, b) => b.lamports.sub(a.lamports).toNumber()))
121
149
 
122
- const reserveStake = await connection.getAccountInfo(stakePool.reserveStake)
123
- const reserveStakeBalance = new BN((reserveStake?.lamports ?? 0) - minBalanceForRentExemption)
150
+ // Add reserve stake using actual balance (last item in batch fetch)
151
+ const reserveAccountInfo = accountInfos[accountInfos.length - 1]
152
+ const reserveStakeBalance = new BN((reserveAccountInfo?.lamports ?? 0) - minBalanceForRentExemption)
124
153
  if (reserveStakeBalance.gt(new BN(0))) {
125
154
  accounts.push({
126
155
  type: 'reserve',