Skip to content

Instantly share code, notes, and snippets.

@rubpy
Created August 14, 2024 19:50
Show Gist options
  • Save rubpy/964ab7866d767f046b205e0de64b69f7 to your computer and use it in GitHub Desktop.
Save rubpy/964ab7866d767f046b205e0de64b69f7 to your computer and use it in GitHub Desktop.
import type {
Commitment,
TransactionForAccounts,
TransactionForFullBase58,
TransactionForFullBase64,
TransactionForFullJsonParsed,
} from '@solana/rpc-types';
import type { TransactionVersion } from '@solana/transaction-messages';
import type { Address } from '@solana/addresses';
import type { Signature } from '@solana/keys';
import {
type AllowedNumericKeypaths,
type ParamsTransformerConfig,
getDefaultParamsTransformerForSolanaRpc,
getDefaultResponseTransformerForSolanaRpc,
jsonParsedAccountsConfigs,
KEYPATH_WILDCARD,
} from '@solana/rpc-transformers';
import {
AccountNotificationsApi,
} from '@solana/rpc-subscriptions-api';
import {
type RpcSubscriptionsApiMethods,
type RpcSubscriptionsApi,
createRpcSubscriptionsApi,
} from '@solana/rpc-subscriptions-spec';
// -----------------------------------------------------------------------------
// Subscription notification types
export type TransactionNotificationsNotificationBase = Readonly<{
signature: Signature;
}>;
export type TransactionNotificationsFilter = {
vote?: boolean;
failed?: boolean;
signature?: Signature[];
accountInclude?: Address[];
accountExclude?: Address[];
accountRequired?: Address[];
};
export type TransactionNotificationsCommonOptions = Readonly<{
commitment?: Commitment;
showRewards?: boolean;
}>;
export type TransactionNotificationsEncoding = 'base58' | 'base64' | 'jsonParsed';
export type TransactionNotificationsMaxSupportedTransactionVersion = Exclude<TransactionVersion, 'legacy'>;
export type TransactionNotificationsTransactionDetails = 'full' | 'signatures' | 'accounts' | 'none';
export interface TransactionNotificationsApi extends RpcSubscriptionsApiMethods {
// transactionDetails=full (default), encoding=jsonParsed, maxSupportedTransactionVersion=0
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: 'jsonParsed';
maxSupportedTransactionVersion: TransactionNotificationsMaxSupportedTransactionVersion;
transactionDetails?: 'full';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForFullJsonParsed<TransactionNotificationsMaxSupportedTransactionVersion>;
}>;
// transactionDetails=full (default), encoding=jsonParsed, maxSupportedTransactionVersion=missing
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: 'jsonParsed';
transactionDetails?: 'full';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForFullJsonParsed<void>;
}>;
// transactionDetails=full (default), encoding=base64, maxSupportedTransactionVersion=0
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: 'base64';
maxSupportedTransactionVersion: TransactionNotificationsMaxSupportedTransactionVersion;
transactionDetails?: 'full';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForFullBase64<TransactionNotificationsMaxSupportedTransactionVersion>;
}>;
// transactionDetails=full (default), encoding=base64, maxSupportedTransactionVersion=missing
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: 'base64';
transactionDetails?: 'full';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForFullBase64<void>;
}>;
// transactionDetails=full (default), encoding=base58, maxSupportedTransactionVersion=0
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: 'base58';
maxSupportedTransactionVersion: TransactionNotificationsMaxSupportedTransactionVersion;
transactionDetails?: 'full';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForFullBase58<TransactionNotificationsMaxSupportedTransactionVersion>;
}>;
// transactionDetails=full (default), encoding=base58, maxSupportedTransactionVersion=missing
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: 'base58';
transactionDetails?: 'full';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForFullBase58<void>;
}>;
// transactionDetails=signatures, encoding + maxSupportedTransactionVersion irrelevant
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: TransactionNotificationsEncoding;
maxSupportedTransactionVersion?: TransactionNotificationsMaxSupportedTransactionVersion;
transactionDetails: 'signatures';
}>,
): TransactionNotificationsNotificationBase;
// transactionDetails=none, encoding + maxSupportedTransactionVersion irrelevant
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: TransactionNotificationsEncoding;
maxSupportedTransactionVersion?: TransactionNotificationsMaxSupportedTransactionVersion;
transactionDetails: 'none';
}>,
): TransactionNotificationsNotificationBase;
// transactionDetails=accounts, maxSupportedTransactionVersion=0, encoding irrelevant
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: TransactionNotificationsEncoding;
maxSupportedTransactionVersion: TransactionNotificationsMaxSupportedTransactionVersion;
transactionDetails: 'accounts';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForAccounts<TransactionNotificationsMaxSupportedTransactionVersion>;
}>;
// transactionDetails=accounts, maxSupportedTransactionVersion=missing, encoding irrelevant
transactionNotifications(
filter: TransactionNotificationsFilter,
options: TransactionNotificationsCommonOptions &
Readonly<{
encoding?: TransactionNotificationsEncoding;
transactionDetails: 'accounts';
}>,
): TransactionNotificationsNotificationBase &
Readonly<{
transaction: TransactionForAccounts<void>;
}>;
}
// -----------------------------------------------------------------------------
type Config = ParamsTransformerConfig;
export type SolanaRpcEnhancedSubscriptionsApi = AccountNotificationsApi & TransactionNotificationsApi;
export function createSolanaRpcEnhancedSubscriptionsApi<TApi extends RpcSubscriptionsApiMethods = SolanaRpcEnhancedSubscriptionsApi>(
config?: Config,
): RpcSubscriptionsApi<TApi> {
return createSolanaRpcEnhancedSubscriptionsApi_INTERNAL<TApi>(config);
}
function createSolanaRpcEnhancedSubscriptionsApi_INTERNAL<TApi extends RpcSubscriptionsApiMethods>(
config?: Config,
): RpcSubscriptionsApi<TApi> {
return createRpcSubscriptionsApi<TApi>({
parametersTransformer: getDefaultParamsTransformerForSolanaRpc(config) as (params: unknown[]) => unknown[],
responseTransformer: getDefaultResponseTransformerForSolanaRpc({
allowedNumericKeyPaths: getAllowedNumericKeypaths(),
}),
subscribeNotificationNameTransformer: (notificationName: string) =>
notificationName.replace(/Notifications$/, 'Subscribe'),
unsubscribeNotificationNameTransformer: (notificationName: string) =>
notificationName.replace(/Notifications$/, 'Unsubscribe'),
});
}
let memoizedKeypaths: AllowedNumericKeypaths<
RpcSubscriptionsApi<SolanaRpcEnhancedSubscriptionsApi>
>;
function getAllowedNumericKeypaths(): AllowedNumericKeypaths<
RpcSubscriptionsApi<SolanaRpcEnhancedSubscriptionsApi>
> {
if (!memoizedKeypaths) {
memoizedKeypaths = {
accountNotifications: jsonParsedAccountsConfigs.map(c => ['value', ...c]),
transactionNotifications: [
[
'transaction',
'meta',
'preTokenBalances',
KEYPATH_WILDCARD,
'accountIndex',
],
[
'transaction',
'meta',
'preTokenBalances',
KEYPATH_WILDCARD,
'uiTokenAmount',
'decimals',
],
[
'transaction',
'meta',
'postTokenBalances',
KEYPATH_WILDCARD,
'accountIndex',
],
[
'transaction',
'meta',
'postTokenBalances',
KEYPATH_WILDCARD,
'uiTokenAmount',
'decimals',
],
[
'transaction',
'meta',
'rewards',
KEYPATH_WILDCARD,
'commission',
],
[
'transaction',
'meta',
'innerInstructions',
KEYPATH_WILDCARD,
'index',
],
[
'transaction',
'meta',
'innerInstructions',
KEYPATH_WILDCARD,
'instructions',
KEYPATH_WILDCARD,
'programIdIndex',
],
[
'transaction',
'meta',
'innerInstructions',
KEYPATH_WILDCARD,
'instructions',
KEYPATH_WILDCARD,
'accounts',
KEYPATH_WILDCARD,
],
[
'transaction',
'transaction',
'message',
'addressTableLookups',
KEYPATH_WILDCARD,
'writableIndexes',
KEYPATH_WILDCARD,
],
[
'transaction',
'transaction',
'message',
'addressTableLookups',
KEYPATH_WILDCARD,
'readonlyIndexes',
KEYPATH_WILDCARD,
],
[
'transaction',
'transaction',
'message',
'instructions',
KEYPATH_WILDCARD,
'programIdIndex',
],
[
'transaction',
'transaction',
'message',
'instructions',
KEYPATH_WILDCARD,
'accounts',
KEYPATH_WILDCARD,
],
[
'transaction',
'transaction',
'message',
'header',
'numReadonlySignedAccounts',
],
[
'transaction',
'transaction',
'message',
'header',
'numReadonlyUnsignedAccounts',
],
[
'transaction',
'transaction',
'message',
'header',
'numRequiredSignatures',
],
],
};
}
return memoizedKeypaths;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment