Skip to content

Commit

Permalink
refactor(experimental): sysvars package: epoch rewards
Browse files Browse the repository at this point in the history
This commit introduces the `EpochRewards` sysvar to the `@solana/sysvars` package.
  • Loading branch information
buffalojoec committed Mar 14, 2024
1 parent f08ab63 commit cc3a5ac
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 1 deletion.
19 changes: 19 additions & 0 deletions packages/sysvars/src/__tests__/epoch-rewards-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getSysvarEpochRewardsCodec } from '../epoch-rewards';

describe('epoch rewards', () => {
it('decode', () => {
// prettier-ignore
const epochRewardsState = new Uint8Array([
0, 45, 49, 1, 0, 0, 0, 0, // distributionCompleteBlockHeight
134, 74, 2, 0, 0, 0, 0, 0, // distributedRewards
0, 132, 215, 23, 0, 0, 0, 0, // totalRewards
]);
expect(getSysvarEpochRewardsCodec().decode(epochRewardsState)).toMatchObject({
distributedRewards: 150_150n,
distributionCompleteBlockHeight: 20_000_000n,
totalRewards: 400_000_000n,
});
});
// TODO: This account does not seem to exist on-chain yet.
it.todo('fetch');
});
2 changes: 2 additions & 0 deletions packages/sysvars/src/__tests__/sysvar-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,6 @@ describe('sysvar account', () => {
});
});
});
// `EpochRewards` will only appear at the start of an epoch, after epoch 0 concludes.
// See https://github.com/solana-labs/solana/blob/e0203f22dc83cb792fa97f91dbe6e924cbd08af1/docs/src/runtime/sysvars.md?plain=1#L155-L168
});
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 @@ -3,6 +3,7 @@ import type { Address } from '@solana/addresses';
import type { JsonParsedSysvarAccount } from '@solana/rpc-parsed-types';

import { fetchSysvarClock, type SysvarClock } from '../clock';
import { fetchSysvarEpochRewards, type SysvarEpochRewards } from '../epoch-rewards';
import { fetchEncodedSysvarAccount, fetchJsonParsedSysvarAccount, SYSVAR_CLOCK_ADDRESS } from '../sysvar';

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

// `fetchSysvarEpochRewards`
{
// Returns a `SysvarEpochRewards`.
fetchSysvarEpochRewards(rpc) satisfies Promise<SysvarEpochRewards>;
// @ts-expect-error Returns a `SysvarEpochRewards`.
fetchSysvarEpochRewards(rpc) satisfies Promise<{ foo: string }>;
}
83 changes: 83 additions & 0 deletions packages/sysvars/src/epoch-rewards.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { assertAccountExists, decodeAccount, type FetchAccountConfig } from '@solana/accounts';
import {
combineCodec,
type FixedSizeCodec,
type FixedSizeDecoder,
type FixedSizeEncoder,
getStructDecoder,
getStructEncoder,
getU64Decoder,
getU64Encoder,
} from '@solana/codecs';
import type { GetAccountInfoApi } from '@solana/rpc-api';
import type { Rpc } from '@solana/rpc-spec';

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

type SysvarEpochRewardsSize = 24;

/**
* The `EpochRewards` sysvar.
*
* Tracks the progress of epoch rewards distribution. It includes:
* - Total rewards for the current epoch, in lamports.
* - Rewards for the current epoch distributed so far, in lamports.
* - Distribution completed block height, i.e. distribution of all staking rewards for the current
* epoch will be completed at this block height.
*
* Note that `EpochRewards` only lasts for a handful of blocks at the start of
* an epoch. When all rewards have been distributed, the sysvar is deleted.
* See https://github.com/solana-labs/solana/blob/e0203f22dc83cb792fa97f91dbe6e924cbd08af1/docs/src/runtime/sysvars.md?plain=1#L155-L168
*/
export type SysvarEpochRewards = Readonly<{
distributedRewards: bigint;
distributionCompleteBlockHeight: bigint;
totalRewards: bigint;
}>;

export function getSysvarEpochRewardsEncoder(): FixedSizeEncoder<SysvarEpochRewards, SysvarEpochRewardsSize> {
return getStructEncoder([
['distributionCompleteBlockHeight', getU64Encoder()],
['distributedRewards', getU64Encoder()],
['totalRewards', getU64Encoder()],
]) as FixedSizeEncoder<SysvarEpochRewards, SysvarEpochRewardsSize>;
}

export function getSysvarEpochRewardsDecoder(): FixedSizeDecoder<SysvarEpochRewards, SysvarEpochRewardsSize> {
return getStructDecoder([
['distributionCompleteBlockHeight', getU64Decoder()],
['distributedRewards', getU64Decoder()],
['totalRewards', getU64Decoder()],
]) as FixedSizeDecoder<SysvarEpochRewards, SysvarEpochRewardsSize>;
}

export function getSysvarEpochRewardsCodec(): FixedSizeCodec<
SysvarEpochRewards,
SysvarEpochRewards,
SysvarEpochRewardsSize
> {
return combineCodec(getSysvarEpochRewardsEncoder(), getSysvarEpochRewardsDecoder());
}

/**
* Fetch the `EpochRewards` sysvar.
*
* Tracks the progress of epoch rewards distribution. It includes:
* - Total rewards for the current epoch, in lamports.
* - Rewards for the current epoch distributed so far, in lamports.
* - Distribution completed block height, i.e. distribution of all staking rewards for the current
* epoch will be completed at this block height.
*
* Note that `EpochRewards` only lasts for a handful of blocks at the start of
* an epoch. When all rewards have been distributed, the sysvar is deleted.
* See https://github.com/solana-labs/solana/blob/e0203f22dc83cb792fa97f91dbe6e924cbd08af1/docs/src/runtime/sysvars.md?plain=1#L155-L168
*/
export async function fetchSysvarEpochRewards(
rpc: Rpc<GetAccountInfoApi>,
config?: FetchAccountConfig,
): Promise<SysvarEpochRewards> {
const account = await fetchEncodedSysvarAccount(rpc, SYSVAR_EPOCH_REWARDS_ADDRESS, config);
assertAccountExists(account);
const decoded = decodeAccount(account, getSysvarEpochRewardsDecoder());
return decoded.data;
}
1 change: 1 addition & 0 deletions packages/sysvars/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './clock';
export * from './epoch-rewards';
export * from './sysvar';
4 changes: 3 additions & 1 deletion packages/sysvars/src/sysvar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import type { Rpc } from '@solana/rpc-spec';

export const SYSVAR_CLOCK_ADDRESS =
'SysvarC1ock11111111111111111111111111111111' as Address<'SysvarC1ock11111111111111111111111111111111'>;
export const SYSVAR_EPOCH_REWARDS_ADDRESS =
'SysvarEpochRewards1111111111111111111111111' as Address<'SysvarEpochRewards1111111111111111111111111'>;

type SysvarAddress = typeof SYSVAR_CLOCK_ADDRESS;
type SysvarAddress = typeof SYSVAR_CLOCK_ADDRESS | typeof SYSVAR_EPOCH_REWARDS_ADDRESS;

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

0 comments on commit cc3a5ac

Please sign in to comment.