> For the complete documentation index, see [llms.txt](https://raiden-4.gitbook.io/raiden.wtf.docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://raiden-4.gitbook.io/raiden.wtf.docs/bolt-low-latency/quic-submission.md).

# QUIC

Lowest-latency transaction submission via QUIC protocol

Send raw transaction bytes over QUIC — the fastest path with microsecond-level latency, zero JSON overhead, and fire-and-forget delivery.

{% hint style="info" %}
QUIC access is currently **on-demand only**. Contact the Raiden team to get it enabled on your account.
{% endhint %}

## Protocol Specification

| Property                   | Value                                                   |
| -------------------------- | ------------------------------------------------------- |
| **Transport**              | QUIC (UDP port 4433)                                    |
| **ALPN**                   | `raiden-tpu`                                            |
| **Auth**                   | ed25519 TLS client certificate derived from API key     |
| **Stream type**            | Unidirectional (client → server)                        |
| **Max concurrent streams** | 64 per connection                                       |
| **Max payload**            | 4096 bytes (Transaction v1); 1232 bytes for legacy / v0 |
| **Delivery**               | Fire-and-forget (no response)                           |
| **0-RTT**                  | Supported (session resumption)                          |
| **Idle timeout**           | 30 seconds                                              |

## How It Works

```
1. Connect to {region}.bolt.raiden.wtf:4433 with your derived certificate
2. Open a unidirectional stream
3. Write raw transaction bytes (max 4096 for Transaction v1, 1232 for legacy / v0)
4. Close the stream
5. Repeat from step 2 for more transactions
```

The connection stays open — reuse it for all transactions. Each transaction gets its own stream.

## Authentication

QUIC uses certificate-based auth. See [QUIC Authentication](/raiden.wtf.docs/bolt-low-latency/authentication/quic-authentication.md) for the full derivation process.

**Summary:** `SHA-256(api_key)` → ed25519 seed → keypair → self-signed X.509 certificate → presented as TLS client cert.

## Examples

{% tabs %}
{% tab title="Go" %}

```go
import (
    "crypto/ed25519"
    "crypto/sha256"
    "crypto/tls"
    "crypto/x509"
    "crypto/rand"
    "math/big"
    "time"

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

// Derive certificate from API key
hash := sha256.Sum256([]byte(apiKey))
priv := ed25519.NewKeyFromSeed(hash[:])
pub := priv.Public().(ed25519.PublicKey)
template := &x509.Certificate{
    SerialNumber: big.NewInt(1),
    NotBefore:    time.Now().Add(-time.Hour),
    NotAfter:     time.Now().Add(365 * 24 * time.Hour),
    KeyUsage:     x509.KeyUsageDigitalSignature,
}
certDER, _ := x509.CreateCertificate(rand.Reader, template, template, pub, priv)
cert := tls.Certificate{Certificate: [][]byte{certDER}, PrivateKey: priv}

// Connect
conn, err := quic.DialAddr(ctx, "fra.bolt.raiden.wtf:4433",
    &tls.Config{
        Certificates:       []tls.Certificate{cert},
        InsecureSkipVerify: true,
        NextProtos:         []string{"raiden-tpu"},
    },
    &quic.Config{
        MaxIdleTimeout:  30 * time.Second,
        KeepAlivePeriod: 25 * time.Second,
    },
)

// Send transactions
stream, _ := conn.OpenUniStream()
stream.Write(txBytes) // raw serialized transaction
stream.Close()
```

{% endtab %}

{% tab title="Rust" %}

```rust
use ed25519_dalek::{SecretKey, SigningKey};
use quinn::{ClientConfig, Endpoint};
use sha2::{Sha256, Digest};

// Derive keypair from API key
let seed = Sha256::digest(api_key.as_bytes());
let signing_key = SigningKey::from_bytes(&seed.into());

// Build TLS config with derived certificate
// (use rcgen to create self-signed cert from signing_key)
let client_config = ClientConfig::new(/* ... */);

let endpoint = Endpoint::client("0.0.0.0:0".parse()?)?;
let connection = endpoint
    .connect_with(client_config, "fra.bolt.raiden.wtf:4433".parse()?, "raiden-tpu")?
    .await?;

// Send transaction
let mut stream = connection.open_uni().await?;
stream.write_all(&tx_bytes).await?;
stream.finish().await?;
```

{% endtab %}

{% tab title="JavaScript" %}

```javascript
import { createHash } from 'crypto';
import * as ed25519 from '@noble/ed25519';

// Derive keypair from API key
const seed = createHash('sha256').update(apiKey).digest();
const privateKey = seed;
const publicKey = await ed25519.getPublicKeyAsync(privateKey);

// Connect via QUIC (library-specific)
// ALPN: "raiden-tpu"
// Port: 4433/UDP

// Send transaction
const stream = await connection.openUniStream();
stream.write(txBytes);
stream.close();
```

{% endtab %}
{% endtabs %}

## Connection Lifecycle

1. **Connect once** — QUIC handshake includes authentication. No separate auth step.
2. **Reuse connection** — Open new streams for each transaction on the same connection.
3. **0-RTT resumption** — Subsequent connections skip the full handshake.
4. **Keep-alive** — Set a keep-alive period (recommended: 25 seconds) to prevent idle disconnection.

## Error Handling

QUIC is fire-and-forget — no per-transaction error responses. The connection itself may be closed by the server:

| Error Code | Meaning                                      |
| ---------- | -------------------------------------------- |
| `0`        | Normal close                                 |
| `1`        | Unknown certificate — API key not recognized |
| `3`        | QUIC access not enabled on account           |
| `4`        | Account suspended or banned                  |

Oversized payloads (>4096 bytes) and empty streams are silently dropped. A legacy or v0 transaction over 1232 bytes is dropped as well — only Transaction v1 can exceed that size.
