QUIC Transaction Submission
Use QUIC when you already have signed Solana transaction bytes and want a low-overhead submission path from Node.js or TypeScript.
This example uses @matrixai/quic and follows the same flow as the other languages:
Connect to a regional endpoint with ALPN
nb-tx/1Authenticate once with your API key on a bidirectional stream
Reuse the connection and send each transaction on a unidirectional stream
Example
import { randomFillSync } from "node:crypto";
import { QUICClient } from "@matrixai/quic";
const AUTH_OK = 0x00;
const MAX_TX_SIZE = 1232;
class NextblockQuicClient {
constructor(private readonly client: QUICClient) {}
static async connect(serverAddr: string, apiKey: string): Promise<NextblockQuicClient> {
const [host, portString] = serverAddr.split(":");
const port = Number(portString);
const client = await QUICClient.createQUICClient({
host,
port,
serverName: host,
crypto: {
ops: {
async randomBytes(data: ArrayBuffer): Promise<void> {
randomFillSync(new Uint8Array(data));
},
},
},
config: {
applicationProtos: ["nb-tx/1"],
maxIdleTimeout: 60_000,
keepAliveIntervalTime: 15_000,
},
});
const authStream = client.connection.newStream("bidi");
const authWriter = authStream.writable.getWriter();
await authWriter.write(new TextEncoder().encode(apiKey));
await authWriter.close();
const authReader = authStream.readable.getReader();
const { value } = await authReader.read();
if (!value || value[0] !== AUTH_OK) {
await client.destroy();
throw new Error("Authentication rejected");
}
return new NextblockQuicClient(client);
}
async sendTransaction(rawTx: Uint8Array): Promise<void> {
if (rawTx.byteLength > MAX_TX_SIZE) {
throw new Error(`Transaction too large: ${rawTx.byteLength}`);
}
const txStream = this.client.connection.newStream("uni");
const txWriter = txStream.writable.getWriter();
await txWriter.write(rawTx);
await txWriter.close();
}
async close(): Promise<void> {
await this.client.destroy();
}
}
async function main(): Promise<void> {
const apiKey = process.env.NEXTBLOCK_API_KEY;
if (!apiKey) {
throw new Error("Set NEXTBLOCK_API_KEY before running");
}
const client = await NextblockQuicClient.connect(
"amsterdam.nextblock.io:11100",
apiKey,
);
try {
// Replace this with the serialized bytes of your signed transaction.
// Example with @solana/web3.js: const rawTx = signedTransaction.serialize();
const rawTx = new Uint8Array([0, 1, 2, 3]);
await client.sendTransaction(rawTx);
console.log("transaction queued");
} finally {
await client.close();
}
}
void main();What To Replace
Replace
amsterdam.nextblock.io:11100with the region closest to you.Read the API key from
NEXTBLOCK_API_KEYor your own config source.Replace
rawTxwith the serialized bytes of your signed transaction.
Notes
Send raw transaction bytes, not base64.
Reuse one
NextblockQuicClientfor many sends.QUIC does not support the extra gRPC submission flags or atomic bundle submission.
If you build transactions with
@solana/web3.js, serialize the signed transaction first and pass the resultingUint8ArrayintosendTransaction().
See QUIC Transaction Submission for the full endpoint list and protocol summary.
Last updated