-
Notifications
You must be signed in to change notification settings - Fork 791
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(experimental): sysvars package: clock
This commit introduces the `Clock` sysvar to the `@solana/sysvars` package. It also introduces the `fetchSysvar` API, for fetching sysvar accounts.
- Loading branch information
1 parent
662a84f
commit f08ab63
Showing
9 changed files
with
306 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { createSolanaRpcApi, type SolanaRpcApi } from '@solana/rpc-api'; | ||
import { createRpc, type Rpc } from '@solana/rpc-spec'; | ||
import { createHttpTransport } from '@solana/rpc-transport-http'; | ||
|
||
export function createLocalhostSolanaRpc(): Rpc<SolanaRpcApi> { | ||
return createRpc({ | ||
api: createSolanaRpcApi(), | ||
transport: createHttpTransport({ url: 'http://127.0.0.1:8899' }), | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import type { GetAccountInfoApi } from '@solana/rpc-api'; | ||
import type { Rpc } from '@solana/rpc-spec'; | ||
|
||
import { fetchSysvarClock, getSysvarClockCodec } from '../clock'; | ||
import { createLocalhostSolanaRpc } from './__setup__'; | ||
|
||
describe('clock', () => { | ||
let rpc: Rpc<GetAccountInfoApi>; | ||
beforeEach(() => { | ||
rpc = createLocalhostSolanaRpc(); | ||
}); | ||
it('decode', () => { | ||
// prettier-ignore | ||
const clockState = new Uint8Array([ | ||
119, 233, 246, 16, 0, 0, 0, 0, // slot | ||
246, 255, 255, 255, 255, 255, 255, 255, // epochStartTimestamp | ||
4, 0, 0, 0, 0, 0, 0, 0, // epoch | ||
0, 0, 0, 0, 0, 0, 0, 0, // leaderScheduleEpoch | ||
224, 177, 255, 255, 255, 255, 255, 255, // unixTimestamp | ||
]); | ||
expect(getSysvarClockCodec().decode(clockState)).toMatchObject({ | ||
epoch: 4n, | ||
epochStartTimestamp: -10n, | ||
leaderScheduleEpoch: 0n, | ||
slot: 284_617_079n, | ||
unixTimestamp: -20_000n, | ||
}); | ||
}); | ||
it('fetch', async () => { | ||
expect.assertions(1); | ||
const clock = await fetchSysvarClock(rpc); | ||
expect(clock).toMatchObject({ | ||
epoch: expect.any(BigInt), | ||
epochStartTimestamp: expect.any(BigInt), | ||
leaderScheduleEpoch: expect.any(BigInt), | ||
slot: expect.any(BigInt), | ||
unixTimestamp: expect.any(BigInt), | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,48 @@ | ||
import type { GetAccountInfoApi } from '@solana/rpc-api'; | ||
import type { Rpc } from '@solana/rpc-spec'; | ||
|
||
import { fetchEncodedSysvarAccount, fetchJsonParsedSysvarAccount, SYSVAR_CLOCK_ADDRESS } from '../sysvar'; | ||
import { createLocalhostSolanaRpc } from './__setup__'; | ||
|
||
describe('sysvar account', () => { | ||
it.todo('tests'); | ||
let rpc: Rpc<GetAccountInfoApi>; | ||
beforeEach(() => { | ||
rpc = createLocalhostSolanaRpc(); | ||
}); | ||
const assertValidEncodedSysvarAccount = async (address: Parameters<typeof fetchEncodedSysvarAccount>[1]) => { | ||
const account = await fetchEncodedSysvarAccount(rpc, address); | ||
expect(account.address).toEqual(address); | ||
expect(account.exists).toBe(true); | ||
expect(account).toMatchObject({ | ||
data: expect.any(Uint8Array), | ||
}); | ||
}; | ||
const assertValidJsonParsedSysvarAccount = async ( | ||
address: Parameters<typeof fetchEncodedSysvarAccount>[1], | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
data: any, | ||
) => { | ||
const account = await fetchJsonParsedSysvarAccount(rpc, address); | ||
expect(account.address).toEqual(address); | ||
expect(account.exists).toBe(true); | ||
expect(account).toMatchObject(data); | ||
}; | ||
describe('clock', () => { | ||
it('fetch encoded', async () => { | ||
expect.assertions(3); | ||
await assertValidEncodedSysvarAccount(SYSVAR_CLOCK_ADDRESS); | ||
}); | ||
it('fetch JSON-parsed', async () => { | ||
expect.assertions(3); | ||
await assertValidJsonParsedSysvarAccount(SYSVAR_CLOCK_ADDRESS, { | ||
data: { | ||
epoch: expect.any(BigInt), | ||
epochStartTimestamp: expect.any(BigInt), | ||
leaderScheduleEpoch: expect.any(BigInt), | ||
slot: expect.any(BigInt), | ||
unixTimestamp: expect.any(BigInt), | ||
}, | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import type { MaybeAccount, MaybeEncodedAccount } from '@solana/accounts'; | ||
import type { Address } from '@solana/addresses'; | ||
import type { JsonParsedSysvarAccount } from '@solana/rpc-parsed-types'; | ||
|
||
import { fetchSysvarClock, type SysvarClock } from '../clock'; | ||
import { fetchEncodedSysvarAccount, fetchJsonParsedSysvarAccount, SYSVAR_CLOCK_ADDRESS } from '../sysvar'; | ||
|
||
const rpc = null as unknown as Parameters<typeof fetchEncodedSysvarAccount>[0]; | ||
|
||
// `fetchEncodedSysvarAccount` | ||
{ | ||
// Only accepts a valid sysvar address. | ||
fetchEncodedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS); | ||
// @ts-expect-error Only accepts a valid sysvar address. | ||
fetchEncodedSysvarAccount(rpc, 'Foo1111' as Address<'Foo1111'>); | ||
|
||
// Returns a `MaybeEncodedAccount` with the provided sysvar address. | ||
fetchEncodedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS) satisfies Promise< | ||
MaybeEncodedAccount<typeof SYSVAR_CLOCK_ADDRESS> | ||
>; | ||
// @ts-expect-error Returns a `MaybeEncodedAccount` with the provided sysvar address. | ||
fetchEncodedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS) satisfies Promise<MaybeEncodedAccount<Address<'Foo1111'>>>; | ||
} | ||
|
||
// `fetchJsonParsedSysvarAccount` | ||
{ | ||
// Only accepts a valid sysvar address. | ||
fetchJsonParsedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS); | ||
// @ts-expect-error Only accepts a valid sysvar address. | ||
fetchJsonParsedSysvarAccount(rpc, 'Foo1111' as Address<'Foo1111'>); | ||
|
||
// Returns an `MaybeAccount or MaybeEncodedAccount` with the provided sysvar address, | ||
// as well as the `JsonParsedSysvarAccount` data. | ||
fetchJsonParsedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS) satisfies Promise< | ||
| MaybeAccount<JsonParsedSysvarAccount, typeof SYSVAR_CLOCK_ADDRESS> | ||
| MaybeEncodedAccount<typeof SYSVAR_CLOCK_ADDRESS> | ||
>; | ||
// @ts-expect-error Returns an `MaybeAccount or MaybeEncodedAccount` with the provided address. | ||
fetchJsonParsedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS) satisfies Promise< | ||
MaybeAccount<JsonParsedSysvarAccount, Address<'Foo1111'>> | MaybeEncodedAccount<Address<'Foo1111'>> | ||
>; | ||
// @ts-expect-error Returns an `MaybeAccount or MaybeEncodedAccount` with `JsonParsedSysvarAccount` data. | ||
fetchJsonParsedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS) satisfies Promise< | ||
MaybeAccount<{ foo: string }, typeof SYSVAR_CLOCK_ADDRESS> | MaybeEncodedAccount<typeof SYSVAR_CLOCK_ADDRESS> | ||
>; | ||
} | ||
|
||
// `fetchSysvarClock` | ||
{ | ||
// Returns a `SysvarClock`. | ||
fetchSysvarClock(rpc) satisfies Promise<SysvarClock>; | ||
// @ts-expect-error Returns a `SysvarClock`. | ||
fetchSysvarClock(rpc) satisfies Promise<{ foo: string }>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { assertAccountExists, decodeAccount, type FetchAccountConfig } from '@solana/accounts'; | ||
import { | ||
combineCodec, | ||
type FixedSizeCodec, | ||
type FixedSizeDecoder, | ||
type FixedSizeEncoder, | ||
getI64Decoder, | ||
getI64Encoder, | ||
getStructDecoder, | ||
getStructEncoder, | ||
getU64Decoder, | ||
getU64Encoder, | ||
} from '@solana/codecs'; | ||
import type { GetAccountInfoApi } from '@solana/rpc-api'; | ||
import type { Rpc } from '@solana/rpc-spec'; | ||
import type { Epoch, Slot } from '@solana/rpc-types'; | ||
|
||
import { fetchEncodedSysvarAccount, SYSVAR_CLOCK_ADDRESS } from './sysvar'; | ||
|
||
type UnixTimestamp = bigint; | ||
|
||
type SysvarClockSize = 40; | ||
|
||
/** | ||
* The `Clock` sysvar. | ||
* | ||
* Information about the network’s clock, ticks, slots, etc. | ||
*/ | ||
export type SysvarClock = Readonly<{ | ||
epoch: Epoch; | ||
epochStartTimestamp: UnixTimestamp; | ||
leaderScheduleEpoch: Epoch; | ||
slot: Slot; | ||
unixTimestamp: UnixTimestamp; | ||
}>; | ||
|
||
export function getSysvarClockEncoder(): FixedSizeEncoder<SysvarClock, SysvarClockSize> { | ||
return getStructEncoder([ | ||
['slot', getU64Encoder()], | ||
['epochStartTimestamp', getI64Encoder()], | ||
['epoch', getU64Encoder()], | ||
['leaderScheduleEpoch', getU64Encoder()], | ||
['unixTimestamp', getI64Encoder()], | ||
]) as FixedSizeEncoder<SysvarClock, SysvarClockSize>; | ||
} | ||
|
||
export function getSysvarClockDecoder(): FixedSizeDecoder<SysvarClock, SysvarClockSize> { | ||
return getStructDecoder([ | ||
['slot', getU64Decoder()], | ||
['epochStartTimestamp', getI64Decoder()], | ||
['epoch', getU64Decoder()], | ||
['leaderScheduleEpoch', getU64Decoder()], | ||
['unixTimestamp', getI64Decoder()], | ||
]) as FixedSizeDecoder<SysvarClock, SysvarClockSize>; | ||
} | ||
|
||
export function getSysvarClockCodec(): FixedSizeCodec<SysvarClock, SysvarClock, SysvarClockSize> { | ||
return combineCodec(getSysvarClockEncoder(), getSysvarClockDecoder()); | ||
} | ||
|
||
/** | ||
* Fetch the `Clock` sysvar. | ||
* | ||
* Information about the network’s clock, ticks, slots, etc. | ||
*/ | ||
export async function fetchSysvarClock(rpc: Rpc<GetAccountInfoApi>, config?: FetchAccountConfig): Promise<SysvarClock> { | ||
const account = await fetchEncodedSysvarAccount(rpc, SYSVAR_CLOCK_ADDRESS, config); | ||
assertAccountExists(account); | ||
const decoded = decodeAccount(account, getSysvarClockDecoder()); | ||
return decoded.data; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './clock'; | ||
export * from './sysvar'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { | ||
type FetchAccountConfig, | ||
fetchEncodedAccount, | ||
fetchJsonParsedAccount, | ||
type MaybeAccount, | ||
type MaybeEncodedAccount, | ||
} from '@solana/accounts'; | ||
import type { Address } from '@solana/addresses'; | ||
import type { GetAccountInfoApi } from '@solana/rpc-api'; | ||
import type { JsonParsedSysvarAccount } from '@solana/rpc-parsed-types'; | ||
import type { Rpc } from '@solana/rpc-spec'; | ||
|
||
export const SYSVAR_CLOCK_ADDRESS = | ||
'SysvarC1ock11111111111111111111111111111111' as Address<'SysvarC1ock11111111111111111111111111111111'>; | ||
|
||
type SysvarAddress = typeof SYSVAR_CLOCK_ADDRESS; | ||
|
||
/** | ||
* Fetch an encoded sysvar account. | ||
* | ||
* Sysvars are special accounts that contain dynamically-updated data about the | ||
* network cluster, the blockchain history, and the executing transaction. | ||
*/ | ||
export async function fetchEncodedSysvarAccount<TAddress extends SysvarAddress>( | ||
rpc: Rpc<GetAccountInfoApi>, | ||
address: TAddress, | ||
config?: FetchAccountConfig, | ||
): Promise<MaybeEncodedAccount<TAddress>> { | ||
return fetchEncodedAccount<TAddress>(rpc, address, config); | ||
} | ||
|
||
/** | ||
* Fetch a JSON-parsed sysvar account. | ||
* | ||
* Sysvars are special accounts that contain dynamically-updated data about the | ||
* network cluster, the blockchain history, and the executing transaction. | ||
*/ | ||
export async function fetchJsonParsedSysvarAccount<TAddress extends SysvarAddress>( | ||
rpc: Rpc<GetAccountInfoApi>, | ||
address: TAddress, | ||
config?: FetchAccountConfig, | ||
): Promise<MaybeAccount<JsonParsedSysvarAccount, TAddress> | MaybeEncodedAccount<TAddress>> { | ||
return fetchJsonParsedAccount<JsonParsedSysvarAccount, TAddress>(rpc, address, config); | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.