A fully-typed TypeScript client for the Solana Yellowstone Geyser gRPC service, providing real-time access to Solana blockchain data with complete type safety and @solana/web3.js compatibility.
npm install @igroza/yellowstone-grpc-clientCopy the gRPC proto files to your project's proto folder:
cp ./node_modules/@igroza/yellowstone-grpc-client/proto/* ./proto/This will copy the required .proto files (geyser.proto and solana-storage.proto) to your project for gRPC connection.
These dependencies will not be installed in your project, but are included in the release bundle file via esbuild.
Dependency graph:
These dependencies used for building the package.
- @typescript-eslint/eslint-plugin@8.32.1
- @typescript-eslint/parser@8.32.1
- esbuild@0.25.10
- esbuild-plugin-d.ts@1.3.1
- eslint@9.27.0
- eslint-config-prettier@10.1.5
- eslint-plugin-eslint-comments@3.2.0
- eslint-plugin-import@2.31.0
- eslint-plugin-prettier@5.4.0
- eslint-plugin-sort-class-members@1.21.0
- prettier@3.6.2
- ts-proto@2.7.7
- typescript@5.7.3
- typescript-eslint@8.32.1
-
Clone the repository:
git clone https://github.com/igroza/yellowstone-grpc-client.git cd yellowstone-grpc-client -
replace
grpc-url.com:10101with your actual gRPC endpoint in the example/example.ts file -
Install dependencies:
npm install
-
Run example code:
npm run example
import { YellowstoneGeyserClient, CommitmentLevel, TransactionFormatter } from '@igroza/yellowstone-grpc-client';
// Create client
const client = new YellowstoneGeyserClient({
endpoint: 'grpc-url.com:10101',
credentials: 'your-api-token' // optional
});
// Connect to server
await client.connect();
// Subscribe to transactions
const stream = client.createSubscription(
{
transactions: {
'my_filter': {
vote: false,
failed: false,
account_include: ['YourAccountAddressHere']
}
},
commitment: CommitmentLevel.PROCESSED
},
(update) => {
if (update.transaction) {
const tx = TransactionFormatter.formTransactionFromJson(update);
console.log('Transaction:', tx.transaction.signatures[0]);
}
},
(error) => console.error('Error:', error),
() => console.log('Stream ended')
);const client = new YellowstoneGeyserClient(config: YellowstoneGeyserClientConfig);Configuration:
interface YellowstoneGeyserClientConfig {
endpoint: string; // gRPC endpoint
credentials?: string; // Optional authentication token (added to 'x-token' header)
options?: grpc.ChannelOptions; // Optional gRPC channel options
}Example with authentication:
const client = new YellowstoneGeyserClient({
endpoint: 'grpc-url.com:10101',
credentials: 'your-api-token-here'
});Establishes connection to the gRPC server.
await client.connect();Creates a subscription with callback handlers for data, errors, and stream end.
const stream = client.createSubscription(
{ transactions: { 'filter': { vote: false } }, commitment: CommitmentLevel.PROCESSED },
(update) => console.log('Update:', update),
(error) => console.error('Error:', error),
() => console.log('Stream ended')
);Low-level method that creates a bidirectional streaming subscription. Returns a gRPC stream object.
const stream = client.subscribe({
transactions: { 'filter': { vote: false } },
commitment: CommitmentLevel.PROCESSED
});Retrieves the Geyser server version information.
const version = await client.getVersion();
console.log('Version:', version.version);Gets the current slot number.
const slot = await client.getSlot();
console.log('Current slot:', slot.slot);Gets the current block height.
const blockHeight = await client.getBlockHeight();
console.log('Block height:', blockHeight.block_height);Gets the latest blockhash.
const { blockhash, slot, last_valid_block_height } = await client.getLatestBlockhash();Checks if a blockhash is still valid.
const { valid } = await client.isBlockhashValid({ blockhash: 'your-blockhash' });Sends a ping to test connectivity.
const response = await client.ping({ count: 1 });Sends a ping through an existing subscription stream to keep the connection alive.
client.sendPing(stream, Date.now());Closes the gRPC client connection.
client.close();Subscribe to account updates with flexible filtering options.
client.createSubscription(
{
accounts: {
'my_accounts': {
account: ['AccountPubkey1', 'AccountPubkey2'],
owner: ['ProgramId1', 'ProgramId2'],
filters: [
{ memcmp: { offset: 0, base58: 'SomeData' } },
{ datasize: 165 },
{ token_account_state: true },
{ lamports: { gt: 1000000 } }
],
nonempty_txn_signature: true
}
},
commitment: CommitmentLevel.CONFIRMED
},
(update) => {
if (update.account) {
const { account, slot } = update.account;
console.log('Account updated:', {
pubkey: Buffer.from(account.pubkey).toString('base64'),
lamports: account.lamports,
slot: slot
});
}
}
);Subscribe to transactions with filtering options.
client.createSubscription(
{
transactions: {
'my_transactions': {
vote: false,
failed: false,
account_include: ['Account1', 'Account2'],
account_exclude: ['Account3'],
account_required: ['Account4']
}
},
commitment: CommitmentLevel.PROCESSED,
from_slot: 100000000
},
(update) => {
if (update.transaction) {
const tx = TransactionFormatter.formTransactionFromJson(update);
console.log('Transaction:', {
signature: tx.transaction.signatures[0],
slot: tx.slot,
success: tx.meta?.err === null
});
}
}
);Subscribe to slot status updates.
client.createSubscription(
{
slots: {
'my_slots': {
filter_by_commitment: true,
interslot_updates: true
}
},
commitment: CommitmentLevel.FINALIZED
},
(update) => {
if (update.slot) {
console.log('Slot:', update.slot.slot, 'Status:', update.slot.status);
}
}
);Subscribe to complete block data.
client.createSubscription(
{
blocks: {
'my_blocks': {
account_include: ['AccountToMonitor'],
include_transactions: true,
include_accounts: true,
include_entries: true
}
},
commitment: CommitmentLevel.CONFIRMED
},
(update) => {
if (update.block) {
console.log('Block:', update.block.slot, 'Txs:', update.block.executed_transaction_count);
}
}
);Subscribe to lightweight block metadata.
client.createSubscription(
{
blocks_meta: { 'my_block_meta': {} },
commitment: CommitmentLevel.FINALIZED
},
(update) => {
if (update.block_meta) {
console.log('Block:', update.block_meta.slot, update.block_meta.blockhash);
}
}
);Subscribe to entry updates.
client.createSubscription(
{ entry: { 'my_entries': {} } },
(update) => {
if (update.entry) {
console.log('Entry:', update.entry.slot, update.entry.index);
}
}
);Subscribe to lightweight transaction status updates.
client.createSubscription(
{
transactions_status: {
'my_tx_status': {
vote: false,
failed: false
}
}
},
(update) => {
if (update.transaction_status) {
console.log('Status:', update.transaction_status.slot);
}
}
);Utility class for converting between Yellowstone transaction data and Solana web3.js compatible formats.
Converts raw Yellowstone transaction data into Solana web3.js VersionedTransactionResponse format.
import { TransactionFormatter } from '@igroza/yellowstone-grpc-client';
const tx = TransactionFormatter.formTransactionFromJson(update);
console.log('Signature:', tx.transaction.signatures[0]);
console.log('Fee:', tx.meta.fee);
console.log('Success:', tx.meta.err === null);
console.log('Slot:', tx.slot);
console.log('Block Time:', tx.blockTime);Converts a VersionedTransactionResponse to plain JSON format for serialization or storage.
import { TransactionFormatter } from '@igroza/yellowstone-grpc-client';
const tx = TransactionFormatter.formTransactionFromJson(update);
const json = TransactionFormatter.toJSON(tx);
// Store or transmit as JSON
console.log(JSON.stringify(json, null, 2));
// Save to file or database
fs.writeFileSync('transaction.json', JSON.stringify(json));Check the update_oneof field to determine update type:
import { UpdateType } from '@igroza/yellowstone-grpc-client';
client.createSubscription(request, (update) => {
switch (update.update_oneof) {
case UpdateType.ACCOUNT:
console.log('Account:', update.account);
break;
case UpdateType.TRANSACTION:
const tx = TransactionFormatter.formTransactionFromJson(update);
console.log('Transaction:', tx);
break;
case UpdateType.SLOT:
console.log('Slot:', update.slot);
break;
case UpdateType.PING:
client.sendPing(stream, Date.now());
break;
case UpdateType.PONG:
console.log('Pong:', update.pong?.id);
break;
}
});filters: [{ memcmp: { offset: 32, base58: 'YourMintAddress' } }]filters: [{ datasize: 165 }]filters: [{ lamports: { gt: 1000000 } }] // gt, lt, eq, neaccounts_data_slice: [{ offset: 0, length: 32 }]CommitmentLevel.PROCESSED // Fastest, may be rolled back
CommitmentLevel.CONFIRMED // Confirmed by supermajority
CommitmentLevel.FINALIZED // Finalized, cannot be rolled backclient.createSubscription(
request,
(update) => { /* handle data */ },
(error) => {
console.error('Error:', error.message, error.code);
// Common codes: 14 (UNAVAILABLE), 4 (DEADLINE_EXCEEDED), 13 (INTERNAL)
},
() => console.log('Stream ended')
);
// Graceful shutdown
process.on('SIGINT', () => {
client.close();
process.exit(0);
});The client extends EventEmitter and emits various events during its lifecycle. Use the YellowstoneGeyserClientEvents enum for type-safe event handling:
import { YellowstoneGeyserClient, YellowstoneGeyserClientEvents } from '@igroza/yellowstone-grpc-client';
const client = new YellowstoneGeyserClient({ endpoint: 'grpc-url.com:10101' });
// Listen to lifecycle events
client.on(YellowstoneGeyserClientEvents.INITIALIZED, () => {
console.log('Client initialized');
});
client.on(YellowstoneGeyserClientEvents.CONNECTED, () => {
console.log('Connected to server');
});
client.on(YellowstoneGeyserClientEvents.SUBSCRIBED, (request) => {
console.log('Subscription created:', request);
});
client.on(YellowstoneGeyserClientEvents.ERROR, (error) => {
console.error('Error occurred:', error);
});
client.on(YellowstoneGeyserClientEvents.STREAM_ENDED, () => {
console.log('Stream ended');
});
client.on(YellowstoneGeyserClientEvents.CLOSED, () => {
console.log('Client closed');
});
client.on(YellowstoneGeyserClientEvents.STATUS, (status) => {
console.log('Status update:', status);
});Available Events:
INITIALIZED- Client has been initializedCONNECTED- Successfully connected to the gRPC serverSUBSCRIBED- Subscription has been createdERROR- An error occurredSTREAM_ENDED- Subscription stream has endedCLOSED- Client connection has been closedSTATUS- gRPC status update received
MIT
