Team Shyft
· January 22, 2026

Moonshot simplifies the launch and discovery of meme coins on the Solana blockchain, similar to pump.fun. It provides a platform where traders can invest in promising meme tokens early, offering the potential for significant profits. Investing early in projects can be challenging due to the increasing number of tokens launched, making monitoring them difficult. Although Moonshot is relatively new, with little or no development tool available for streaming data, Shyft makes it possible to stream transactions and obtain necessary information for trading on Moonshot using the gRPC service. This tutorial will explore how to use gRPC to stream transactions on Moonshot and potentially gain significant profits.
To get started, we will need a few things.
Authentication: Your Shyft API Key, gRPC endpoint and gRPC token
You can get your own Shyft API Key(an auth parameter used by Shyft) from the Shyft website. You can also find your region-specific gRPC endpoint and access token on your Shyft Dashboard.
A server-side backend (like NodeJS) to receive gRPC data
As gRPC services are unsupported in web browsers, you would need a backend application to receive gRPC data. For this example, we have used NodeJS, but any other backend server-side languages such as C#, Go, Java, Kotlin, Python or PHP can also be used.
In this article, we will look at streaming “trading transactions” on Moonshot using gRPC. We will be covering the following topics,
To get started, we need to clone the git repository where the code can be found.
To clone the repository using Git, you follow this process:
**git clone** followed by the URL of the repository you want to clone. It should look something like this$ git clone <https://github.com/Shyft-to/solana-defi.git>
On your terminal, you need to open the folder where the code is by using cd grpc-moonshot and npm install to install all dependencies.
cd grpc-moonshot npm install
All the code related to this article is available on our GitHub here. Please feel free to clone it and follow along.
To begin streaming data from Moonshot using gRPC, we need to establish the client service. This requires two crucial pieces of information: the gRPC URL and the gRPC Access Token. The access token is a security credential that is usually a long string of alphanumeric characters while the URL is your gRPC region URL. With these two details in hand, we can proceed to set up the client service and start streaming data from Moonshot
const client = new Client( 'Your Region specific Shyft gRPC URL', 'Shyft gRPC Access Token', undefined, );
Now we can proceed to obtain our data from the blockchain.
Our goal is to stream specific data from the Solana blockchain using gRPC. To achieve this, we will utilize
client: Client: This parameter is used to subscribe to on-chain events and establish a connection to the Solana blockchain.
arg: SubscriberRequest: This parameter specifies the data that we want to fetch from the blockchain.
Join Medium for free to get updates from this writer.
Using the handleStream() function, we can efficiently and conveniently stream real-time on-chain data on the Solana blockchain
interface SubscribeRequest { accounts: { [key: string]: SubscribeRequestFilterAccounts }; slots: { [key: string]: SubscribeRequestFilterSlots }; transactions: { [key: string]: SubscribeRequestFilterTransactions }; transactionsStatus: { [key: string]: SubscribeRequestFilterTransactions }; blocks: { [key: string]: SubscribeRequestFilterBlocks }; blocksMeta: { [key: string]: SubscribeRequestFilterBlocksMeta }; entry: { [key: string]: SubscribeRequestFilterEntry }; commitment?: CommitmentLevel | undefined; accountsDataSlice: SubscribeRequestAccountsDataSlice[]; ping?: SubscribeRequestPing | undefined; }
async function handleStream(client: Client, args: SubscribeRequest) { const stream = await client.subscribe(); const streamClosed = new Promise<void>((resolve, reject) => { stream.on("error", (error) => { console.log("ERROR", error); reject(error); stream.end(); }); stream.on("end", () => { resolve(); }); stream.on("close", () => { resolve(); }); }); stream.on("data", async (data) => { try{ console.log(data); }catch(error){ if(error){ console.log(error) } }}); await new Promise<void>((resolve, reject) => { stream.write(args, (err: any) => { if (err === null || err === undefined) { resolve(); } else { reject(err); } }); }).catch((reason) => { console.error(reason); throw reason; }); await streamClosed; }
To fetch token transactions on Moonshot from the Solana blockchain, we need to define a SubscriberRequest object that specifies the parameters for our data request. This object should include the following information:
Here is an example of what the SubscriberRequest object for fetching token transactions on Moonshot might look like:
const req = { accounts: {}, slots: {}, transactions: { moonshot: { vote: false, failed: false, signature: undefined, accountInclude: ['MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG'], //Address MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG accountExclude: [], accountRequired: [], }, }, transactionsStatus: {}, entry: {}, blocks: {}, blocksMeta: {}, accountsDataSlice: [], ping: undefined, commitment: CommitmentLevel.CONFIRMED, //for receiving confirmed txn updates };
The string 'MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG' represents the on-chain address for the Moonshot platform on the Solana blockchain. To stream the real-time on-chain data for this address, we need to define the SubscribeCommand function. This function will be used to specify the parameters for our data request and establish a connection to the Solana blockchain’s streaming endpoint. The SubscribeCommand function should accept a SubscriberRequest object as its parameter, and use it to subscribe to the desired data stream on the Solana blockchain.
async function subscribeCommand(client: Client, args: SubscribeRequest) { while (true) { try { await handleStream(client, args); } catch (error) { console.error("Stream error, restarting in 1 second...", error); await new Promise((resolve) => setTimeout(resolve, 1000)); } } }
and call it
subscribeCommand(client, req);
our output should look like this
{ filters: [ 'moonshot' ], account: undefined, slot: undefined, transaction: { transaction: { signature: <Buffer 18 f2 41 ca 0d c3 2d 24 a3 4c 25 0b 0f d5 e0 bd fd 75 35 a1 ed a3 43 8a f8 fc 86 c0 01 08 70 32 f9 ac 53 3c 46 e8 e2 62 22 4c 79 ab 3f f5 4b d5 81 0c ... 14 more bytes>, isVote: false, transaction: [Object], meta: [Object], index: '315' }, slot: '283417510' }, block: undefined, ping: undefined, pong: undefined, blockMeta: undefined, entry: undefined}
We have successfully requested the data from the Solana blockchain, but the output is not yet in a format that is easy to understand. To make the data clearer, we need to deserialize it. This process involves converting the raw data into a format that can be easily read and interpreted by our application. Once the data has been deserialized, we can access the specific information we are interested in and use it as needed.
To transform the data obtained from the Solana blockchain’s streaming endpoint into a more understandable and usable format, we need to de-serialize our data. Here is an example of how you could write a function to deserialize the data:
import base58 from "bs58";export function decodeTransact(data){ const output = data?base58.encode(Buffer.from(data,'base64')):""; return output;}
The function decodes any Buffered data to more usable format.
After deserializing the data received from the Solana blockchain’s streaming endpoint, we may want to format the output to make it more readable and usable. This could involve organizing the data into an object structure and adding additional properties or methods as needed
export function tOutPut(data){ const dataTx = data?data?.transaction?.transaction:null; const signature = decodeTransact(dataTx?.signature); const message = dataTx?.transaction?.message const header = message?.header; const accountKeys = message?.accountKeys.map((t)=>{ return decodeTransact(t) }) const recentBlockhash = decodeTransact(message?.recentBlockhash); const instructions = message?.instructions const meta = dataTx?.meta return { signature, message:{ header, accountKeys, recentBlockhash, instructions }, meta }}
We recode our handleStream()function to fit into the updated codes
async function handleStream(client: Client, args: SubscribeRequest) { const stream = await client.subscribe(); const streamClosed = new Promise<void>((resolve, reject) => { stream.on("error", (error) => { console.log("ERROR", error); reject(error); stream.end(); }); stream.on("end", () => { resolve(); }); stream.on("close", () => { resolve(); }); }); stream.on("data", async (data) => { try{ const result = await tOutPut(data); console.log(result.signature !==''?result:''); }catch(error){ if(error){ console.log(error) } }}); await new Promise<void>((resolve, reject) => { stream.write(args, (err: any) => { if (err === null || err === undefined) { resolve(); } else { reject(err); } }); }).catch((reason) => { console.error(reason); throw reason; }); await streamClosed; }
Finally, we make our call with the SubscribeCommand Function
subscribeCommand(client, req);
The output should look like this
{ signature: 'F3ase8GKiytLqoMiPBL4sptPuuaYPW3fpwGJ6r7dV2uxmogHC6TFbQdzyvVb77YXgxvtCwuYv38UFSqXAsCsMFJ', message: { header: { numRequiredSignatures: 1, numReadonlySignedAccounts: 0, numReadonlyUnsignedAccounts: 7 }, accountKeys: [ '97g5tRgnKM7d7EZf2W9o3NxGMPksXJoLDTUAycocAQuK', '4QkvjiH6sHvTCJXsZiXamCC2ysP1bfVfdtVC9ja5hfaE', 'mvVnB62M7aUsNppDVA9betcTqir9te2YzVEjNXjyghX', 'CQYpAhWmJFSCPgZJBYVN3mV4VKr37X2geU3nTewsYbeW', '3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N', '5K5RtTWzzLp4P8Npi84ocf7F1vBsAu29N1irG4iiUnzt', 'ComputeBudget111111111111111111111111111111', 'MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG', 'BLnMt8KrxBtG1mXAjSgba2Nuodj3kCVzbQYFSP4bybHN', '36Eru7v11oU5Pfrojyn5oY3nETA1a1iqsw2WUu6afkM9', 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL', '11111111111111111111111111111111' ], recentBlockhash: 'ABwgGgy713Z6czNDJFQSK2ubiCeTAz8pDCn57KgnpPrP', instructions: [ [Object], [Object] ] }, meta: { err: undefined, fee: '25000', preBalances: [ '344615897', '2039280', '9963175231', '2039280', '8323215952497', '259293084532', '1', '1141440', '1461600', '3695760', '934087680', '731913600', '1' ], postBalances: [ '346044889', '2039280', '9961706553', '2039280', '8323215964245', '259293087470', '1', '1141440', '1461600', '3695760', '934087680', '731913600', '1' ], innerInstructions: [ [Object] ], innerInstructionsNone: false, logMessages: [ 'Program ComputeBudget111111111111111111111111111111 invoke [1]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG invoke [1]', 'Program log: Instruction: Sell', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6147 of 171847 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program log: Transfering collateral from buyer to curve account: 1468678, Helio fee: 2938, Dex fee: 11748', 'Program data: vdt/007mYe7zz7Pm7RoAAAZpFgAAAAAA5C0AAAAAAAB6CwAAAAAAAJEtbwURArYDC4ILPxD2CUa4FmMaMw96vJcSUCkj1DuqK4dqcsjMXQoGm4hX/quBhPtof2NGGMA12sQ53BrrO1WYoPAAAAAAAXiTLEfL8KvuPTDg6RVaqQbRyn4zxend6hy0mdI4TxcuAQUAAAB0cmFkZQ==', 'Program MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG consumed 37734 of 199850 compute units', 'Program MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG success' ], logMessagesNone: false, preTokenBalances: [ [Object], [Object] ], postTokenBalances: [ [Object], [Object] ], rewards: [], loadedWritableAddresses: [], loadedReadonlyAddresses: [], returnData: undefined, returnDataNone: true, computeUnitsConsumed: '37884' }}{ signature: '5YqVBBkFggF3ciFjahP8T5Q2NaGHtp5qHkxPnjBGZePqgvDQRzLPNQ9ayi1W5d7n1wmo6LfnqB5FsyjaDjZrMwk3', message: { header: { numRequiredSignatures: 1, numReadonlySignedAccounts: 0, numReadonlyUnsignedAccounts: 7 }, accountKeys: [ '6kXJBci9Qbfn2rwWKxMYLYHBEGxayaRULn8chKwNrZFC', '2ePzHUn5hXotG3i78tGg3fkSPmKAeC5GHy12zTaPw8a1', 'FTkdej4H2Gp4YhLZWkyzLzH2dNzKdLMryodqRrhHtaY9', 'GgDyDkmpMsr3AkPZ6rbJRHiUtXRpXC8Jg8eAD1ft3Cwb', '3udvfL24waJcLhskRAsStNMoNUvtyXdxrWQz4hgi953N', '5K5RtTWzzLp4P8Npi84ocf7F1vBsAu29N1irG4iiUnzt', 'ComputeBudget111111111111111111111111111111', 'MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG', 'FMV1DqEWrS8VBiPB8qiQYfgyxQwJQqn2HYv1hiwUR3e9', '36Eru7v11oU5Pfrojyn5oY3nETA1a1iqsw2WUu6afkM9', 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL', '11111111111111111111111111111111' ], recentBlockhash: 'S6pLqsRmTNxiynMHUzcEBL6bQcNFumKibxn2h4UM76q', instructions: [ [Object], [Object] ] }, meta: { err: undefined, fee: '6600', preBalances: [ '44012931', '2039280', '13550876129', '2039280', '8323215964245', '259293087470', '1', '1141440', '1461600', '3695760', '934087680', '731913600', '1' ], postBalances: [ '9868250', '2039280', '13584676210', '2039280', '8323216234645', '259293155070', '1', '1141440', '1461600', '3695760', '934087680', '731913600', '1' ], innerInstructions: [ [Object] ], innerInstructionsNone: false, logMessages: [ 'Program ComputeBudget111111111111111111111111111111 invoke [1]', 'Program ComputeBudget111111111111111111111111111111 success', 'Program MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG invoke [1]', 'Program log: Instruction: Buy', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]', 'Program log: Instruction: TransferChecked', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6147 of 165113 compute units', 'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success', 'Program 11111111111111111111111111111111 invoke [2]', 'Program 11111111111111111111111111111111 success', 'Program 11111111111111111111111111111111 invoke [2]', 'Program 11111111111111111111111111111111 success', 'Program 11111111111111111111111111111111 invoke [2]', 'Program 11111111111111111111111111111111 success', 'Program log: Transfering collateral from buyer to curve account: 33800081, Helio fee: 67600, Dex fee: 270400', 'Program data: vdt/007mYe4AmkbpKpIAAJG/AwIAAAAAQCAEAAAAAAAQCAEAAAAAADkLCdQS3JkB1t4NgWtwaSxbggMUEpqXZ5M4amSlpe5mL9DR/cKY7uoGm4hX/quBhPtof2NGGMA12sQ53BrrO1WYoPAAAAAAAVVwql70RJfaHQQqopGcce9nko4PrwdIvkPwzmAU4nTvAAUAAAB0cmFkZQ==', 'Program MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG consumed 50139 of 199850 compute units', 'Program MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG success' ], logMessagesNone: false, preTokenBalances: [ [Object], [Object] ], postTokenBalances: [ [Object], [Object] ], rewards: [], loadedWritableAddresses: [], loadedReadonlyAddresses: [], returnData: undefined, returnDataNone: true, computeUnitsConsumed: '50289' }}
At Shyft, we are excited to introduce an innovative solution for streaming data on the Solana blockchain: gRPC. This technology simplifies the stream of on-chain data, allowing developers to focus on building robust applications that harness the power of the Solana blockchain. With gRPC, developers can enjoy a simpler code base and lower latency. Streaming data on the Solana blockchain using dedicated nodes can be complex and resource-intensive, requiring a larger and more intricate code base. However, with gRPC, this process is simplified, making it easier to build even the most complex projects. We are committed to providing the necessary tools and support to make this process seamless. In summary, gRPC offers several benefits for developers building on the Solana blockchain:
For further assistance, we invite you to join our Discord Server or follow us on Twitter for updates. Our team is here to help you make the most of this innovative technology.
All the code related to this article is available on our GitHub here. Please feel free to clone it and follow along. To know more about data streaming on Solana, checkout our other articles on streaming real-time data on Solana , Real-Time Data Streaming with gRPC: Accounts, Transactions, Blocks , How to stream real-time Pump.fun updates on Solana or Tracking new Pools on Raydium.

In this article you will learn how to implement a reconnect logic for your Solana gRPC streams with replay functionality...
January 24, 2026

Learn how to modify your yellowstone gRPC Subscribe Requests on Solana without stopping your stream or losing data ...
January 24, 2026

Get new token launches on Pump.fun using gRPC ...
January 23, 2026
Get in touch with our discord community and keep up with the latest feature
releases. Get help from our developers who are always here to help you take off.