Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit e3d5a82

Browse files
authored
stake-pool-js: Add deserializer for FutureEpoch (#6745)
* stake-pool-js: Add deserializer for `FutureEpoch` * Fixup for new types
1 parent 2b564b0 commit e3d5a82

File tree

5 files changed

+108
-46
lines changed

5 files changed

+108
-46
lines changed

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

stake-pool/js/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@solana/web3.js": "^1.91.8",
5050
"bn.js": "^5.2.0",
5151
"buffer": "^6.0.3",
52+
"buffer-layout": "^1.2.2",
5253
"superstruct": "^1.0.4"
5354
},
5455
"devDependencies": {

stake-pool/js/src/index.ts

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -139,43 +139,46 @@ export async function getStakeAccount(
139139
export async function getStakePoolAccounts(
140140
connection: Connection,
141141
stakePoolProgramAddress: PublicKey,
142-
): Promise<(StakePoolAccount | ValidatorListAccount)[] | undefined> {
142+
): Promise<(StakePoolAccount | ValidatorListAccount | undefined)[] | undefined> {
143143
const response = await connection.getProgramAccounts(stakePoolProgramAddress);
144144

145-
return response.map((a) => {
146-
let decodedData;
147-
148-
if (a.account.data.readUInt8() === 1) {
145+
return response
146+
.map((a) => {
149147
try {
150-
decodedData = StakePoolLayout.decode(a.account.data);
148+
if (a.account.data.readUInt8() === 1) {
149+
const data = StakePoolLayout.decode(a.account.data);
150+
return {
151+
pubkey: a.pubkey,
152+
account: {
153+
data,
154+
executable: a.account.executable,
155+
lamports: a.account.lamports,
156+
owner: a.account.owner,
157+
},
158+
};
159+
} else if (a.account.data.readUInt8() === 2) {
160+
const data = ValidatorListLayout.decode(a.account.data);
161+
return {
162+
pubkey: a.pubkey,
163+
account: {
164+
data,
165+
executable: a.account.executable,
166+
lamports: a.account.lamports,
167+
owner: a.account.owner,
168+
},
169+
};
170+
} else {
171+
console.error(
172+
`Could not decode. StakePoolAccount Enum is ${a.account.data.readUInt8()}, expected 1 or 2!`,
173+
);
174+
return undefined;
175+
}
151176
} catch (error) {
152-
console.log('Could not decode StakeAccount. Error:', error);
153-
decodedData = undefined;
177+
console.error('Could not decode account. Error:', error);
178+
return undefined;
154179
}
155-
} else if (a.account.data.readUInt8() === 2) {
156-
try {
157-
decodedData = ValidatorListLayout.decode(a.account.data);
158-
} catch (error) {
159-
console.log('Could not decode ValidatorList. Error:', error);
160-
decodedData = undefined;
161-
}
162-
} else {
163-
console.error(
164-
`Could not decode. StakePoolAccount Enum is ${a.account.data.readUInt8()}, expected 1 or 2!`,
165-
);
166-
decodedData = undefined;
167-
}
168-
169-
return {
170-
pubkey: a.pubkey,
171-
account: {
172-
data: decodedData,
173-
executable: a.account.executable,
174-
lamports: a.account.lamports,
175-
owner: a.account.owner,
176-
},
177-
};
178-
});
180+
})
181+
.filter((a) => a !== undefined);
179182
}
180183

181184
/**

stake-pool/js/src/layouts.ts

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { publicKey, struct, u32, u64, u8, option, vec } from '@coral-xyz/borsh';
1+
import { Layout, publicKey, struct, u32, u64, u8, option, vec } from '@coral-xyz/borsh';
2+
import { Layout as LayoutCls, u8 as u8Cls } from 'buffer-layout';
23
import { PublicKey } from '@solana/web3.js';
34
import BN from 'bn.js';
45
import {
@@ -37,6 +38,50 @@ export const PublicKeyFromString = coerce(
3738
(value) => new PublicKey(value),
3839
);
3940

41+
export class FutureEpochLayout<T> extends LayoutCls<T | null> {
42+
layout: Layout<T>;
43+
discriminator: Layout<number>;
44+
45+
constructor(layout: Layout<T>, property?: string) {
46+
super(-1, property);
47+
this.layout = layout;
48+
this.discriminator = u8Cls();
49+
}
50+
51+
encode(src: T | null, b: Buffer, offset = 0): number {
52+
if (src === null || src === undefined) {
53+
return this.discriminator.encode(0, b, offset);
54+
}
55+
// This isn't right, but we don't typically encode outside of tests
56+
this.discriminator.encode(2, b, offset);
57+
return this.layout.encode(src, b, offset + 1) + 1;
58+
}
59+
60+
decode(b: Buffer, offset = 0): T | null {
61+
const discriminator = this.discriminator.decode(b, offset);
62+
if (discriminator === 0) {
63+
return null;
64+
} else if (discriminator === 1 || discriminator === 2) {
65+
return this.layout.decode(b, offset + 1);
66+
}
67+
throw new Error('Invalid future epoch ' + this.property);
68+
}
69+
70+
getSpan(b: Buffer, offset = 0): number {
71+
const discriminator = this.discriminator.decode(b, offset);
72+
if (discriminator === 0) {
73+
return 1;
74+
} else if (discriminator === 1 || discriminator === 2) {
75+
return this.layout.getSpan(b, offset + 1) + 1;
76+
}
77+
throw new Error('Invalid future epoch ' + this.property);
78+
}
79+
}
80+
81+
export function futureEpoch<T>(layout: Layout<T>, property?: string): Layout<T | null> {
82+
return new FutureEpochLayout<T>(layout, property);
83+
}
84+
4085
export type StakeAccountType = Infer<typeof StakeAccountType>;
4186
export const StakeAccountType = enums(['uninitialized', 'initialized', 'delegated', 'rewardsPool']);
4287

@@ -131,19 +176,19 @@ export const StakePoolLayout = struct<StakePool>([
131176
u64('lastUpdateEpoch'),
132177
struct([u64('unixTimestamp'), u64('epoch'), publicKey('custodian')], 'lockup'),
133178
struct(feeFields, 'epochFee'),
134-
option(struct(feeFields), 'nextEpochFee'),
179+
futureEpoch(struct(feeFields), 'nextEpochFee'),
135180
option(publicKey(), 'preferredDepositValidatorVoteAddress'),
136181
option(publicKey(), 'preferredWithdrawValidatorVoteAddress'),
137182
struct(feeFields, 'stakeDepositFee'),
138183
struct(feeFields, 'stakeWithdrawalFee'),
139-
option(struct(feeFields), 'nextStakeWithdrawalFee'),
184+
futureEpoch(struct(feeFields), 'nextStakeWithdrawalFee'),
140185
u8('stakeReferralFee'),
141186
option(publicKey(), 'solDepositAuthority'),
142187
struct(feeFields, 'solDepositFee'),
143188
u8('solReferralFee'),
144189
option(publicKey(), 'solWithdrawAuthority'),
145190
struct(feeFields, 'solWithdrawalFee'),
146-
option(struct(feeFields), 'nextSolWithdrawalFee'),
191+
futureEpoch(struct(feeFields), 'nextSolWithdrawalFee'),
147192
u64('lastEpochPoolTokenSupply'),
148193
u64('lastEpochTotalLamports'),
149194
]);
Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
declare module 'buffer-layout' {
2-
export class Layout {}
3-
export class UInt {}
4-
/* eslint-disable @typescript-eslint/no-unused-vars */
5-
export function struct<T>(fields: any, property?: string, decodePrefixes?: boolean): any;
6-
export function s32(property?: string): UInt;
7-
export function u32(property?: string): UInt;
8-
export function s16(property?: string): UInt;
9-
export function u16(property?: string): UInt;
10-
export function s8(property?: string): UInt;
11-
export function u8(property?: string): UInt;
2+
export class Layout<T = any> {
3+
span: number;
4+
property?: string;
5+
constructor(span: number, property?: string);
6+
decode(b: Buffer | undefined, offset?: number): T;
7+
encode(src: T, b: Buffer, offset?: number): number;
8+
getSpan(b: Buffer, offset?: number): number;
9+
replicate(name: string): this;
10+
}
11+
export function struct<T>(
12+
fields: Layout<any>[],
13+
property?: string,
14+
decodePrefixes?: boolean,
15+
): Layout<T>;
16+
export function s32(property?: string): Layout<number>;
17+
export function u32(property?: string): Layout<number>;
18+
export function s16(property?: string): Layout<number>;
19+
export function u16(property?: string): Layout<number>;
20+
export function s8(property?: string): Layout<number>;
21+
export function u8(property?: string): Layout<number>;
1222
}

0 commit comments

Comments
 (0)