# Modèle d’acquisition des transactions Solana ## Décision Le pipeline ne conserve pas un payload complet différent pour chaque fournisseur. Toutes les sources doivent produire une représentation canonique commune avant l’écriture principale. ```text getTransaction JSON-RPC Helius transactionSubscribe JSON Yellowstone gRPC Protobuf logsSubscribe + getTransaction backfill ou réparation | v adaptateur de source dans kb_rpc | v transaction Solana canonique | +--> kb_sol_raw_transactions | +--> kb_sol_obs_transaction_observations ``` ## Transaction canonique `kb_sol_raw_transactions` contient une seule ligne par signature. Le contenu est indépendant du fournisseur et ne doit contenir aucun champ propre à Helius, Triton, Chainstack, Shyft ou un endpoint particulier. Le document canonique doit inclure, quand la source les fournit : - signature ; - slot ; - version de transaction ; - message header ; - static account keys ; - Address Lookup Tables ; - loaded writable et readonly addresses ; - recent blockhash ; - outer instructions ; - inner instructions ; - logs ; - statut et erreur ; - fee et compute units ; - balances SOL avant/après ; - balances SPL avant/après ; - rewards ; - return data ; - block time. Les public keys et signatures restent en base58, avec validation stricte de leur taille décodée : 32 octets pour les clés et blockhashes, 64 octets pour les signatures. Les données binaires d’instruction restent en base64. Les montants SPL conservent le montant entier brut normalisé sans zéros non significatifs, les décimales et une représentation décimale fixe recalculée localement, sans dépendre de `uiAmount` ou `uiAmountString` du fournisseur. Depuis `0.3.2`, ce contrat est matérialisé par `kb_model::CanonicalTransaction` avec `canonical_format_version = 1`. La sérialisation trie récursivement les clés des objets JSON avant calcul d’un hash SHA-256 sur le document compact. Les tableaux conservent l’ordre Solana d’origine. Les détails de source, timestamps locaux et identifiants de subscription sont exclus du document hashé. L’adaptateur HTTP standard demande `encoding = json`, puis convertit explicitement les payloads d’instruction reçus en base58 vers la représentation base64 canonique. Les champs optionnels absents et `null` sont normalisés vers le même état interne afin de stabiliser le hash entre réponses compatibles. ## Observations de source `kb_sol_obs_transaction_observations` décrit comment et quand une transaction a été détectée ou reçue. Elle ne duplique pas la transaction complète. Champs minimaux visés : | Champ | Rôle | |-----------------------|----------------------------------------------------------------------------------------------| | `observation_key` | clé idempotente de l’observation | | `raw_transaction_id` | lien optionnel vers la transaction canonique | | `signature` | signature observée | | `slot` | slot observé quand connu | | `provider` | fournisseur, par exemple `helius`, `triton`, `chainstack`, `shyft` | | `endpoint_code` | endpoint de configuration utilisé | | `protocol` | `solana_http_json_rpc`, `solana_ws_json_rpc`, `helius_ws` ou `yellowstone_grpc` | | `acquisition_method` | `getTransaction`, `transactionSubscribe`, `yellowstone_transactions`, `logs_hydration`, etc. | | `origin` | `live`, `backfill`, `replay`, `repair` ou `migration` | | `commitment` | commitment demandé ou observé | | `capture_session_id` | session ou campagne de capture | | `filter_code` | profil de filtre utilisé | | `detected_at` | réception du premier signal, par exemple le log | | `received_at` | réception de la transaction complète | | `normalized_at` | fin de normalisation canonique | | `persisted_at` | fin d’écriture durable | | `payload_size_bytes` | taille du message source reçu | | `source_payload_hash` | hash optionnel du message source sans le conserver | | `status` | `detected`, `received`, `normalized`, `persisted`, `failed` ou `missing` | | `error_code` | code technique normalisé optionnel | | `error_message` | message de diagnostic optionnel | Les observations permettent de comparer les sources par signature, sans multiplier le volume de stockage transactionnel. ## Notifications WebSocket `kb_sol_raw_ws_notifications` n’est plus une cible durable. Une notification `logsSubscribe` peut être conservée temporairement en mémoire jusqu’à l’hydratation de la transaction. La base conserve ensuite seulement : - les timestamps utiles ; - la signature et le slot ; - la source et la méthode ; - la taille ; - le statut de l’hydratation ; - le lien éventuel vers la transaction canonique. Les notifications non transactionnelles comme `slotSubscribe`, `accountSubscribe` ou `programSubscribe` pourront avoir des tables métier dédiées seulement si un besoin durable apparaît. Elles ne doivent pas être entassées dans une table générique de payloads WebSocket. ## Fusion multi-source Pour une signature déjà présente : 1. normaliser le nouveau message ; 2. calculer le hash canonique ; 3. ajouter l’observation de source ; 4. si le hash est identique, ne pas dupliquer la transaction ; 5. si la nouvelle source apporte uniquement des champs auparavant absents, appliquer un enrichissement déterministe ; 6. si des champs incompatibles diffèrent, ne pas écraser silencieusement et enregistrer un conflit technique. Les différences liées au commitment ou à la disponibilité progressive des metadata doivent être distinguées d’un conflit réel. ## Raw provider facultatif Un payload fournisseur complet peut être exporté de façon bornée pour une campagne de diagnostic, par exemple en NDJSON ou Protobuf, mais il ne fait pas partie du stockage PostgreSQL transactionnel normal. Ces exports doivent être : - explicitement activés ; - limités en durée et en volume ; - associés à une session de capture ; - supprimables sans affecter les replays métier. ## Frontières des crates - `kb_rpc` contient les transports et adaptateurs HTTP, WebSocket Helius et Yellowstone gRPC. - `kb_model` contient le contrat canonique source-indépendant. - `kb_store_core` contient les DTOs et repositories de transactions et observations. - `kb_store_pg` contient la migration, les queries et les repositories PostgreSQL. - les décodeurs ne dépendent jamais de la source d’acquisition.