📥 Recevoir les transactions décodées

Un Decoded Shred Stream est poussé en UDP vers l'IP et le port que vous configurez depuis la page de détail du stream — pas de connexion, pas de handshake, pas de retransmission. Chaque datagramme fait au plus 1 408 octets et porte une transaction complète : un header binaire de 16 octets, le slot, puis la transaction au format wire Solana standard.


📦 Le header de l'enveloppe (16 octets)

Chaque datagramme commence par le mĂŞme header fixe. Tous les entiers sont en little-endian.

OffsetTailleChampValeur / sémantique
02magic0x5AE7 (u16 LE) — toute autre valeur ⇒ rejeter le datagramme
21version2 — rejeter toute version inconnue
31msg_type2 = DecodedTx (1 = réservé)
41flagsRéservé, 0
51frag_indexIndex du fragment, base 0 — toujours 0 pour les transactions décodées
61frag_countNombre de fragments — toujours 1 pour les transactions décodées (jamais fragmentées)
71_pad0
88sequ64 LE, strictement croissant par flux produit

đź§ľ Le payload (msg_type = 2)

Immédiatement après le header :

ChampTailleSémantique
slot8u64 LE — le slot auquel appartient la transaction
transactionreste du datagrammeFormat wire Solana standard (VersionedTransaction sérialisée en bincode)

Les octets de la transaction sont exactement ce qu'attend n'importe quel SDK Solana — donnez-les tels quels à votre parser existant. Les transactions décodées ne sont jamais fragmentées : un datagramme est toujours une transaction entière. Les transactions de vote sont exclues par défaut.


📉 Détecter la perte avec seq

L'UDP ne retransmet pas. Chaque datagramme porte un seq qui augmente d'exactement 1 au sein de votre stream. Un trou dans seq signifie autant de datagrammes perdus :

text
seq reçu = 1042, précédent = 1039 → 2 datagrammes perdus

Gardez trace du dernier seq observé et alertez sur les trous — une perte soutenue signifie généralement un buffer de réception trop petit (ci-dessous) ou un goulot d'étranglement sur le chemin.


đź”§ Dimensionner le buffer

Le flux est en rafales — un slot chargé peut livrer de nombreuses transactions coup sur coup. Donnez au socket un buffer de réception généreux (SO_RCVBUF, plusieurs mégaoctets) pour absorber les rafales courtes au lieu de les laisser jeter par le noyau, et videz le socket depuis une boucle dédiée et rapide.


💻 Décodeur minimal (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)) # le port configuré sur votre dashboard
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 # rejeter magic/version inconnus
if msg_type != 2:
continue # pas un datagramme DecodedTx
(seq,) = struct.unpack_from("<Q", dgram, 8)
if expected is not None and seq != expected:
print(f"trou : {seq - expected} datagramme(s) perdu(s)")
expected = seq + 1
(slot,) = struct.unpack_from("<Q", dgram, 16)
tx_bytes = dgram[24:] # format wire standard — pour n'importe quel parser Solana

🦀 Clients officiels et le module codec

Vous n'avez pas à écrire le décodeur ci-dessus : les clients officiels decoded-shredstream (Rust, Go, JavaScript/TypeScript, Python) ouvrent le port en écoute, parsent l'enveloppe, détectent les trous de seq et vous livrent chaque transaction avec son slot et ses signatures — en UDP et en gRPC, avec la même 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(())
}

Le crate Rust expose aussi le framing sous la forme du module codec (constantes FRAME_MAGIC = 0x5AE7, FRAME_VERSION = 2, FRAME_HEADER_LEN = 16, MAX_DATAGRAM = 1408, parse_header, StreamDecoder), pour décoder des datagrammes lus sur un socket que vous gérez vous-même — le client Go expose la même chose via 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.


➡️ Prochaines étapes

  • Decoded Shred Stream — positionnement, latence et modes de livraison.
  • Livraison gRPC — les mĂŞmes transactions sur un flux ordonnĂ©, avec filtrage des comptes cĂ´tĂ© serveur.
Recevoir les transactions décodées — Docs | ShredStream.com