TypeScript Example
Prerequisites
bun add @grpc/grpc-js @grpc/proto-loader
bun add @solana/addresses @solana/codec-strings @solana/kit
# Generate the TX Stream proto client from the proto specification
# In the example repo, dependencies and the generated .ts files are already availableExample
import * as grpc from "@grpc/grpc-js";
import {
NextStreamNotification,
NextStreamServiceClient,
} from "./protos/stream";
import { NextStreamSubscription } from "./protos/stream";
import {
createKeyPairFromBytes,
getAddressFromPublicKey,
getBase58Decoder,
getBase58Encoder,
getCompiledTransactionMessageDecoder,
getSignatureFromTransaction,
getTransactionDecoder,
getUtf8Encoder,
signBytes,
type Address,
} from "@solana/kit";
/*
the auth message needs to be built clients-side.
it is a pipe-separated string made of:
1. domain that's being connected to (e.g. fra.stream.nextblock.io)
2. the publickey that sent the fee to strmuYvHKeA1qvHqooUpwUk2BFwaAmMbK9WXY9mh2GJ
3. a random nonce
4. the current unix timestamp
it is then signed by the supplied publickey.
*/
function buildAuthMessage(
domain: string,
authenticatedPublicKey: Address
): string {
const nonce = Math.random().toString();
const ts = Math.floor(Date.now());
return `${domain}|${authenticatedPublicKey.toString()}|${nonce}|${ts}`;
}
async function main() {
const domain = "fra.stream.nextblock.io:22221";
const authenticationPrivateKey = await createKeyPairFromBytes(
getBase58Encoder().encode("YOUR_PRIVATE_KEY")
);
const authenticationPublicKey = await getAddressFromPublicKey(
authenticationPrivateKey.publicKey
);
const authenticationMessage = buildAuthMessage(
domain,
authenticationPublicKey
);
const authenticationSignature = await signBytes(
authenticationPrivateKey.privateKey,
getUtf8Encoder().encode(authenticationMessage)
);
const client = new NextStreamServiceClient(
domain,
grpc.credentials.createInsecure(),
{
"grpc.keepalive_time_ms": 5000,
"grpc.keepalive_timeout_ms": 1000,
"grpc.keepalive_permit_without_calls": 0,
}
);
const req: NextStreamSubscription = {
authenticationPublickey: authenticationPublicKey.toString(),
authenticationMessage,
authenticationSignature: getBase58Decoder().decode(
authenticationSignature
),
accounts: ["675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"],
};
const stream = client.subscribeNextStream(req);
stream.on("data", (msg: NextStreamNotification) => {
try {
const packet = msg.packet;
if (!packet) {
return;
}
const txBytes = packet.transaction;
const slot = packet.slot;
const tx = getTransactionDecoder().decode(txBytes);
const message = getCompiledTransactionMessageDecoder().decode(
tx.messageBytes
);
console.log(
`got new sig ${getSignatureFromTransaction(
tx
).toString()} on slot ${slot}`
);
} catch (e) {
console.error("error processing message:", e);
}
});
stream.on("error", (err) => {
console.error("error receiving from sub:", err);
process.exit(1);
});
stream.on("end", () => {
console.error("stream ended");
process.exit(0);
});
}
main();Usage Examples
Available Endpoints
Best Practices for Trading Bots
Last updated