# QUIC Transaction Submission

Use QUIC when you want to send signed transaction bytes to NextBlock with minimal transport overhead from Go.

The pattern is simple:

1. Dial a regional endpoint such as `frankfurt.nextblock.io:11100`
2. Authenticate once on a bidirectional stream
3. Reuse the same QUIC connection and open one unidirectional stream per transaction

## Example

```go
package main

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"os"
	"time"

	quic "github.com/quic-go/quic-go"
)

const (
	authOK    byte = 0x00
	maxTxSize      = 1232
)

type NextblockQuicClient struct {
	conn quic.Connection
}

func Connect(ctx context.Context, serverAddr string, apiKey string) (*NextblockQuicClient, error) {
	host, _, err := net.SplitHostPort(serverAddr)
	if err != nil {
		return nil, fmt.Errorf("invalid server address: %w", err)
	}

	conn, err := quic.DialAddr(ctx, serverAddr, &tls.Config{
		ServerName: host,
		NextProtos: []string{"nb-tx/1"},
		MinVersion: tls.VersionTLS13,
	}, &quic.Config{
		KeepAlivePeriod: 15 * time.Second,
		MaxIdleTimeout:  60 * time.Second,
	})
	if err != nil {
		return nil, fmt.Errorf("quic dial failed: %w", err)
	}

	authStream, err := conn.OpenStreamSync(ctx)
	if err != nil {
		conn.CloseWithError(1, "auth stream failed")
		return nil, fmt.Errorf("open auth stream: %w", err)
	}

	if _, err := authStream.Write([]byte(apiKey)); err != nil {
		conn.CloseWithError(1, "auth write failed")
		return nil, fmt.Errorf("write api key: %w", err)
	}
	if err := authStream.Close(); err != nil {
		conn.CloseWithError(1, "auth close failed")
		return nil, fmt.Errorf("close auth stream: %w", err)
	}

	response := make([]byte, 1)
	if _, err := io.ReadFull(authStream, response); err != nil {
		conn.CloseWithError(1, "auth read failed")
		return nil, fmt.Errorf("read auth response: %w", err)
	}
	if response[0] != authOK {
		conn.CloseWithError(1, "auth rejected")
		return nil, fmt.Errorf("authentication rejected")
	}

	return &NextblockQuicClient{conn: conn}, nil
}

func (c *NextblockQuicClient) SendTransaction(ctx context.Context, rawTx []byte) error {
	if len(rawTx) > maxTxSize {
		return fmt.Errorf("transaction too large: %d", len(rawTx))
	}

	stream, err := c.conn.OpenUniStreamSync(ctx)
	if err != nil {
		return fmt.Errorf("open tx stream: %w", err)
	}

	if _, err := stream.Write(rawTx); err != nil {
		return fmt.Errorf("write tx bytes: %w", err)
	}

	return stream.Close()
}

func (c *NextblockQuicClient) Close() error {
	return c.conn.CloseWithError(0, "client closing")
}

func main() {
	ctx := context.Background()
	apiKey := os.Getenv("NEXTBLOCK_API_KEY")
	if apiKey == "" {
		panic("set NEXTBLOCK_API_KEY before running")
	}

	client, err := Connect(ctx, "frankfurt.nextblock.io:11100", apiKey)
	if err != nil {
		panic(err)
	}
	defer client.Close()

	// Replace this with the serialized bytes of your signed Solana transaction.
	// Example with solana-go: rawTx, err := tx.MarshalBinary()
	rawTx := []byte{0, 1, 2, 3}

	if err := client.SendTransaction(ctx, rawTx); err != nil {
		panic(err)
	}

	fmt.Println("transaction queued")
}
```

## What To Replace

* Swap `frankfurt.nextblock.io:11100` for your nearest region.
* Replace `rawTx` with the serialized bytes of your signed transaction.
* Keep one `NextblockQuicClient` alive and reuse it instead of reconnecting per send.

## Notes

* Send raw transaction bytes, not base64.
* Each transaction goes on its own unidirectional stream.
* QUIC does not support the extra gRPC submission flags or atomic bundle submission.
* If you already build transactions with `solana-go`, serialize the signed transaction and pass those bytes into `SendTransaction()`.

See [QUIC Transaction Submission](/api/quic-transaction-submission.md) for the full endpoint list and protocol summary.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.nextblock.io/api/examples/golang/quic.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
