Skip to content

Commit

Permalink
refactor(experimental): sysvars package: stake history
Browse files Browse the repository at this point in the history
This commit introduces the `StakeHistory` sysvar to the `@solana/sysvars` package.
  • Loading branch information
buffalojoec committed Mar 14, 2024
1 parent 8c307a9 commit d3800f7
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 1 deletion.
43 changes: 43 additions & 0 deletions packages/sysvars/src/__tests__/stake-history-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { GetAccountInfoApi } from '@solana/rpc-api';
import type { Rpc } from '@solana/rpc-spec';

import { fetchSysvarStakeHistory, getSysvarStakeHistoryCodec } from '../stake-history';
import { createLocalhostSolanaRpc } from './__setup__';

describe('stake history', () => {
let rpc: Rpc<GetAccountInfoApi>;
beforeEach(() => {
rpc = createLocalhostSolanaRpc();
});
it('decode', () => {
// prettier-ignore
const stakeHistoryState = new Uint8Array([
2, 0, 0, 0, // array length
0, 208, 237, 144, 46, 0, 0, 0, // effective
0, 160, 219, 33, 93, 0, 0, 0, // activating
0, 112, 201, 178, 139, 0, 0, 0, // deactivating
0, 160, 219, 33, 93, 0, 0, 0, // effective
0, 112, 201, 178, 139, 0, 0, 0, // activating
0, 64, 183, 67, 186, 0, 0, 0, // deactivating
]);
expect(getSysvarStakeHistoryCodec().decode(stakeHistoryState)).toMatchObject(
expect.arrayContaining([
{
activating: 400_000_000_000n,
deactivating: 600_000_000_000n,
effective: 200_000_000_000n,
},
{
activating: 600_000_000_000n,
deactivating: 800_000_000_000n,
effective: 400_000_000_000n,
},
]),
);
});
it('fetch', async () => {
expect.assertions(1);
const stakeHistory = await fetchSysvarStakeHistory(rpc);
expect(stakeHistory).toMatchObject(expect.any(Array)); // Not always populated on test validator
});
});
13 changes: 13 additions & 0 deletions packages/sysvars/src/__tests__/sysvar-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
SYSVAR_RENT_ADDRESS,
SYSVAR_SLOT_HASHES_ADDRESS,
SYSVAR_SLOT_HISTORY_ADDRESS,
SYSVAR_STAKE_HISTORY_ADDRESS,
} from '../sysvar';
import { createLocalhostSolanaRpc } from './__setup__';

Expand Down Expand Up @@ -174,4 +175,16 @@ describe('sysvar account', () => {
});
});
});
describe('stake history', () => {
it('fetch encoded', async () => {
expect.assertions(3);
await assertValidEncodedSysvarAccount(SYSVAR_STAKE_HISTORY_ADDRESS);
});
it('fetch JSON-parsed', async () => {
expect.assertions(3);
await assertValidJsonParsedSysvarAccount(SYSVAR_STAKE_HISTORY_ADDRESS, {
data: expect.any(Array), // Not always populated on test validator
});
});
});
});
9 changes: 9 additions & 0 deletions packages/sysvars/src/__typetests__/sysvar-typetest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { fetchSysvarRecentBlockhashes, type SysvarRecentBlockhashes } from '../r
import { fetchSysvarRent, type SysvarRent } from '../rent';
import { fetchSysvarSlotHashes, type SysvarSlotHashes } from '../slot-hashes';
import { fetchSysvarSlotHistory, type SysvarSlotHistory } from '../slot-history';
import { fetchSysvarStakeHistory, type SysvarStakeHistory } from '../stake-history';
import { fetchEncodedSysvarAccount, fetchJsonParsedSysvarAccount, SYSVAR_CLOCK_ADDRESS } from '../sysvar';

const rpc = null as unknown as Parameters<typeof fetchEncodedSysvarAccount>[0];
Expand Down Expand Up @@ -124,3 +125,11 @@ const rpc = null as unknown as Parameters<typeof fetchEncodedSysvarAccount>[0];
// @ts-expect-error Returns a `SysvarSlotHistory`.
fetchSysvarSlotHistory(rpc) satisfies Promise<{ foo: string }>;
}

// `fetchSysvarStakeHistory`
{
// Returns a `SysvarStakeHistory`.
fetchSysvarStakeHistory(rpc) satisfies Promise<SysvarStakeHistory>;
// @ts-expect-error Returns a `SysvarStakeHistory`.
fetchSysvarStakeHistory(rpc) satisfies Promise<{ foo: string }>;
}
1 change: 1 addition & 0 deletions packages/sysvars/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export * from './recent-blockhashes';
export * from './rent';
export * from './slot-hashes';
export * from './slot-history';
export * from './stake-history';
export * from './sysvar';
68 changes: 68 additions & 0 deletions packages/sysvars/src/stake-history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { assertAccountExists, decodeAccount, type FetchAccountConfig } from '@solana/accounts';
import {
combineCodec,
getArrayDecoder,
getArrayEncoder,
getStructDecoder,
getStructEncoder,
type VariableSizeCodec,
type VariableSizeDecoder,
type VariableSizeEncoder,
} from '@solana/codecs';
import type { GetAccountInfoApi } from '@solana/rpc-api';
import type { Rpc } from '@solana/rpc-spec';
import { getLamportsDecoder, getLamportsEncoder, type LamportsUnsafeBeyond2Pow53Minus1 } from '@solana/rpc-types';

import { fetchEncodedSysvarAccount, SYSVAR_STAKE_HISTORY_ADDRESS } from './sysvar';

type Entry = Readonly<{
activating: LamportsUnsafeBeyond2Pow53Minus1;
deactivating: LamportsUnsafeBeyond2Pow53Minus1;
effective: LamportsUnsafeBeyond2Pow53Minus1;
}>;

/**
* The `StakeHistory` sysvar.
*
* History of stake activations and de-activations.
*/
export type SysvarStakeHistory = Entry[];

export function getSysvarStakeHistoryEncoder(): VariableSizeEncoder<SysvarStakeHistory> {
return getArrayEncoder(
getStructEncoder([
['effective', getLamportsEncoder()],
['activating', getLamportsEncoder()],
['deactivating', getLamportsEncoder()],
]),
);
}

export function getSysvarStakeHistoryDecoder(): VariableSizeDecoder<SysvarStakeHistory> {
return getArrayDecoder(
getStructDecoder([
['effective', getLamportsDecoder()],
['activating', getLamportsDecoder()],
['deactivating', getLamportsDecoder()],
]),
);
}

export function getSysvarStakeHistoryCodec(): VariableSizeCodec<SysvarStakeHistory> {
return combineCodec(getSysvarStakeHistoryEncoder(), getSysvarStakeHistoryDecoder());
}

/**
* Fetch the `StakeHistory` sysvar.
*
* History of stake activations and de-activations.
*/
export async function fetchSysvarStakeHistory(
rpc: Rpc<GetAccountInfoApi>,
config?: FetchAccountConfig,
): Promise<SysvarStakeHistory> {
const account = await fetchEncodedSysvarAccount(rpc, SYSVAR_STAKE_HISTORY_ADDRESS, config);
assertAccountExists(account);
const decoded = decodeAccount(account, getSysvarStakeHistoryDecoder());
return decoded.data;
}
5 changes: 4 additions & 1 deletion packages/sysvars/src/sysvar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const SYSVAR_SLOT_HASHES_ADDRESS =
'SysvarS1otHashes111111111111111111111111111' as Address<'SysvarS1otHashes111111111111111111111111111'>;
export const SYSVAR_SLOT_HISTORY_ADDRESS =
'SysvarS1otHistory11111111111111111111111111' as Address<'SysvarS1otHistory11111111111111111111111111'>;
export const SYSVAR_STAKE_HISTORY_ADDRESS =
'SysvarStakeHistory1111111111111111111111111' as Address<'SysvarStakeHistory1111111111111111111111111'>;

type SysvarAddress =
| typeof SYSVAR_CLOCK_ADDRESS
Expand All @@ -41,7 +43,8 @@ type SysvarAddress =
| typeof SYSVAR_RECENT_BLOCKHASHES_ADDRESS
| typeof SYSVAR_RENT_ADDRESS
| typeof SYSVAR_SLOT_HASHES_ADDRESS
| typeof SYSVAR_SLOT_HISTORY_ADDRESS;
| typeof SYSVAR_SLOT_HISTORY_ADDRESS
| typeof SYSVAR_STAKE_HISTORY_ADDRESS;

/**
* Fetch an encoded sysvar account.
Expand Down

0 comments on commit d3800f7

Please sign in to comment.