📥 Dekodierte Transaktionen empfangen

Ein Decoded Shred Stream wird per UDP an die IP und den Port gepusht, die Sie auf der Stream-Detailseite konfigurieren — keine Verbindung, kein Handshake, keine erneute Übertragung. Jedes Datagramm ist höchstens 1.408 Bytes groß und trägt genau eine vollständige Transaktion: einen binären 16-Byte-Header, den Slot und die Transaktion im Standard-Wire-Format von Solana.


📦 Der Umschlag-Header (16 Bytes)

Jedes Datagramm beginnt mit demselben festen Header. Alle Ganzzahlen sind Little-Endian.

OffsetGrößeFeldWert / Semantik
02magic0x5AE7 (u16 LE) — alles andere ⇒ Datagramm verwerfen
21version2 — jede unbekannte Version verwerfen
31msg_type2 = DecodedTx (1 = reserviert)
41flagsReserviert, 0
51frag_indexFragment-Index, 0-basiert — bei dekodierten Transaktionen immer 0
61frag_countFragment-Anzahl — bei dekodierten Transaktionen immer 1 (nie fragmentiert)
71_pad0
88sequ64 LE, monoton steigend pro Produkt-Stream

đź§ľ Der Payload (msg_type = 2)

Direkt nach dem Header:

FeldGrößeSemantik
slot8u64 LE — der Slot, zu dem die Transaktion gehört
transactionRest des DatagrammsStandard-Wire-Format von Solana (bincode-serialisierte VersionedTransaction)

Die Transaktionsbytes sind exakt das, was jedes Solana-SDK erwartet — geben Sie sie unverändert an Ihren bestehenden Parser. Dekodierte Transaktionen werden nie fragmentiert: ein Datagramm ist immer eine ganze Transaktion. Vote-Transaktionen sind standardmäßig ausgeschlossen.


📉 Verluste mit seq erkennen

UDP überträgt nicht erneut. Jedes Datagramm trägt eine seq, die innerhalb Ihres Streams um genau 1 steigt. Eine Lücke in seq bedeutet, dass entsprechend viele Datagramme verloren gingen:

text
seq empfangen = 1042, vorherige = 1039 → 2 Datagramme verloren

Merken Sie sich die zuletzt gesehene seq und alarmieren Sie bei Lücken — anhaltender Verlust bedeutet in der Regel, dass Ihr Empfangspuffer zu klein ist (siehe unten) oder ein Engpass auf dem Netzwerkpfad vorliegt.


🔧 Puffergröße

Der Stream ist bursty — ein aktiver Slot kann viele Transaktionen direkt hintereinander liefern. Geben Sie dem Socket einen großzügigen Empfangspuffer (SO_RCVBUF, mehrere Megabyte), damit kurze Bursts absorbiert statt vom Kernel verworfen werden, und leeren Sie den Socket aus einer dedizierten, schnellen Schleife.


đź’» Minimaler Decoder (Python)

python
import socket, struct
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8 * 1024 * 1024)
sock.bind(("0.0.0.0", 5000)) # der Port, den Sie im Dashboard konfiguriert haben
expected = None
while True:
dgram = sock.recv(2048)
if len(dgram) < 24:
continue
magic, version, msg_type = struct.unpack_from("<HBB", dgram, 0)
if magic != 0x5AE7 or version != 2:
continue # unbekannte magic/version verwerfen
if msg_type != 2:
continue # kein DecodedTx-Datagramm
(seq,) = struct.unpack_from("<Q", dgram, 8)
if expected is not None and seq != expected:
print(f"gap: {seq - expected} datagram(s) lost")
expected = seq + 1
(slot,) = struct.unpack_from("<Q", dgram, 16)
tx_bytes = dgram[24:] # Standard-Wire-Format — an jeden Solana-Tx-Parser übergeben

🦀 Offizielle Clients und das codec-Modul

Sie müssen den obigen Decoder nicht selbst schreiben: Die offiziellen decoded-shredstream-Clients (Rust, Go, JavaScript/TypeScript, Python) binden den Port, parsen den Umschlag, erkennen seq-Lücken und übergeben Ihnen jede Transaktion mit ihrem Slot und ihren Signaturen — über UDP und gRPC, mit derselben API.

rust
// cargo add decoded-shredstream tokio --features tokio/macros,tokio/rt-multi-thread
use decoded_shredstream::{UdpClient, UdpConfig};
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut client = UdpClient::bind(UdpConfig { port: 8002, ..Default::default() })?;
while let Some(update) = client.next_update().await {
println!("slot={} sig={} {}B", update.slot(), update.signature(), update.bytes().len());
}
Ok(())
}

Das Rust-Crate stellt das Framing außerdem als codec-Modul bereit (Konstanten FRAME_MAGIC = 0x5AE7, FRAME_VERSION = 2, FRAME_HEADER_LEN = 16, MAX_DATAGRAM = 1408, parse_header, StreamDecoder), sodass Sie Datagramme dekodieren können, die Sie von einem selbst verwalteten Socket lesen — der Go-Client bietet dasselbe über ParseHeader / StreamDecoder:

rust
use std::net::UdpSocket;
use decoded_shredstream::codec::{Push, StreamDecoder, MAX_DATAGRAM};
fn decode_loop(socket: &UdpSocket) -> std::io::Result<()> {
let mut decoder = StreamDecoder::new();
let mut buf = [0u8; MAX_DATAGRAM + 64];
loop {
let n = socket.recv(&mut buf)?;
if let Push::Update(update) = decoder.push(&buf[..n]) {
println!("slot={} sig={}", update.slot(), update.signature());
}
}
}

Installation: npm install decoded-shredstream · pip install decoded-shredstream · cargo add decoded-shredstream · go get github.com/shredstream/decoded-shredstream-go.


➡️ Nächste Schritte

  • Decoded Shred Stream — Einordnung, Latenz und Auslieferungsmodi.
  • gRPC-Zustellung — dieselben Transaktionen ĂĽber einen geordneten Stream mit serverseitiger Kontenfilterung.
Dekodierte Transaktionen empfangen — Docs | ShredStream.com