0.1.0
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
<!-- file: docs/ACCOUNT_ONLY_CANDIDATES.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Candidats qui ne sont pas encore des program_id
|
||||
|
||||
Certains identifiants vus dans les explorateurs peuvent être des comptes de configuration, de pool, d'autorité, de registre, de vault ou de PDA. Ils ne doivent pas devenir des crates de décodeur tant que leur owner executable n'est pas prouvé.
|
||||
|
||||
## Bags
|
||||
|
||||
| Identifiant | Source supposée | Statut | Décision |
|
||||
|------------------------------------------------|-----------------|-----------------------|-----------------------------------------------------------------------------------------------------------|
|
||||
| `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` | Bags | `account_not_program` | Ne pas créer `kb_decoder_bags` comme surface canonique tant que le vrai `program_id` n'est pas identifié. |
|
||||
|
||||
## Règle
|
||||
|
||||
Avant de créer une crate de décodeur :
|
||||
|
||||
1. vérifier que l'adresse est bien un programme exécutable ;
|
||||
2. vérifier l'owner et les transactions qui l'invoquent ;
|
||||
3. rattacher les comptes non exécutables à leur `program_id` propriétaire ;
|
||||
4. documenter les comptes importants dans une matrice séparée du registre des programmes.
|
||||
|
||||
## Bags Fee Share
|
||||
|
||||
Les programmes Fee Share V1/V2 sont maintenant documentés dans `docs/BAGS_FM.md`. Le compte `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` reste un compte observé, pas une surface exécutable canonique.
|
||||
210
migration/khadhroony-bot2-reference/docs/AGAVE_LOCAL_NODE.md
Normal file
210
migration/khadhroony-bot2-reference/docs/AGAVE_LOCAL_NODE.md
Normal file
@@ -0,0 +1,210 @@
|
||||
<!-- file: docs/AGAVE_LOCAL_NODE.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Nœud Agave local expérimental
|
||||
|
||||
Ce document cadre le jalon `0.3.x`. L'objectif est de mesurer si un nœud Agave local non votant peut devenir une source temps réel utile avant de construire l'ingestion, le backfill et l'extracteur raw vers core en `0.4.x`.
|
||||
|
||||
## Positionnement
|
||||
|
||||
`khadhroony-bot2` ne doit pas embarquer un validateur. Le validateur reste un process externe démarré manuellement ou par script de développement.
|
||||
|
||||
```text
|
||||
agave-validator local non votant
|
||||
-> HTTP RPC local
|
||||
-> WebSocket RPC local
|
||||
-> demo_ws ou future démo Agave
|
||||
-> mesures de capacité et de retard
|
||||
-> décision pour l'ingestion 0.4.x
|
||||
```
|
||||
|
||||
Le jalon `0.3.x` est une phase de recherche. Il ne doit pas écrire de raw de production, ne doit pas extraire vers core et ne doit pas déclencher d'achat/revente.
|
||||
|
||||
## Hypothèse à tester
|
||||
|
||||
Un nœud local Agave non votant avec ledger pruné pourrait permettre :
|
||||
|
||||
- `blockSubscribe` local avec transactions complètes ;
|
||||
- moins de `getTransaction` temps réel ;
|
||||
- moins de listeners par programme ;
|
||||
- filtrage local par program id, logs, instructions, balances et discriminators ;
|
||||
- usage des providers externes surtout pour backfill historique, réparation et `sendTransaction`.
|
||||
|
||||
Cette hypothèse doit être mesurée. Elle ne doit pas devenir une décision d'architecture sans chiffres locaux.
|
||||
|
||||
## Commande de départ
|
||||
|
||||
La commande exacte dépend de la version Agave installée, du snapshot disponible, des ports libres et des ressources locales. La commande de départ à documenter et ajuster est :
|
||||
|
||||
```bash
|
||||
agave-validator \
|
||||
--no-voting \
|
||||
--full-rpc-api \
|
||||
--enable-rpc-transaction-history \
|
||||
--rpc-pubsub-enable-block-subscription \
|
||||
--limit-ledger-size <N_SHREDS> \
|
||||
--ledger ./data/agave-ledger \
|
||||
--accounts ./data/agave-accounts \
|
||||
--rpc-port 8899 \
|
||||
--dynamic-port-range 8000-8020
|
||||
```
|
||||
|
||||
`<N_SHREDS>` est une limite de shreds, pas une durée exacte ni un nombre exact de blocks. La fenêtre réellement conservée dépend du débit réseau, de la purge, des snapshots, de la vitesse disque et de l'état local du nœud.
|
||||
|
||||
## Préparation locale
|
||||
|
||||
Créer les dossiers explicitement pour éviter de mélanger le ledger expérimental avec d'autres données :
|
||||
|
||||
```bash
|
||||
mkdir -p ./data/agave-ledger ./data/agave-accounts ./logs/agave_local_research
|
||||
```
|
||||
|
||||
Avant chaque essai, noter :
|
||||
|
||||
```text
|
||||
version agave-validator
|
||||
commande exacte
|
||||
valeur N_SHREDS
|
||||
heure de lancement
|
||||
état initial du dossier ledger
|
||||
état initial du dossier accounts
|
||||
profil de configuration utilisé
|
||||
endpoint externe de référence
|
||||
```
|
||||
|
||||
Ne pas supprimer automatiquement `./data/agave-ledger` ou `./data/agave-accounts` dans le projet. Toute purge doit rester une action manuelle et documentée dans le compte rendu d'essai.
|
||||
|
||||
## Profil de configuration
|
||||
|
||||
`config/example.config.json` contient le profil `agave_local_research`, non actif par défaut.
|
||||
|
||||
```text
|
||||
HTTP local: http://127.0.0.1:8899
|
||||
WS local: ws://127.0.0.1:8900
|
||||
```
|
||||
|
||||
Pour l'utiliser, changer `active_profile` dans un fichier local non versionné ou lancer l'application avec `KB_CONFIG_PATH` pointant vers une copie locale.
|
||||
|
||||
Le profil expose les rôles suivants :
|
||||
|
||||
| Rôle | Usage |
|
||||
|----------------------|-----------------------------------------------------------|
|
||||
| `local_node_status` | santé, version et retard du nœud local |
|
||||
| `http_queries` | lectures RPC légères locales |
|
||||
| `http_heavy` | `getBlock`, `getTransaction`, `getProgramAccounts` locaux |
|
||||
| `block_stream` | probe `blockSubscribe` local |
|
||||
| `slot_notifications` | `slotSubscribe`, `slotsUpdatesSubscribe`, `rootSubscribe` |
|
||||
| `logs_subscribe` | `logsSubscribe` par mention de programme |
|
||||
| `program_subscribe` | `programSubscribe` par programme |
|
||||
| `account_subscribe` | `accountSubscribe` par compte précis |
|
||||
|
||||
Le fallback externe est volontairement séparé. Il ne doit pas masquer un échec local pendant les mesures.
|
||||
|
||||
## Probes minimaux
|
||||
|
||||
Les probes `0.3.2` devront couvrir au minimum :
|
||||
|
||||
| Famille | Méthode | Résultat attendu |
|
||||
|---------|-------------------------------------------------|--------------------------------------------------|
|
||||
| HTTP | `getVersion` | version du nœud ou erreur explicite |
|
||||
| HTTP | `getHealth` | état du nœud ou erreur explicite |
|
||||
| HTTP | `getSlot` | slot local |
|
||||
| HTTP | `getBlockHeight` | block height local |
|
||||
| HTTP | `getMaxShredInsertSlot` | slot maximal de shred inséré |
|
||||
| WS | `blockSubscribe` avec `transactionDetails=none` | `supported`, `unsupported`, `timeout` ou `error` |
|
||||
| WS | `slotSubscribe` | subscription id puis notifications |
|
||||
| WS | `slotsUpdatesSubscribe` | subscription id puis notifications |
|
||||
| WS | `rootSubscribe` | subscription id puis notifications |
|
||||
| WS | `logsSubscribe` `mentions` | subscription id ou refus provider |
|
||||
| WS | `programSubscribe` | subscription id ou refus provider |
|
||||
| WS | `accountSubscribe` | subscription id ou refus provider |
|
||||
|
||||
Le probe minimal `blockSubscribe` ne doit pas laisser un flux ouvert :
|
||||
|
||||
```text
|
||||
1. ouvrir la WebSocket ;
|
||||
2. envoyer blockSubscribe avec transactionDetails=none ;
|
||||
3. attendre un subscription id ou une erreur ;
|
||||
4. désabonner immédiatement ;
|
||||
5. classer supported / unsupported / timeout / error ;
|
||||
6. journaliser la réponse brute tronquée si nécessaire.
|
||||
```
|
||||
|
||||
Paramètres de départ à tester dans `demo_ws` :
|
||||
|
||||
```text
|
||||
role: block_stream
|
||||
method: blockSubscribe
|
||||
filterJson: "all"
|
||||
configJson: {"commitment":"confirmed","encoding":"json","transactionDetails":"none","showRewards":false}
|
||||
```
|
||||
|
||||
Si `"all"` est refusé par la version RPC testée, documenter l'erreur et tester ensuite un filtre plus ciblé par programme dans `0.3.4`.
|
||||
|
||||
## Mesures à collecter
|
||||
|
||||
| Mesure | Commande ou source indicative | Rôle |
|
||||
|-----------------------------|----------------------------------------------|-----------------------------------|
|
||||
| taille ledger | `du -sh ./data/agave-ledger` | calibrer `--limit-ledger-size` |
|
||||
| taille accounts | `du -sh ./data/agave-accounts` | vérifier le coût disque |
|
||||
| CPU | `pidstat`, `top`, `htop` | mesurer le coût du nœud local |
|
||||
| RAM | `pidstat`, `free`, `ps` | vérifier la faisabilité locale |
|
||||
| réseau RX/TX | `nload`, `iftop`, `/proc/net/dev` | mesurer le coût mainnet |
|
||||
| slot local | `getSlot` local | vérifier l'avancement |
|
||||
| slot externe | `getSlot` provider de référence | calculer le retard |
|
||||
| lag slots | slot externe moins slot local | détecter un décrochage |
|
||||
| lag secondes | lag slots multiplié par estimation slot time | estimer l'impact trading |
|
||||
| débit WebSocket | compte notifications par seconde | comparer les modes live |
|
||||
| taille moyenne notification | taille texte JSON ou bytes WS | dimensionner raw_ws_notifications |
|
||||
| reconnects | logs `kb_app_demo.demo_ws` et nœud | détecter l'instabilité |
|
||||
| erreurs provider ou Agave | logs et réponses JSON-RPC | classer les limites |
|
||||
|
||||
La mesure de lag secondes reste approximative tant que le slot time local n'est pas calculé sur fenêtre glissante. Le chiffre utile pour la décision reste le couple `lag slots` plus `lag secondes estimées`.
|
||||
|
||||
## Erreurs à documenter sans les masquer
|
||||
|
||||
| Catégorie | Exemples à noter |
|
||||
|------------|---------------------------------------------------------------------------------------------|
|
||||
| snapshot | téléchargement lent, snapshot incompatible, absence de snapshot |
|
||||
| ledger | corruption, purge trop agressive, croissance disque excessive |
|
||||
| accounts | croissance inattendue, I/O saturée, chemin invalide |
|
||||
| ports | `8899` occupé, port WS indisponible, `dynamic-port-range` trop étroit |
|
||||
| ressources | CPU saturé, RAM insuffisante, disque saturé, réseau insuffisant |
|
||||
| retard | slot local qui n'avance plus, lag croissant, root très en retard |
|
||||
| RPC | méthode non supportée, timeouts, erreurs JSON-RPC |
|
||||
| WebSocket | connexion refusée, fermeture serveur, subscription refusée, notifications trop volumineuses |
|
||||
|
||||
Un essai qui échoue est exploitable s'il indique précisément la commande, le contexte, l'erreur et la ressource bloquante.
|
||||
|
||||
## Table de résultats à remplir
|
||||
|
||||
| Date | Agave | N_SHREDS | durée | ledger | accounts | CPU moy/max | RAM moy/max | RX/TX | slot local | slot réf. | lag slots | lag sec. | blockSubscribe none | remarques |
|
||||
|-----------|-----------|-----------|-----------|-----------|-----------|-------------|-------------|-----------|------------|-----------|-----------|-----------|---------------------|-----------|
|
||||
| À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir |
|
||||
|
||||
## Critères de décision pour `0.4.x`
|
||||
|
||||
Le nœud local peut être retenu comme source live principale uniquement si les conditions suivantes sont mesurées :
|
||||
|
||||
- le slot local suit l'endpoint externe avec un retard stable et acceptable ;
|
||||
- le ledger et les accounts restent soutenables sur le disque local ;
|
||||
- `blockSubscribe` est supporté et stable avec au moins `transactionDetails=none` ;
|
||||
- le mode `full` ou `signatures` ne bloque pas la WebView ni le process Rust lorsqu'il sera testé ;
|
||||
- les reconnects et timeouts restent rares ou récupérables ;
|
||||
- un fallback externe reste disponible pour backfill, réparation et envoi de transaction.
|
||||
|
||||
Si une de ces conditions échoue, `0.4.x` doit privilégier `slotSubscribe + getBlock`, les listeners ciblés ou une stratégie hybride, sans dépendance obligatoire à Agave local.
|
||||
|
||||
## Sortie attendue de `0.3.x`
|
||||
|
||||
La fin de `0.3.x` doit produire une décision documentée :
|
||||
|
||||
```text
|
||||
source temps réel principale retenue
|
||||
fallback temps réel retenu
|
||||
politique raw_ws_notifications
|
||||
faisabilité du nœud local
|
||||
limites ressources observées
|
||||
prochain design de l'ingestion 0.4.x
|
||||
```
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<!-- file: docs/APPLICATION_CRATES.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Crates applicatifs
|
||||
|
||||
Les crates applicatifs sont les points d'entrée exécutables ou Tauri du workspace. Ils orchestrent les crates internes mais ne portent pas la logique métier profonde.
|
||||
|
||||
## Nomenclature
|
||||
|
||||
- Les applications ponctuelles ou interactives utilisent le préfixe `kb_app_`.
|
||||
- `kb_app_cli` contient les commandes de contrôle, import, replay et diagnostic.
|
||||
- `kb_app_demo` contient la démonstration Tauri.
|
||||
- `kb_worker` est l'exception autorisée, car il représente un processus de travail long-running.
|
||||
|
||||
## Responsabilités autorisées
|
||||
|
||||
- Lire la configuration.
|
||||
- Initialiser `kb_logging`.
|
||||
- Appeler les APIs publiques de `kb_pipeline`.
|
||||
- Afficher ou exposer les résultats.
|
||||
- Retourner des codes de sortie ou événements UI.
|
||||
|
||||
## Responsabilités interdites
|
||||
|
||||
- Décoder directement une instruction DEX.
|
||||
- Écrire directement dans PostgreSQL sans passer par les traits de stockage.
|
||||
- Réimplémenter les règles de matérialisation.
|
||||
- Charger ou manipuler des secrets wallet hors `kb_wallet`.
|
||||
108
migration/khadhroony-bot2-reference/docs/ARCHITECTURE.md
Normal file
108
migration/khadhroony-bot2-reference/docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,108 @@
|
||||
<!-- file: docs/ARCHITECTURE.md -->
|
||||
<!-- version: 7 -->
|
||||
|
||||
# Architecture
|
||||
|
||||
L'architecture cible est organisée en couches strictes afin d'éviter de recréer un monolithe. Les noms `raw`, `core`, `obs`, `decode`, `mat`, `agg` et `ops` désignent des couches logiques, pas des schémas PostgreSQL applicatifs.
|
||||
|
||||
## Chaîne principale
|
||||
|
||||
```text
|
||||
sources RPC HTTP / WebSocket / gRPC
|
||||
-> kb_rpc
|
||||
-> kb_model transaction canonique
|
||||
-> raw
|
||||
-> core
|
||||
-> obs
|
||||
-> decode
|
||||
-> mat
|
||||
-> agg
|
||||
-> strategy
|
||||
-> execution
|
||||
|
||||
sources historiques déjà indexées
|
||||
-> kb_external_sources
|
||||
-> candidats de signatures
|
||||
-> kb_pipeline
|
||||
-> hydratation canonique via kb_rpc
|
||||
```
|
||||
|
||||
## Responsabilités
|
||||
|
||||
- `kb_rpc` gère les méthodes et flux RPC Solana : JSON-RPC HTTP, WebSocket, Helius/LaserStream et Yellowstone gRPC lorsque ces surfaces seront activées.
|
||||
- `kb_external_sources` est une crate future réservée aux APIs REST, exports et imports historiques externes, par exemple Solscan Pro ou un CSV officiel. Elle ne produit jamais directement une transaction canonique et ne doit pas être confondue avec un futur index interne PostgreSQL.
|
||||
- `kb_pipeline` combine la découverte de signatures par `kb_rpc` ou `kb_external_sources` avec l’hydratation canonique par `kb_rpc`.
|
||||
- `kb_model` définit la transaction Solana canonique indépendante du fournisseur.
|
||||
- `raw` conserve une transaction canonique unique et rejouable par signature.
|
||||
- `obs` conserve les observations techniques de source, programme, instruction et discriminator.
|
||||
- `core` expose les structures Solana normalisées pour les décodeurs.
|
||||
- `decode` contient les événements protocolairement compris.
|
||||
- `mat` contient les projections métier.
|
||||
- `agg` contient les agrégations temporelles ou analytiques.
|
||||
- `ops` trace les modules, versions et traitements.
|
||||
|
||||
## Acquisition multi-source
|
||||
|
||||
```text
|
||||
getTransaction JSON-RPC
|
||||
transactionSubscribe Helius
|
||||
Yellowstone gRPC
|
||||
logsSubscribe + hydration
|
||||
|
|
||||
v
|
||||
transaction canonique identique
|
||||
```
|
||||
|
||||
Les détails de fournisseur restent dans `kb_sol_obs_transaction_observations`. Ils ne contaminent pas les décodeurs ni les tables core.
|
||||
|
||||
`kb_rpc` ne doit pas être renommée en `kb_com` ou `kb_transport` pour accueillir des sources historiques externes. Ces noms seraient trop génériques et mélangeraient RPC Solana, APIs REST indexées, imports CSV et orchestration métier. Le nom `kb_external_sources` évite la confusion avec l’indexation locale et décrit explicitement une frontière de fournisseurs externes plutôt qu’une simple action de récupération. Si des primitives HTTP réellement communes apparaissent plus tard, elles pourront être extraites dans une crate technique dédiée sans modifier la frontière fonctionnelle entre `kb_rpc` et `kb_external_sources`.
|
||||
|
||||
## Convention DB associée
|
||||
|
||||
Quand une couche logique devient une table Solana PostgreSQL, elle est encodée dans le nom de table :
|
||||
|
||||
```text
|
||||
kb_sol_<domain>_<name>
|
||||
```
|
||||
|
||||
Exemples :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions
|
||||
kb_sol_obs_transaction_observations
|
||||
kb_sol_core_transactions
|
||||
kb_sol_obs_program_observations
|
||||
kb_sol_decode_decoded_events
|
||||
kb_sol_mat_trade_events
|
||||
kb_sol_ops_processing_ledger
|
||||
```
|
||||
|
||||
Les formes qualifiées héritées, écrites ici avec `DOT` (`raw DOT sol_transactions`, `core DOT sol_instructions`, `obs DOT program_observations`), sont interdites.
|
||||
|
||||
## Frontières interdites
|
||||
|
||||
- Un décodeur ne dépend pas du store concret.
|
||||
- Un décodeur ne dépend pas du fournisseur ou du transport d’acquisition.
|
||||
- Un matérialisateur ne dépend pas du RPC.
|
||||
- Le wallet ne dépend pas des décodeurs.
|
||||
- L'application de démonstration ne doit pas contourner les APIs de pipeline.
|
||||
- Une observation de source ne doit pas dupliquer le payload canonique complet.
|
||||
|
||||
## Frontière extraction canonique vers core
|
||||
|
||||
Depuis `0.3.4`, `kb_pipeline` contient un extracteur pur qui dépend de `kb_model` et des contrats `kb_store_core`, mais pas de PostgreSQL ni de Tauri. `kb_store_pg` implémente la transaction atomique et `kb_app_demo` ne fait qu’orchestrer la requête opérateur.
|
||||
|
||||
```text
|
||||
kb_model::CanonicalTransaction
|
||||
|
|
||||
v
|
||||
kb_pipeline::core_extraction
|
||||
| CoreExtractionBundle
|
||||
v
|
||||
kb_store_core::CoreExtractionStore
|
||||
|
|
||||
v
|
||||
kb_store_pg::PostgresStore
|
||||
```
|
||||
|
||||
Les futurs décodeurs consommeront les inputs core contextualisés ; ils ne reliront pas directement le JSON canonique pour reconstruire les comptes, CPI, logs ou balances.
|
||||
145
migration/khadhroony-bot2-reference/docs/BACKFILL_HTTP.md
Normal file
145
migration/khadhroony-bot2-reference/docs/BACKFILL_HTTP.md
Normal file
@@ -0,0 +1,145 @@
|
||||
<!-- file: docs/BACKFILL_HTTP.md -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Backfill HTTP transactionnel
|
||||
|
||||
## Objectif
|
||||
|
||||
Le jalon `0.3.3` fournit un moteur de backfill réutilisable qui transforme les réponses HTTP Solana standard en transactions canoniques indépendantes du fournisseur.
|
||||
|
||||
```text
|
||||
signatures explicites
|
||||
ou getSignaturesForAddress
|
||||
-> getTransaction
|
||||
-> kb_model::CanonicalTransaction
|
||||
-> kb_sol_raw_transactions
|
||||
-> kb_sol_obs_transaction_observations
|
||||
```
|
||||
|
||||
Aucun décodeur DEX, aucune extraction core et aucune source temps réel payante ne sont exécutés dans ce jalon.
|
||||
|
||||
## Sources de signatures
|
||||
|
||||
Le moteur accepte quatre catégories :
|
||||
|
||||
- liste explicite de signatures, une signature par ligne ;
|
||||
- historique d’un `program_id` ;
|
||||
- historique d’un mint de token ;
|
||||
- historique d’une adresse de pool.
|
||||
|
||||
Les trois modes par adresse utilisent la même méthode standard `getSignaturesForAddress`. La catégorie est conservée dans le `filter_code` des observations afin de distinguer `program_before`, `program_after`, `program_latest`, et les équivalents token/pool.
|
||||
|
||||
## Sens de pagination
|
||||
|
||||
### Avant une signature
|
||||
|
||||
Avec une ancre, le paramètre RPC `before` sélectionne les transactions plus anciennes que cette signature. Sans ancre, le mode `Before` omet `before` et commence sur la page la plus récente retournée par le RPC. Le moteur poursuit la pagination jusqu’à atteindre la limite demandée, une page incomplète, la limite de pages ou une demande d’arrêt.
|
||||
|
||||
### Après une signature
|
||||
|
||||
Le moteur transmet directement la signature d’ancrage dans le paramètre RPC `until`. Les pages suivantes combinent le même `until` avec un curseur `before` correspondant à la dernière signature de la page précédente.
|
||||
|
||||
Le mode `after` utilise des pages RPC de 1 000 signatures afin que la limite par défaut de 20 pages puisse couvrir jusqu’à 20 000 signatures plus récentes que l’ancre. Le moteur ne conserve que les `X` candidates les plus proches de l’ancre, en plus de l’ensemble de déduplication borné par `max_pages × 1 000`.
|
||||
|
||||
Une page incomplète signifie que la borne `until` a été atteinte. Si toutes les pages autorisées sont pleines, la campagne échoue avec le nombre de signatures inspectées et demande d’augmenter `max_pages`. Une erreur RPC de borne inconnue est propagée sans être transformée en résultat vide.
|
||||
|
||||
## Limites et retries
|
||||
|
||||
Le moteur combine :
|
||||
|
||||
- les limites saisies par l’opérateur ;
|
||||
- `requests_per_second` du rôle sélectionné ;
|
||||
- `max_concurrent_requests` du rôle sélectionné ;
|
||||
- `pause_after_rate_limit_ms` après une erreur 429 ;
|
||||
- un backoff borné pour les autres erreurs temporaires.
|
||||
|
||||
Le rôle HTTP doit supporter à la fois :
|
||||
|
||||
```text
|
||||
get_signatures_for_address
|
||||
get_transaction
|
||||
```
|
||||
|
||||
Le rôle recommandé reste `history_backfill`.
|
||||
|
||||
## Persistance
|
||||
|
||||
Une signature déjà présente dans `kb_sol_raw_transactions` est ignorée avant l’appel `getTransaction`.
|
||||
|
||||
Chaque tentative d’hydratation produit une observation légère :
|
||||
|
||||
- provider et endpoint ;
|
||||
- protocole `solana_http_json_rpc` ;
|
||||
- méthode `getTransaction` ;
|
||||
- origine `backfill` ;
|
||||
- commitment, session et filtre ;
|
||||
- timestamps ;
|
||||
- taille et hash du payload source lorsque disponibles ;
|
||||
- statut `persisted`, `missing` ou `failed` ;
|
||||
- erreur normalisée lorsque nécessaire.
|
||||
|
||||
Le payload source complet n’est jamais dupliqué dans la table d’observations.
|
||||
|
||||
## Démo Tauri et arrêt coopératif
|
||||
|
||||
La fenêtre `demo_backfill` regroupe dans des accordéons Bootstrap 5 :
|
||||
|
||||
1. paramètres communs ;
|
||||
2. signatures explicites ;
|
||||
3. programme ;
|
||||
4. token ;
|
||||
5. pool ;
|
||||
6. journal et résumé JSON.
|
||||
|
||||
Une seule campagne peut fonctionner à la fois. Le bouton `Arrêter` pose un drapeau d’annulation coopérative lu entre les pages, les candidats, le pacer et les retries.
|
||||
|
||||
Depuis la préversion de clôture `0.3.4-pre.011`, l’annulation ne se limite plus aux frontières entre appels : les futures RPC `getSignaturesForAddress` et `getTransaction` sont mises en concurrence avec un observateur d’arrêt, l’attente du pacer est annulable et une pause de retry, y compris après un 429, est interrompue par sondage borné. L’abandon de la future HTTP empêche une campagne arrêtée d’attendre un timeout réseau complet.
|
||||
|
||||
Le moteur maintient une file bornée par la concurrence effective et cesse d’admettre de nouveaux candidats dès que l’arrêt est demandé. Les candidats non démarrés ne sont ni journalisés comme traités, ni inclus dans `candidates_completed`.
|
||||
|
||||
Le résumé distingue :
|
||||
|
||||
- `candidates_selected` : candidats découverts ;
|
||||
- `candidates_started` : candidats admis dans la file d’exécution ;
|
||||
- `candidates_completed` : candidats arrivés à un résultat terminal ;
|
||||
- `candidates_cancelled` : candidats démarrés puis interrompus avant un résultat terminal ;
|
||||
- `candidates_not_started` : candidats jamais admis après la demande d’arrêt.
|
||||
|
||||
Les invariants attendus sont :
|
||||
|
||||
```text
|
||||
candidates_selected = candidates_started + candidates_not_started
|
||||
candidates_started = candidates_completed + candidates_cancelled
|
||||
```
|
||||
|
||||
Pour une campagne `before`, `resume_before_signature` correspond à la dernière signature terminée dans un préfixe contigu de la liste ordonnée. Une transaction terminée hors ordre ne fait pas avancer seule ce curseur. Si aucun candidat n’a terminé, le curseur reste la signature d’ancrage initiale. Cette règle interdit de sauter les candidats non traités lors d’une reprise.
|
||||
|
||||
Le calcul de cette frontière est couvert par les tests unitaires. Une campagne réelle de 500 candidats a déjà validé l’arrêt et la séparation entre terminés, annulés et non démarrés. Le rejeu manuel depuis `resume_before_signature` reste un contrôle opératoire recommandé, mais il ne constitue plus un jalon séparé ni un blocage pour `0.4.0`.
|
||||
|
||||
## Validation finale `0.3.3`
|
||||
|
||||
Validations locales :
|
||||
|
||||
```text
|
||||
cargo test -p kb_rpc : 54 tests passés
|
||||
cargo test -p kb_pipeline : 10 tests passés
|
||||
cargo test -p kb_store_core: 31 tests passés
|
||||
cargo test -p kb_store_pg : 32 tests passés
|
||||
cargo test -p kb_app_demo : 38 tests passés
|
||||
cargo clippy --all-targets : validé
|
||||
```
|
||||
|
||||
Les campagnes Tauri ont validé :
|
||||
|
||||
- signatures explicites ;
|
||||
- programme avant/après ;
|
||||
- token avant/après ;
|
||||
- pool avant/après ;
|
||||
- parcours profond `program_after` avec 5 516 signatures indexées parcourues pour 5 transactions hydratées ;
|
||||
- arrêt d’une campagne de 500 candidats avec 7 démarrés, 3 terminés, 4 annulés et 493 non démarrés.
|
||||
|
||||
Les diagnostics PostgreSQL ont confirmé 62 transactions canoniques et 62 observations avant les derniers tests d’arrêt. Les tables core restent volontairement vides jusqu’à `0.3.4`.
|
||||
|
||||
## Recherche latest sans ancre
|
||||
|
||||
Pour programme, token et pool, une direction `before` avec ancre absente produit un filtre `*_latest`. La première page est demandée sans `before`, puis les pages suivantes utilisent normalement la dernière signature reçue comme curseur. Une direction `after` sans ancre est refusée, car la borne `until` ne peut pas être déterminée. En cas d’arrêt avant le premier candidat terminé, une campagne `*_latest` reprend depuis la page la plus récente et ne fabrique aucun curseur.
|
||||
26
migration/khadhroony-bot2-reference/docs/BAGS_FM.md
Normal file
26
migration/khadhroony-bot2-reference/docs/BAGS_FM.md
Normal file
@@ -0,0 +1,26 @@
|
||||
<!-- file: docs/BAGS_FM.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Bags.fm
|
||||
|
||||
## Décision de classification
|
||||
|
||||
`BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` reste classé comme compte observé et non comme `program_id` exécutable prouvé.
|
||||
|
||||
En revanche, la documentation publique Bags liste deux programmes Fee Share :
|
||||
|
||||
| Surface canonique | Program id | Statut |
|
||||
|--------------------------|------------------------------------------------|---------|
|
||||
| `fees_bags_fee_share_v1` | `FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi` | legacy |
|
||||
| `fees_bags_fee_share_v2` | `FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK` | current |
|
||||
|
||||
## Crates réservés
|
||||
|
||||
- `kb_decoder_fees_bags_fee_share_v1`
|
||||
- `kb_executor_fees_bags_fee_share_v1`
|
||||
- `kb_decoder_fees_bags_fee_share_v2`
|
||||
- `kb_executor_fees_bags_fee_share_v2`
|
||||
|
||||
## Rôle dans le trading assisté
|
||||
|
||||
Ces surfaces ne sont pas des DEX directs. Elles peuvent cependant être utiles pour détecter des configurations de frais, claims, partenaires, vaults de frais ou comportements liés à des tokens lancés via Bags.
|
||||
41
migration/khadhroony-bot2-reference/docs/CODE_REUSE.md
Normal file
41
migration/khadhroony-bot2-reference/docs/CODE_REUSE.md
Normal file
@@ -0,0 +1,41 @@
|
||||
<!-- file: docs/CODE_REUSE.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Réutilisation de code
|
||||
|
||||
Ce document liste les composants de l'ancien workspace qui peuvent servir de référence pour le nouveau workspace. Le code ne doit pas être copié mécaniquement : chaque portion reprise doit être adaptée aux nouvelles frontières de crates, aux règles de nommage, aux règles `crate::` et au backend PostgreSQL.
|
||||
|
||||
## Composants candidats
|
||||
|
||||
| Composant | Cible dans le nouveau workspace | Décision provisoire |
|
||||
|--------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Client RPC HTTP | `kb_rpc` | Réutiliser les idées : retry, backoff, limites, traces, idempotence. Réécrire l'API selon les nouveaux modèles. |
|
||||
| Pool RPC HTTP | `kb_rpc` | Reprendre le principe de sélection d'endpoint et de contrôle de débit. Isoler les métriques et les erreurs dans `kb_core`. |
|
||||
| Client WebSocket | `kb_rpc` | Reprendre la gestion ping/pong, subscribe/unsubscribe, shutdown explicite et reconnexion contrôlée. |
|
||||
| Pool WebSocket | `kb_rpc` | Garder l'idée de pool, mais éviter toute dépendance vers les décodeurs ou le stockage concret. |
|
||||
| Ledger de replay | `kb_pipeline` et `kb_store_core` | Réécrire en versionné par module, version, scope et input hash. |
|
||||
| Validation SQL | `validation_sql/` | Porter seulement les contrôles encore utiles : doublons de signatures, events failed, non-swap vers trade, coverage résiduelle. |
|
||||
| Découvertes de discriminators | `docs/DECODER_SURFACE_AUDIT.md`, `idls/`, futurs catalogues | Conserver comme corpus de décision, pas comme support automatique. |
|
||||
| Matérialisations trade/liquidity/lifecycle | `kb_materializer_*` | Reprendre les règles validées, mais séparer strictement decode et materialize. |
|
||||
|
||||
## Interdictions
|
||||
|
||||
- Ne pas importer directement l'ancien schéma SQLite dans les nouveaux décodeurs.
|
||||
- Ne pas faire dépendre `kb_rpc` de `kb_store_pg`.
|
||||
- Ne pas faire dépendre un décodeur de `kb_pipeline`.
|
||||
- Ne pas conserver une fonction monolithique de replay global.
|
||||
|
||||
## Critère d'acceptation
|
||||
|
||||
Une portion reprise est acceptée seulement si elle respecte les règles suivantes :
|
||||
|
||||
- responsabilité limitée à un crate cible ;
|
||||
- documentation de code en anglais ;
|
||||
- absence de `use` hors traits nécessaires ;
|
||||
- erreurs typées via `kb_core` ;
|
||||
- tracing target stable ;
|
||||
- tests unitaires ou validation de build.
|
||||
|
||||
## Interfaces officielles actuelles
|
||||
|
||||
La réutilisation prioritaire ne concerne pas uniquement l’ancien code. Les enums, types, IDs et encodeurs publiés dans les interfaces officielles Solana/SPL doivent être utilisés lorsqu’ils exposent un contrat compatible avec les règles du workspace. La matrice, les exceptions `bincode` et l’ordre `wincode`/Borsh/parser borné sont maintenus dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md`.
|
||||
114
migration/khadhroony-bot2-reference/docs/CONFIGURATION.md
Normal file
114
migration/khadhroony-bot2-reference/docs/CONFIGURATION.md
Normal file
@@ -0,0 +1,114 @@
|
||||
<!-- file: docs/CONFIGURATION.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Configuration
|
||||
|
||||
La configuration sert de contrat commun entre les applications Tauri, les workers, les tests locaux, les stores, le RPC, le wallet et les exécuteurs.
|
||||
|
||||
## Fichier principal
|
||||
|
||||
```text
|
||||
config/example.config.json
|
||||
```
|
||||
|
||||
Ce fichier ne contient pas de commentaires, car JSON ne le permet pas.
|
||||
|
||||
## Profils
|
||||
|
||||
La configuration racine contient plusieurs profils et un seul profil actif :
|
||||
|
||||
```json
|
||||
{
|
||||
"active_profile": "local_devnet",
|
||||
"profiles": []
|
||||
}
|
||||
```
|
||||
|
||||
Les profils fournis sont `local_devnet`, `mainnet_research` et `mainnet`.
|
||||
|
||||
## Logging
|
||||
|
||||
Chaque sortie définit son nom, activation, sink, niveau, chemin, rotation, format, ANSI et liste de targets. Toute crate opérationnelle disposant d'un target `tracing` canonique doit recevoir ses fichiers `debug.log`, `info.log` et `error.jsonl` dédiés dans chaque profil.
|
||||
|
||||
`kb_wallet` devient une crate opérationnelle en `0.4.2-pre.003`; ses logs ne contiennent jamais de secret ou d'octets de keypair.
|
||||
|
||||
## Endpoints HTTP et WS
|
||||
|
||||
Le modèle principal conserve les endpoints HTTP JSON-RPC et WebSocket Solana standards. Les rôles permettent de sélectionner un endpoint selon le type de requête et d'appliquer débit, burst, concurrence, subscriptions et pause 429.
|
||||
|
||||
Les transports gRPC, Helius enrichi et LaserStream restent planifiés dans leurs jalons dédiés.
|
||||
|
||||
## URL et clés API
|
||||
|
||||
Une clé API doit rester sous forme de placeholder dans l'URL :
|
||||
|
||||
```text
|
||||
https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}
|
||||
```
|
||||
|
||||
Aucune clé réelle ne doit être committée.
|
||||
|
||||
## Wallet
|
||||
|
||||
Le bloc `wallet` contient :
|
||||
|
||||
```text
|
||||
wallet_dir
|
||||
temporary_wallet_enabled
|
||||
temporary_wallet_alias
|
||||
temporary_wallet_persist
|
||||
cluster
|
||||
localnet_send_enabled
|
||||
devnet_send_enabled
|
||||
testnet_send_enabled
|
||||
mainnet_send_enabled
|
||||
```
|
||||
|
||||
Le profil local devnet persiste le wallet sous :
|
||||
|
||||
```text
|
||||
wallets/temporary/local_devnet/local-devnet-operator.json
|
||||
```
|
||||
|
||||
Cette persistance est nécessaire pour conserver la même adresse entre plusieurs sessions, recevoir un airdrop et signer une séquence de tests. Le fichier contient un keypair Solana standard non chiffré : il est exclu du dépôt, protégé par permissions privées sur Unix et réservé au laboratoire local/devnet.
|
||||
|
||||
La validation refuse :
|
||||
|
||||
- un alias vide, trop long ou contenant un séparateur de chemin ;
|
||||
- `temporary_wallet_persist = true` lorsque le wallet temporaire est désactivé ;
|
||||
- un wallet temporaire actif sur `mainnet-beta`.
|
||||
|
||||
## Exécution
|
||||
|
||||
Le bloc `execution` contient :
|
||||
|
||||
```text
|
||||
dry_run_default
|
||||
require_simulation
|
||||
require_operator_confirmation
|
||||
localnet_max_spend_lamports
|
||||
devnet_max_spend_lamports
|
||||
testnet_max_spend_lamports
|
||||
mainnet_max_spend_lamports
|
||||
max_fee_lamports
|
||||
max_compute_unit_price_micro_lamports
|
||||
recent_blockhash_max_age_slots
|
||||
send_max_retries
|
||||
confirmation_poll_interval_ms
|
||||
confirmation_max_attempts
|
||||
devnet_airdrop_max_lamports
|
||||
```
|
||||
|
||||
Règles :
|
||||
|
||||
- toute dépense configurée exige la simulation ;
|
||||
- tout envoi activé exige un plafond de dépense positif pour le cluster correspondant ;
|
||||
- mainnet exige la confirmation opérateur ;
|
||||
- le plafond de frais et l'âge maximal du blockhash doivent être strictement positifs ;
|
||||
- l’intervalle et le nombre de polls de confirmation sont strictement bornés ;
|
||||
- le plafond d’airdrop Devnet doit rester nul dans les profils non Devnet ;
|
||||
- `dry_run_default` reste actif dans tous les profils fournis.
|
||||
|
||||
## Règle de sécurité
|
||||
|
||||
Aucun secret ne doit être écrit dans le dépôt, les logs, PostgreSQL ou un payload Tauri. Les futurs backends chiffrés ou hardware wallet resteront confinés dans `kb_wallet` et exposeront uniquement une interface de signature.
|
||||
@@ -0,0 +1,148 @@
|
||||
<!-- file: docs/CORE_EXTRACTION_CONTRACTS.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Contrats d’extraction core Solana
|
||||
|
||||
## État de clôture `0.3.4-pre.011`
|
||||
|
||||
Le jalon transforme une transaction canonique déjà persistée en graphe core normalisé et rejouable :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions.canonical_json
|
||||
-> kb_model::CanonicalTransaction
|
||||
-> validation version/signature/slot/hash
|
||||
-> kb_pipeline::extract_raw_transaction_to_core
|
||||
-> CoreExtractionBundle
|
||||
-> transaction PostgreSQL atomique
|
||||
```
|
||||
|
||||
Le payload fournisseur contenu à l’origine dans une réponse RPC n’est jamais relu depuis les observations. La seule source fonctionnelle de l’extracteur est le document canonique de `kb_sol_raw_transactions`.
|
||||
|
||||
## Unité atomique
|
||||
|
||||
Une signature produit un `CoreExtractionBundle` contenant :
|
||||
|
||||
- une transaction core liée par `raw_transaction_id` ;
|
||||
- les account keys statiques et chargées par ALT ;
|
||||
- les instructions top-level ;
|
||||
- les inner instructions ;
|
||||
- les logs ordonnés ;
|
||||
- les changements de balances natifs et token ;
|
||||
- l’identité du processor dans le ledger.
|
||||
|
||||
Le repository PostgreSQL supprime puis recrée le graphe de la signature dans une transaction unique. Un échec avant le commit laisse intact l’état précédemment validé. Le test PostgreSQL de clôture provoque volontairement une violation d’unicité après insertion de la transaction et d’une première account key, puis vérifie l’absence totale de graphe partiel, de changement raw et de succès ledger.
|
||||
|
||||
## Validation de l’entrée
|
||||
|
||||
L’extraction refuse explicitement :
|
||||
|
||||
- un `canonical_json` absent ;
|
||||
- un `canonical_json_hash` absent ;
|
||||
- une version différente de `CANONICAL_TRANSACTION_FORMAT_VERSION` ;
|
||||
- un JSON non désérialisable en `CanonicalTransaction` ;
|
||||
- une signature ou un slot différent de la ligne raw ;
|
||||
- un hash recalculé différent du hash stocké ;
|
||||
- un indice de programme ou de compte hors de l’espace résolu.
|
||||
|
||||
L’identité du ledger est :
|
||||
|
||||
```text
|
||||
stage = core_extraction
|
||||
processor_name = canonical_to_core
|
||||
processor_version = 1
|
||||
input_key = signature
|
||||
input_hash = canonical_json_hash
|
||||
```
|
||||
|
||||
## Account keys
|
||||
|
||||
L’espace d’indices est construit strictement dans cet ordre :
|
||||
|
||||
```text
|
||||
static account keys
|
||||
loaded writable addresses
|
||||
loaded readonly addresses
|
||||
```
|
||||
|
||||
Pour les comptes statiques, les flags `signer` et `writable` sont dérivés du header Solana. Les loaded writable sont non-signers et writable. Les loaded readonly sont non-signers et readonly. `executable` reste `NULL`, car cette information n’est pas contenue de manière fiable dans le contrat canonique actuel.
|
||||
|
||||
## Instructions
|
||||
|
||||
Les chemins sont déterministes :
|
||||
|
||||
```text
|
||||
0
|
||||
1
|
||||
2
|
||||
0/0
|
||||
0/1
|
||||
2/0
|
||||
```
|
||||
|
||||
`accounts_json` conserve, dans l’ordre original, chaque indice et la clé publique résolue. `payload_json` conserve `programIdIndex`, `dataBase64` et `stackHeight`. Un SHA-256 stable du payload JSON est persisté dans `payload_json_hash`.
|
||||
|
||||
## Logs
|
||||
|
||||
Chaque log conserve :
|
||||
|
||||
- son `log_index` global ;
|
||||
- son texte original ;
|
||||
- un SHA-256 du texte ;
|
||||
- un `program_id` et un `instruction_path` uniquement lorsque la pile `invoke/success/failed` permet un rattachement déterministe.
|
||||
|
||||
Lorsque la reconstruction est ambiguë, le lien reste `NULL` plutôt que d’inventer une relation.
|
||||
|
||||
## Balances
|
||||
|
||||
Les changements natifs utilisent les valeurs entières en lamports et stockent pre, post et delta comme chaînes décimales dans JSONB afin d’éviter toute perte de précision.
|
||||
|
||||
Les changements SPL et Token-2022 utilisent exclusivement `amount` et `decimals`. Les valeurs UI du fournisseur ne sont pas utilisées comme source de calcul. Pour un compte créé ou fermé pendant la transaction, le côté absent est représenté explicitement par un montant brut nul avec les mêmes décimales.
|
||||
|
||||
Le `balance_change_index` est déterministe : balances natives par index de compte, puis balances token triées par `(account_index, mint, program_id)`.
|
||||
|
||||
## Modes de sélection
|
||||
|
||||
La campagne peut sélectionner des transactions raw par signatures explicites, état `received`, plage de slots ou programme déjà présent dans les instructions top-level core. Les filtres, limites et scénarios de validation sont détaillés dans `docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md`.
|
||||
|
||||
Le mode par programme ne découvre pas un programme nouveau : il dépend des lignes déjà présentes dans `kb_sol_core_instructions` et ne couvre pas actuellement les occurrences exclusivement inner.
|
||||
|
||||
## Idempotence et replay
|
||||
|
||||
En mode normal, une entrée ledger `succeeded` ayant la même version et le même hash provoque un skip.
|
||||
|
||||
Un changement de version ou de hash entraîne une nouvelle extraction. Le mode `force_replay` ignore le skip et remplace atomiquement le graphe existant de la signature.
|
||||
|
||||
## Orchestration
|
||||
|
||||
`kb_pipeline::execute_core_extraction` fournit :
|
||||
|
||||
- sélection par signatures ;
|
||||
- lot raw en état `received` ;
|
||||
- plage de slots ;
|
||||
- replay par program id déjà présent dans core ;
|
||||
- limite et concurrence bornées ;
|
||||
- arrêt coopératif avant admission de nouveaux candidats ;
|
||||
- résumé `selected`, `started`, `completed`, `skipped`, `extracted`, `failed`, `cancelled_candidates` et `not_started`.
|
||||
|
||||
La logique de transformation ne dépend ni de Tauri ni d’un décodeur protocolaire.
|
||||
|
||||
## Transactions échouées
|
||||
|
||||
Une transaction Solana ayant `failed = true` reste extraite intégralement lorsque ses métadonnées sont disponibles. Les instructions, inner instructions et logs exécutés avant l’erreur constituent des observations utiles pour les futurs décodeurs.
|
||||
|
||||
Le futur pipeline doit distinguer :
|
||||
|
||||
```text
|
||||
observation décodée dans une transaction échouée
|
||||
mutation d’état confirmée dans une transaction réussie
|
||||
```
|
||||
|
||||
Une transaction échouée peut produire des observations d’intention, de cause d’échec, de compute budget ou de surface appelée. Elle ne doit pas produire automatiquement un trade, une modification de liquidité ou une candle présentés comme réussis.
|
||||
|
||||
## Validation réelle
|
||||
|
||||
Le skip et le force replay ont été validés sur 14 signatures : `14 skipped`, puis `14 extracted` avec force replay, puis `14 skipped`. Les tables core restent à 70 transactions et le ledger totalise 84 tentatives. Les requêtes `sql/validation/000_core_integrity.sql` ne retournent aucune anomalie.
|
||||
|
||||
## Hors périmètre
|
||||
|
||||
`0.3.4` n’effectue aucun décodage Solana Core/SPL, aucun décodage DEX, aucune matérialisation métier et aucune compaction du JSON canonique.
|
||||
@@ -0,0 +1,456 @@
|
||||
<!-- file: docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Sélection et replay de l’extraction core
|
||||
|
||||
## Objet
|
||||
|
||||
Ce document décrit les modes disponibles dans `demo_core_extraction`, leur effet réel sur PostgreSQL et la différence entre :
|
||||
|
||||
- acquisition d’une transaction depuis le réseau ;
|
||||
- extraction du document canonique vers les tables core ;
|
||||
- futur décodage protocolaire ;
|
||||
- future matérialisation métier.
|
||||
|
||||
Dans `0.3.4`, le mot « replay » désigne uniquement le rejeu de l’étape suivante :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions.canonical_json
|
||||
-> kb_model::CanonicalTransaction
|
||||
-> CoreExtractionBundle
|
||||
-> kb_sol_core_transactions
|
||||
-> kb_sol_core_account_keys
|
||||
-> kb_sol_core_instructions
|
||||
-> kb_sol_core_inner_instructions
|
||||
-> kb_sol_core_logs
|
||||
-> kb_sol_core_balance_changes
|
||||
-> kb_sol_ops_processing_ledger
|
||||
```
|
||||
|
||||
Aucun appel RPC n’est effectué par cette étape. Aucun décodeur Solana Core, SPL, DEX ou autre protocole n’est exécuté. Aucune ligne métier n’est matérialisée.
|
||||
|
||||
## Cycle complet d’une transaction
|
||||
|
||||
Le cycle prévu est séparé en étapes rejouables :
|
||||
|
||||
```text
|
||||
réseau RPC
|
||||
-> acquisition HTTP ou WebSocket
|
||||
-> transaction canonique raw
|
||||
-> extraction structurelle core
|
||||
-> observation et classification
|
||||
-> décodage protocolaire
|
||||
-> matérialisation métier
|
||||
-> agrégations et stratégies
|
||||
```
|
||||
|
||||
`0.3.4` couvre uniquement la transition `raw canonique -> core structurel`.
|
||||
|
||||
Les lignes de `kb_sol_core_instructions` sont créées avec `processing_state = pending`. Elles constituent l’entrée des futurs décodeurs prévus à partir de `0.4.x`.
|
||||
|
||||
## Paramètres communs
|
||||
|
||||
### Transactions maximales
|
||||
|
||||
`limit` borne le nombre de transactions sélectionnées par PostgreSQL avant démarrage de la campagne.
|
||||
|
||||
La sélection est ordonnée par :
|
||||
|
||||
```text
|
||||
slot ASC, signature ASC
|
||||
```
|
||||
|
||||
Cette règle s’applique aussi au mode signatures explicites. L’ordre des lignes collées dans la zone de texte n’est donc pas l’ordre d’exécution garanti.
|
||||
|
||||
### Concurrence
|
||||
|
||||
`max_concurrent_extractions` borne le nombre d’extractions admises simultanément.
|
||||
|
||||
Chaque signature reste une unité transactionnelle indépendante. Une transaction Solana produit son graphe core dans une transaction PostgreSQL atomique.
|
||||
|
||||
### Force replay
|
||||
|
||||
Sans `force_replay`, une signature est ignorée lorsque le ledger contient déjà un succès ayant exactement :
|
||||
|
||||
```text
|
||||
stage = core_extraction
|
||||
processor_name = canonical_to_core
|
||||
processor_version = version courante
|
||||
input_key = signature
|
||||
input_hash = canonical_json_hash courant
|
||||
status = succeeded
|
||||
```
|
||||
|
||||
Avec `force_replay`, ce skip est désactivé. Le graphe core existant de la signature est supprimé puis recréé dans la même transaction PostgreSQL. Les autres signatures ne sont pas touchées.
|
||||
|
||||
Le force replay ne relance pas `getTransaction` et ne remplace pas le document canonique raw. Il reconstruit uniquement le core depuis le raw déjà présent.
|
||||
|
||||
## Mode signatures explicites
|
||||
|
||||
### Entrée
|
||||
|
||||
La zone de texte accepte une signature par ligne.
|
||||
|
||||
Avant l’appel au pipeline, l’interface :
|
||||
|
||||
- supprime les espaces autour de chaque ligne ;
|
||||
- ignore les lignes vides ;
|
||||
- supprime les doublons en conservant la première occurrence ;
|
||||
- valide chaque valeur comme signature Solana.
|
||||
|
||||
### Sélection PostgreSQL
|
||||
|
||||
Le mode sélectionne uniquement les signatures déjà présentes dans `kb_sol_raw_transactions`.
|
||||
|
||||
Une signature absente de la table raw n’est pas téléchargée automatiquement. Elle n’apparaît simplement pas dans les candidats sélectionnés. Le compteur `selected` peut donc être inférieur au nombre de signatures saisies.
|
||||
|
||||
Aucun filtre de `processing_state` n’est appliqué. Ce mode peut sélectionner une transaction raw :
|
||||
|
||||
- `received` ;
|
||||
- `core_extracted` ;
|
||||
- `failed`.
|
||||
|
||||
Le ledger décide ensuite entre skip, extraction ou retry.
|
||||
|
||||
### Usages principaux
|
||||
|
||||
- vérifier l’idempotence sur un échantillon connu ;
|
||||
- forcer la reconstruction de quelques signatures ;
|
||||
- retenter explicitement des transactions raw en échec ;
|
||||
- comparer deux versions de l’extracteur ;
|
||||
- préparer plus tard un corpus ciblé pour un décodeur.
|
||||
|
||||
### Point d’attention
|
||||
|
||||
Si le nombre de signatures existantes dépasse `limit`, seules les premières selon `slot ASC, signature ASC` sont retenues.
|
||||
|
||||
## Mode transactions en attente
|
||||
|
||||
### Filtre réel
|
||||
|
||||
Le mode pending sélectionne :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions.processing_state = received
|
||||
```
|
||||
|
||||
Il ne sélectionne ni les lignes déjà `core_extracted`, ni les lignes `failed`.
|
||||
|
||||
### Transition après succès
|
||||
|
||||
Après commit du graphe core :
|
||||
|
||||
```text
|
||||
received -> core_extracted
|
||||
```
|
||||
|
||||
La même ligne ne sera donc plus sélectionnée lors d’une campagne pending suivante.
|
||||
|
||||
### Transition après échec
|
||||
|
||||
Après un échec d’extraction persistable :
|
||||
|
||||
```text
|
||||
received -> failed
|
||||
```
|
||||
|
||||
La raison est stockée dans `lifecycle_reason` et le ledger reçoit un statut `failed`.
|
||||
|
||||
Une ligne `failed` doit être retentée par signature explicite ou par un futur mode dédié aux erreurs. Une nouvelle campagne pending ne la reprend pas.
|
||||
|
||||
### Usages principaux
|
||||
|
||||
- vider progressivement la file des transactions canoniques nouvellement acquises ;
|
||||
- exécuter l’extraction courante après un backfill HTTP ;
|
||||
- traiter un lot borné sans sélectionner manuellement les signatures.
|
||||
|
||||
### Conséquence sur le corpus actuel
|
||||
|
||||
Après la campagne réelle de 70 transactions :
|
||||
|
||||
```text
|
||||
selected = 70
|
||||
extracted = 70
|
||||
failed = 0
|
||||
```
|
||||
|
||||
les 70 lignes raw sont normalement passées à `core_extracted`. Une nouvelle campagne pending doit donc sélectionner zéro transaction tant qu’aucune nouvelle transaction raw `received` n’a été acquise.
|
||||
|
||||
## Mode plage de slots
|
||||
|
||||
### Filtre réel
|
||||
|
||||
Le mode sélectionne toutes les transactions raw dont le slot se trouve dans l’intervalle inclusif :
|
||||
|
||||
```text
|
||||
min_slot <= slot <= max_slot
|
||||
```
|
||||
|
||||
Aucun filtre de `processing_state` n’est appliqué.
|
||||
|
||||
### Comportement normal
|
||||
|
||||
Les transactions déjà à jour sont sélectionnées puis comptées comme `skipped` par le ledger. Les transactions reçues, en échec, ou dont la version/hash a changé peuvent être extraites.
|
||||
|
||||
### Comportement avec force replay
|
||||
|
||||
Toutes les transactions sélectionnées sont reconstruites dans la limite configurée.
|
||||
|
||||
### Usages principaux
|
||||
|
||||
- rejouer un intervalle temporel connu ;
|
||||
- vérifier une régression sur un bloc ou une période ;
|
||||
- reconstruire un corpus après changement de version de l’extracteur.
|
||||
|
||||
## Mode programme déjà indexé dans core
|
||||
|
||||
### Filtre réel actuel
|
||||
|
||||
Le mode sélectionne les signatures raw pour lesquelles il existe déjà une ligne correspondante dans :
|
||||
|
||||
```text
|
||||
kb_sol_core_instructions
|
||||
```
|
||||
|
||||
avec un `program_id` exactement égal à la valeur demandée.
|
||||
|
||||
La requête actuelle ne recherche pas dans :
|
||||
|
||||
- `kb_sol_core_inner_instructions` ;
|
||||
- `kb_sol_core_logs` ;
|
||||
- `kb_sol_core_account_keys` ;
|
||||
- le document canonique raw.
|
||||
|
||||
### Conséquences
|
||||
|
||||
Ce mode ne peut pas découvrir un programme pour la première fois. Il fonctionne uniquement après une première extraction core ayant déjà résolu au moins une instruction top-level de ce programme.
|
||||
|
||||
Une transaction où le programme apparaît exclusivement en inner instruction n’est pas sélectionnée par ce mode dans son état actuel.
|
||||
|
||||
### Usages principaux
|
||||
|
||||
- reconstruire les transactions d’un programme déjà indexé après changement de version ;
|
||||
- vérifier une correction de résolution des comptes ou instructions ;
|
||||
- préparer un sous-corpus structurel avant le futur replay d’un décodeur.
|
||||
|
||||
### Évolution recommandée
|
||||
|
||||
Le futur sélecteur de corpus devrait distinguer explicitement :
|
||||
|
||||
- programme top-level ;
|
||||
- programme inner ;
|
||||
- programme observé dans les logs ;
|
||||
- union de toutes les occurrences core fiables.
|
||||
|
||||
Le mode d’extraction actuel peut rester strictement basé sur les instructions top-level, à condition que cette limite demeure visible dans l’interface.
|
||||
|
||||
## Interprétation des compteurs
|
||||
|
||||
| Compteur | Signification |
|
||||
|-----------------------|-------------------------------------------------------------|
|
||||
| `selected` | Candidats retournés par PostgreSQL après filtres et limite. |
|
||||
| `started` | Candidats admis dans la file d’exécution bornée. |
|
||||
| `completed` | Candidats arrivés à un résultat terminal. |
|
||||
| `skipped` | Ledger déjà à jour pour la même version et le même hash. |
|
||||
| `extracted` | Graphe core écrit et commit réussi. |
|
||||
| `failed` | Extraction ou persistance terminée en erreur. |
|
||||
| `cancelledCandidates` | Candidats admis mais annulés avant résultat terminal. |
|
||||
| `notStarted` | Candidats sélectionnés mais jamais admis après arrêt. |
|
||||
| `cancelled` | Une demande d’arrêt coopératif a été observée. |
|
||||
|
||||
Pour une campagne terminée normalement :
|
||||
|
||||
```text
|
||||
completed = skipped + extracted + failed
|
||||
selected = completed + cancelledCandidates + notStarted
|
||||
```
|
||||
|
||||
## Scénarios de validation recommandés
|
||||
|
||||
### Vérifier le skip
|
||||
|
||||
1. Extraire 5 à 10 signatures déjà présentes dans core depuis un outil de sélection.
|
||||
2. Les coller dans le mode signatures explicites.
|
||||
3. Laisser `force_replay` désactivé.
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
selected = N
|
||||
skipped = N
|
||||
extracted = 0
|
||||
failed = 0
|
||||
```
|
||||
|
||||
### Vérifier le force replay
|
||||
|
||||
1. Réutiliser exactement les mêmes signatures.
|
||||
2. Activer `force_replay`.
|
||||
|
||||
Résultat attendu :
|
||||
|
||||
```text
|
||||
selected = N
|
||||
skipped = 0
|
||||
extracted = N
|
||||
failed = 0
|
||||
```
|
||||
|
||||
Les cardinalités globales des tables core doivent rester stables pour ces signatures. Le `attempt_count` du ledger doit augmenter.
|
||||
|
||||
### Vérifier un changement de version ou de hash
|
||||
|
||||
Un changement réel de `processor_version` ou de `canonical_json_hash` doit provoquer une nouvelle extraction même sans `force_replay`.
|
||||
|
||||
Cette vérification doit être effectuée dans une base de test dédiée. Le hash canonique d’une ligne de production ne doit pas être modifié manuellement.
|
||||
|
||||
### Vérifier le rollback
|
||||
|
||||
Le rollback atomique doit être validé par un test PostgreSQL avec injection d’une erreur entre les insertions du graphe et le commit.
|
||||
|
||||
La méthode recommandée est un test d’intégration sur `solana_test`, et non une corruption volontaire de la base principale.
|
||||
|
||||
## Sélecteur de corpus dans l’application
|
||||
|
||||
La fenêtre read-only dédiée est :
|
||||
|
||||
```text
|
||||
demo_sql_replay_candidates
|
||||
```
|
||||
|
||||
Elle est accessible depuis le menu principal et depuis l’en-tête de `demo_core_extraction`. `demo_sql_diag` reste limité à la connexion, au profil actif, aux migrations et aux cardinalités.
|
||||
|
||||
Le sélecteur ne déclenche aucune acquisition RPC, extraction core, opération de décodage ou matérialisation. Il lit uniquement les tables PostgreSQL déjà alimentées.
|
||||
|
||||
## Tableau des transactions et signatures
|
||||
|
||||
Les filtres PostgreSQL disponibles sont :
|
||||
|
||||
- fragment de signature ;
|
||||
- slot minimum et maximum inclusifs ;
|
||||
- état raw `received`, `core_extracted`, `decoded`, `materialized` ou `failed` ;
|
||||
- statut ledger `not_started`, `running`, `succeeded` ou `failed` ;
|
||||
- program ID exact ;
|
||||
- portée du programme : `any`, `outer`, `inner` ou `logs` ;
|
||||
- entité exacte : `mint`, `owner` ou `account_key` ;
|
||||
- limite ;
|
||||
- ordre par slot croissant ou décroissant.
|
||||
|
||||
Chaque résultat affiche :
|
||||
|
||||
- signature et slot ;
|
||||
- état raw et rétention ;
|
||||
- présence de la transaction core et éventuel échec Solana ;
|
||||
- dernier statut du ledger `core_extraction` ;
|
||||
- version et nombre de tentatives ;
|
||||
- nombres d’instructions et de programmes outer/inner ;
|
||||
- date de dernière mise à jour raw.
|
||||
|
||||
Le filtre de programme inspecte les instructions outer, les inner instructions et les logs auxquels un `program_id` a pu être rattaché de manière prudente. Une occurrence dans les logs n’est donc visible que lorsque le rattachement core a produit un `program_id` non nul.
|
||||
|
||||
## Tableau des programmes
|
||||
|
||||
Le tableau agrège :
|
||||
|
||||
```text
|
||||
kb_sol_core_instructions.program_id
|
||||
kb_sol_core_inner_instructions.program_id
|
||||
kb_sol_core_logs.program_id
|
||||
```
|
||||
|
||||
Pour chaque programme, il expose :
|
||||
|
||||
- le `program_id` ;
|
||||
- le nombre de transactions distinctes ;
|
||||
- le nombre d’instructions outer ;
|
||||
- le nombre d’instructions inner ;
|
||||
- le nombre de logs reliés ;
|
||||
- le slot minimum et maximum.
|
||||
|
||||
La sélection d’un programme peut alimenter le filtre programme du tableau des transactions, puis charger les signatures correspondantes.
|
||||
|
||||
## Tableau des entités
|
||||
|
||||
Le tableau agrège trois familles :
|
||||
|
||||
- `mint` depuis `kb_sol_core_balance_changes.mint` ;
|
||||
- `owner` depuis `kb_sol_core_balance_changes.owner` ;
|
||||
- `account_key` depuis `kb_sol_core_account_keys.account_key`.
|
||||
|
||||
Chaque ligne indique le nombre de transactions distinctes, le nombre d’occurrences et la plage de slots. Une entité sélectionnée peut alimenter le filtre du tableau des transactions.
|
||||
|
||||
Les account keys ne sont pas présentées comme wallet, pool, mint ou token account sans classification supplémentaire.
|
||||
|
||||
## DataTables, copie et exports
|
||||
|
||||
La fenêtre utilise cinq tableaux DataTables avec intégration Bootstrap 5 et extension Select :
|
||||
|
||||
1. transactions et signatures ;
|
||||
2. programmes ;
|
||||
3. mints ;
|
||||
4. owners ;
|
||||
5. account keys.
|
||||
|
||||
Chaque tableau affiche 10 lignes par défaut. Une checkbox par ligne et une checkbox de header permettent de sélectionner le jeu filtré. Les filtres possèdent un bouton de reset qui restaure les valeurs par défaut, efface la recherche locale, désélectionne les lignes et recharge PostgreSQL.
|
||||
|
||||
Les signatures, program IDs, mints, owners et account keys sont tronqués visuellement sous la forme `préfixe…suffixe`. La valeur complète reste utilisée par le tri, la recherche, les tooltips, la copie et l’export. Un bouton de copie individuel évite les espaces parasites liés à une sélection manuelle.
|
||||
|
||||
La règle d’export est la suivante :
|
||||
|
||||
1. lorsqu’une ou plusieurs lignes sont sélectionnées, seules ces lignes sont exportées ;
|
||||
2. sans sélection, toutes les lignes correspondant au filtre local DataTables sont exportées ;
|
||||
3. la limite PostgreSQL reste la borne supérieure du corpus chargé.
|
||||
|
||||
Le frontend construit un CSV UTF-8 avec BOM, séparateur `;` et fins de ligne CRLF. Une commande Rust l’écrit dans :
|
||||
|
||||
```text
|
||||
data/exports_csv/
|
||||
```
|
||||
|
||||
Le backend ajoute un suffixe numérique lorsque le nom existe déjà. Ce chemin a été validé sous Tauri/Linux et ne dépend pas du téléchargement HTML du WebView.
|
||||
|
||||
La copie multiligne des signatures est directement compatible avec la zone `Signatures explicites` de `demo_core_extraction`.
|
||||
|
||||
## Garanties et limites
|
||||
|
||||
- commandes Tauri read-only ;
|
||||
- requêtes SQL statiques et paramétrées ;
|
||||
- aucune zone permettant d’exécuter du SQL libre ;
|
||||
- limite obligatoire comprise entre `1` et `5 000` ;
|
||||
- filtres exacts pour programme et entité afin d’éviter une sélection ambiguë ;
|
||||
- recherche locale DataTables appliquée après le chargement serveur ;
|
||||
- aucune découverte de transaction absente du raw ;
|
||||
- aucun décodage de protocole ;
|
||||
- aucun changement d’état raw, core ou ledger.
|
||||
|
||||
Les futurs écrans de `0.4.x` pourront réutiliser le même principe pour les replays de décodeurs et de matérialiseurs.
|
||||
## Utilisation du sélecteur SQL
|
||||
|
||||
`demo_sql_replay_candidates` aide à constituer des corpus sans requête manuelle dans pgAdmin ou DBeaver. Il reste séparé du worker : il lit les tables raw/core/ledger, puis produit des signatures ou des filtres réutilisables.
|
||||
|
||||
Dans l’onglet programmes, « Utiliser comme filtre transactions » prend exactement un programme coché. À défaut de sélection, une unique ligne restant après le filtre local est acceptée. L’action renseigne le program ID exact, choisit la portée `any`, ouvre l’onglet transactions et recharge la liste.
|
||||
|
||||
Le sélecteur des programmes connus est alimenté par `kb_program_ids`, mais la saisie libre reste disponible pour les programmes observés qui ne sont pas encore enregistrés.
|
||||
|
||||
Les exports CSV sont écrits dans `data/exports_csv/` par le backend Rust. Ils servent à l’audit et à la conservation d’un corpus ; pour rejouer immédiatement des signatures, la copie multiligne reste le chemin le plus direct.
|
||||
|
||||
Les notions de pool et de paire ne sont pas encore disponibles à ce stade structurel. Une account key core ne suffit pas à prouver qu’un compte représente un pool ou une paire. Cette navigation sera ajoutée après matérialisation de catalogues sémantiques par les décodeurs.
|
||||
|
||||
|
||||
## Validation réelle de l’idempotence
|
||||
|
||||
Un échantillon de 14 signatures a produit les résultats suivants :
|
||||
|
||||
```text
|
||||
mode normal : extracted=0, skipped=14
|
||||
force replay: extracted=14, skipped=0
|
||||
mode normal : extracted=0, skipped=14
|
||||
```
|
||||
|
||||
Le nombre de transactions core est resté à 70. Le ledger contient 70 lignes `succeeded` et 84 tentatives, soit les 70 tentatives initiales plus les 14 remplacements forcés. Les requêtes d’intégrité n’ont détecté aucune anomalie.
|
||||
|
||||
## Politique future pour les transactions échouées
|
||||
|
||||
Les transactions on-chain échouées restent candidates au décodage. Elles conservent des informations sur les programmes appelés, les instructions exécutées avant l’erreur, les logs, le compute consommé et la cause d’échec.
|
||||
|
||||
Les décodeurs devront produire des observations marquées par le statut de la transaction. Les matérialiseurs ne devront pas convertir ces observations en mutations d’état réussies, trades confirmés, changements de liquidité ou candles normales.
|
||||
63
migration/khadhroony-bot2-reference/docs/CORE_PROGRAM_IDS.md
Normal file
63
migration/khadhroony-bot2-reference/docs/CORE_PROGRAM_IDS.md
Normal file
@@ -0,0 +1,63 @@
|
||||
<!-- file: docs/CORE_PROGRAM_IDS.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Identifiants core Solana et SPL
|
||||
|
||||
Ce fichier sépare les identifiants primitifs Solana/SPL du registre des surfaces applicatives. Cette séparation évite de traiter `system`, `token`, `memo`, `sysvar` ou les loaders comme des DEX/routers.
|
||||
|
||||
## Règle de classification
|
||||
|
||||
- Les programmes runtime natifs, loaders et précompiles relèvent de `kb_decoder_solana_core`.
|
||||
- Chaque programme SPL vérifié conserve une frontière spécialisée : Token, Token-2022, ATA, Memo, Stake Pool, Single Pool, Account Compression, Noop ou Name Service.
|
||||
- Les sysvars sont des comptes connus à reconnaître pendant l'extraction, pas des surfaces DEX à décoder comme des programmes applicatifs.
|
||||
- `stake_pool` et `single_pool` sont distincts du Stake Program natif et possèdent leurs propres crates.
|
||||
|
||||
## Table de contrôle
|
||||
|
||||
| Canonique | Program ID | Source | Type | Crate cible | Statut |
|
||||
|--------------------------------------------|------------------------------------------------|-----------------------------------|----------------------|-------------------------------------------|-------------------------------------|
|
||||
| `core_solana_address_lookup_table_v1` | `AddressLookupTab1e1111111111111111111111111` | `address_lookup_table` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_bpf_loader_deprecated_v1` | `BPFLoader1111111111111111111111111111111111` | `bpf_loader_deprecated` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_bpf_loader_v2` | `BPFLoader2111111111111111111111111111111111` | `bpf_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_bpf_loader_upgradeable_v1` | `BPFLoaderUpgradeab1e11111111111111111111111` | `bpf_loader_upgradeable` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_compute_budget_v1` | `ComputeBudget111111111111111111111111111111` | `compute_budget` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_config_v1` | `Config1111111111111111111111111111111111111` | `config` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_ed25519_v1` | `Ed25519SigVerify111111111111111111111111111` | `ed25519` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_feature_v1` | `Feature111111111111111111111111111111111111` | `feature` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_incinerator_v1` | `1nc1nerator11111111111111111111111111111111` | `incinerator` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_loader_v4` | `LoaderV411111111111111111111111111111111111` | `loader_v4` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_native_loader_v1` | `NativeLoader1111111111111111111111111111111` | `native_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_secp256k1_v1` | `KeccakSecp256k11111111111111111111111111111` | `secp256k1` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_secp256r1_v1` | `Secp256r1SigVerify1111111111111111111111111` | `secp256r1` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_slashing_v1` | `S1ashing11111111111111111111111111111111111` | `slashing` | `stateless_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_stake_config_v1` | `StakeConfig11111111111111111111111111111111` | `stake_config` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_stake_v1` | `Stake11111111111111111111111111111111111111` | `stake` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_system_v1` | `11111111111111111111111111111111` | `system` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_sysvar_clock_v1` | `SysvarC1ock11111111111111111111111111111111` | `sysvar_clock` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_epoch_rewards_v1` | `SysvarEpochRewards1111111111111111111111111` | `sysvar_epoch_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_epoch_schedule_v1` | `SysvarEpochSchedu1e111111111111111111111111` | `sysvar_epoch_schedule` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_fees_v1` | `SysvarFees111111111111111111111111111111111` | `sysvar_fees` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_instructions_v1` | `Sysvar1nstructions1111111111111111111111111` | `sysvar_instructions` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_last_restart_slot_v1` | `SysvarLastRestartS1ot1111111111111111111111` | `sysvar_last_restart_slot` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_recent_blockhashes_v1` | `SysvarRecentB1ockHashes11111111111111111111` | `sysvar_recent_blockhashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_rent_v1` | `SysvarRent111111111111111111111111111111111` | `sysvar_rent` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_rewards_v1` | `SysvarRewards111111111111111111111111111111` | `sysvar_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_slot_hashes_v1` | `SysvarS1otHashes111111111111111111111111111` | `sysvar_slot_hashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_slot_history_v1` | `SysvarS1otHistory11111111111111111111111111` | `sysvar_slot_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_stake_history_v1` | `SysvarStakeHistory1111111111111111111111111` | `sysvar_stake_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_v1` | `Sysvar1111111111111111111111111111111111111` | `sysvar` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_vote_v1` | `Vote111111111111111111111111111111111111111` | `vote` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_zk_elgamal_proof_v1` | `ZkE1Gama1Proof11111111111111111111111111111` | `zk_elgamal_proof` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_zk_token_proof_v1` | `ZkTokenProof1111111111111111111111111111111` | `zk_token_proof` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_spl_associated_token_account_v1` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | `associated_token_account` | `program` | `kb_decoder_spl_associated_token_account` | `core_handled` |
|
||||
| `core_spl_account_compression_v1` | `cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK` | `spl_account_compression` | `program` | `kb_decoder_spl_account_compression` | `core_specialized_reserved_current` |
|
||||
| `core_spl_memo_v1` | `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` | `spl_memo_v1` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` |
|
||||
| `core_spl_memo_v3` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | `spl_memo_v3` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` |
|
||||
| `core_spl_memo_v4` | `Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH` | `spl_memo_v4` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` |
|
||||
| `core_spl_noop_v1` | `noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV` | `spl_noop` | `program` | `kb_decoder_spl_noop` | `core_specialized_reserved_current` |
|
||||
| `core_spl_single_pool_v1` | `SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE` | `spl_single_pool` | `program` | `kb_decoder_spl_single_pool` | `core_specialized_reserved_current` |
|
||||
| `core_spl_name_service_v1` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `spl_name_service` | `program` | `kb_decoder_metadata_spl_name_service` | `core_specialized_reserved_current` |
|
||||
| `core_spl_stake_pool_v1` | `SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy` | `stake_pool` | `program` | `kb_decoder_spl_stake_pool` | `core_specialized_reserved_current` |
|
||||
| `core_spl_token_2022_v2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | `token_2022` | `program` | `kb_decoder_spl_token_2022` | `core_handled` |
|
||||
| `core_spl_token_2022_elgamal_registry_v1` | `regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` | `spl_token_2022_elgamal_registry` | `program` | `kb_decoder_spl_token_2022` | `core_specialized_reserved_current` |
|
||||
| `core_spl_token_v1` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | `token` | `program` | `kb_decoder_spl_token` | `core_handled` |
|
||||
73
migration/khadhroony-bot2-reference/docs/CORE_STORE.md
Normal file
73
migration/khadhroony-bot2-reference/docs/CORE_STORE.md
Normal file
@@ -0,0 +1,73 @@
|
||||
<!-- file: docs/CORE_STORE.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Store core Solana
|
||||
|
||||
## Évolution `0.3.4`
|
||||
|
||||
Le store core historique devient alimenté par un extracteur transactionnel à partir de `kb_sol_raw_transactions.canonical_json`.
|
||||
|
||||
| Table | Rôle |
|
||||
|----------------------------------|------------------------------------------------------------|
|
||||
| `kb_sol_core_transactions` | Transaction normalisée, statut et lien vers la ligne raw. |
|
||||
| `kb_sol_core_account_keys` | Espace résolu statique + ALT avec flags Solana. |
|
||||
| `kb_sol_core_instructions` | Instructions top-level et payload canonique hashé. |
|
||||
| `kb_sol_core_inner_instructions` | Inner instructions rattachées à un chemin parent. |
|
||||
| `kb_sol_core_logs` | Logs ordonnés, texte hashé et lien d’invocation optionnel. |
|
||||
| `kb_sol_core_balance_changes` | Deltas natifs, SPL Token et Token-2022. |
|
||||
| `kb_sol_ops_processing_ledger` | Version, hash, statut et tentatives du processor. |
|
||||
|
||||
## Écriture atomique
|
||||
|
||||
Une extraction réussie exécute dans la même transaction PostgreSQL :
|
||||
|
||||
1. validation du bundle ;
|
||||
2. recherche d’un graphe core existant ;
|
||||
3. suppression en cascade de ce graphe pour la signature ;
|
||||
4. insertion de la transaction et de toutes ses lignes filles ;
|
||||
5. passage de la ligne raw à `core_extracted` ;
|
||||
6. upsert du ledger en `succeeded` ;
|
||||
7. commit.
|
||||
|
||||
Une erreur déclenche le rollback implicite de la transaction. L’enregistrement terminal d’un échec utilise une transaction dédiée et marque la ligne raw `failed` avec un diagnostic rejouable.
|
||||
|
||||
## Clés d’idempotence
|
||||
|
||||
Les contraintes actives restent :
|
||||
|
||||
```text
|
||||
core transaction : signature
|
||||
account key : signature + account_index
|
||||
instruction top-level : signature + instruction_path
|
||||
inner instruction : signature + instruction_path
|
||||
log : signature + log_index
|
||||
balance change : signature + balance_change_index
|
||||
ledger : stage + processor_name + processor_version + input_key
|
||||
```
|
||||
|
||||
Le ledger ajoute `input_hash` à la décision de skip sans l’ajouter à la clé unique. Un nouvel hash met à jour la même identité de processor et augmente `attempt_count`.
|
||||
|
||||
## Sélection raw
|
||||
|
||||
Le contrat `CoreExtractionSelectionFilter` permet :
|
||||
|
||||
- une liste de signatures ;
|
||||
- une plage inclusive de slots ;
|
||||
- un état raw ;
|
||||
- un program id déjà indexé dans les instructions core ;
|
||||
- une limite stricte.
|
||||
|
||||
Le filtre par program id est un mode de replay. Il ne peut pas découvrir un programme dans une transaction qui n’a jamais encore été extraite.
|
||||
|
||||
## Repositories
|
||||
|
||||
`CoreExtractionStore` isole le pipeline du backend :
|
||||
|
||||
```text
|
||||
list_raw_transactions_for_core_extraction
|
||||
is_core_extraction_current
|
||||
persist_core_extraction
|
||||
mark_core_extraction_failed
|
||||
```
|
||||
|
||||
Les repositories historiques d’insertion unitaire restent disponibles, mais le worker `0.3.4` utilise exclusivement l’écriture atomique du bundle.
|
||||
155
migration/khadhroony-bot2-reference/docs/DATABASE.md
Normal file
155
migration/khadhroony-bot2-reference/docs/DATABASE.md
Normal file
@@ -0,0 +1,155 @@
|
||||
<!-- file: docs/DATABASE.md -->
|
||||
<!-- version: 11 -->
|
||||
|
||||
# Base de données
|
||||
|
||||
PostgreSQL est le backend principal prévu pour `khadhroony-bot2`. `0.2.0` fixe uniquement les conventions de stockage : les migrations complètes, repositories SQL et diagnostics applicatifs sont reportés aux jalons suivants.
|
||||
|
||||
## Décision PostgreSQL
|
||||
|
||||
Le projet ne crée pas de schémas PostgreSQL applicatifs explicites.
|
||||
|
||||
Le store utilise le schéma courant du profil PostgreSQL, généralement `public`. Le choix du schéma reste donc une responsabilité de configuration ou d'administration PostgreSQL, pas une responsabilité des migrations applicatives.
|
||||
|
||||
Interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel :
|
||||
|
||||
```text
|
||||
raw DOT kb_sol_rpc_transactions
|
||||
core DOT kb_sol_transactions
|
||||
obs DOT kb_sol_program_observations
|
||||
decode DOT kb_sol_decoded_events
|
||||
mat DOT kb_sol_trade_events
|
||||
catalog DOT kb_sol_tokens
|
||||
ops DOT kb_sol_processing_ledger
|
||||
```
|
||||
|
||||
Autorisés :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions
|
||||
kb_sol_core_transactions
|
||||
kb_sol_obs_program_observations
|
||||
kb_sol_decode_decoded_events
|
||||
kb_sol_mat_trade_events
|
||||
kb_sol_catalog_tokens
|
||||
kb_sol_ops_processing_ledger
|
||||
```
|
||||
|
||||
## Format canonique des tables Solana
|
||||
|
||||
Toutes les tables Solana applicatives suivent ce format :
|
||||
|
||||
```text
|
||||
kb_sol_<domain>_<name>
|
||||
```
|
||||
|
||||
Domaines autorisés au départ :
|
||||
|
||||
| Domaine | Rôle logique | Exemple de table |
|
||||
|-----------|-------------------------------------------------------------------------------------|---------------------------------------|
|
||||
| `raw` | transactions Solana canoniques, source-indépendantes et rejouables | `kb_sol_raw_transactions` |
|
||||
| `core` | extraction Solana générique normalisée | `kb_sol_core_instructions` |
|
||||
| `obs` | observations techniques de source, programmes, instructions, logs et discriminators | `kb_sol_obs_transaction_observations` |
|
||||
| `decode` | événements décodés par les décodeurs | `kb_sol_decode_decoded_events` |
|
||||
| `mat` | projections métier matérialisées | `kb_sol_mat_trade_events` |
|
||||
| `catalog` | tokens, pools, paires et références stables | `kb_sol_catalog_pairs` |
|
||||
| `agg` | agrégations temporelles ou analytiques | `kb_sol_agg_pair_candles` |
|
||||
| `ops` | ledger de traitement, migrations applicatives et diagnostics | `kb_sol_ops_processing_ledger` |
|
||||
| `wallet` | métadonnées wallet non sensibles | `kb_sol_wallet_accounts` |
|
||||
|
||||
Le domaine reste un préfixe de nom de table, jamais un schéma PostgreSQL.
|
||||
|
||||
|
||||
## Décision de phasage à partir de `0.3.x`
|
||||
|
||||
`0.3.x` corrige le modèle d’acquisition avant les décodeurs : transaction canonique source-indépendante, observations de source légères, backfill HTTP sur comptes gratuits et extraction vers `core`.
|
||||
|
||||
Les flux payants Helius `transactionSubscribe` et Yellowstone gRPC sont reportés à `0.10.x`, après les décodeurs Core, Pump, Meteora, Raydium, Orca et Jupiter. Ils devront alimenter exactement le même contrat canonique que `getTransaction`.
|
||||
|
||||
## Tables candidates initiales
|
||||
|
||||
Ces tables sont candidates, pas toutes implémentées en `0.2.0`.
|
||||
|
||||
| Table | Jalons pressentis | Rôle |
|
||||
|---------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| `kb_sol_raw_rpc_transactions` | `0.2.3` historique | Ancienne table validée puis remplacée pendant `0.3.1`; absente de la baseline SQL active. |
|
||||
| `kb_sol_raw_transactions` | `0.3.1` | Stocker une transaction Solana canonique unique par signature, indépendante de la source. |
|
||||
| `kb_sol_raw_ws_notifications` | `0.2.3` historique | Ancienne table supprimée pendant `0.3.1` après conversion des métadonnées utiles; absente de la baseline active. |
|
||||
| `kb_sol_obs_transaction_observations` | `0.3.1` | Stocker source, protocole, méthode, timestamps, taille, latences et statuts sans dupliquer le payload transactionnel. |
|
||||
| `kb_sol_core_transactions` | `0.2.4` | Ligne transaction normalisée liée à une signature. |
|
||||
| `kb_sol_core_account_keys` | `0.2.4` | Comptes résolus, y compris loaded addresses. |
|
||||
| `kb_sol_core_instructions` | `0.2.4` | Instructions top-level normalisées. |
|
||||
| `kb_sol_core_inner_instructions` | `0.2.4` | Instructions internes normalisées. |
|
||||
| `kb_sol_core_logs` | `0.2.4` | Logs de transaction et ordre d'apparition. |
|
||||
| `kb_sol_core_balance_changes` | `0.2.4` | Deltas SOL/SPL dérivés du metadata RPC. |
|
||||
| `kb_sol_obs_program_observations` | `0.3.4+` | Observations par programme invoqué, après stabilisation de l'ingestion et de l'extraction. |
|
||||
| `kb_sol_obs_instruction_observations` | `0.3.4+` | Observations par instruction, discriminator et surface candidate, après stabilisation de l'ingestion et de l'extraction. |
|
||||
| `kb_sol_decode_decoded_events` | `0.4.x+` | Événements décodés par surface et version de décodeur. |
|
||||
| `kb_sol_mat_trade_events` | `0.5.x+` | Trades matérialisés. |
|
||||
| `kb_sol_catalog_tokens` | `0.5.x+` | Catalogue de tokens observés. |
|
||||
| `kb_sol_catalog_pools` | `0.5.x+` | Catalogue de pools observés. |
|
||||
| `kb_sol_catalog_pairs` | `0.5.x+` | Catalogue de paires tradables. |
|
||||
| `kb_sol_ops_processing_ledger` | `0.3.4+` | Ledger de traitements par module, version, input et statut, après stabilisation de l'ingestion. |
|
||||
|
||||
## Règles SQL générales
|
||||
|
||||
- `id` est la clé primaire technique, sauf exception explicitement documentée.
|
||||
- `created_at` est le timestamp d'insertion.
|
||||
- `updated_at` existe seulement si la table est mutable.
|
||||
- `slot` est un `BIGINT` côté SQL ; les conversions Rust doivent être explicites.
|
||||
- `signature` est un texte non vide quand applicable.
|
||||
- `program_id` est un texte non vide quand applicable.
|
||||
- `canonical_json` est un `JSONB` réservé à la représentation canonique source-indépendante de la transaction.
|
||||
- `payload_json` est un `JSONB` réservé au payload décodé, enrichi ou matérialisé.
|
||||
- Les index minimaux sont ajoutés selon l'usage : `signature`, `slot`, `program_id`, `created_at`.
|
||||
- Les noms d'index utilisent un préfixe fonctionnel : `ux_` pour les index uniques et `ix_` pour les index non uniques, par exemple `ux_kb_sol_raw_transactions_signature` ou `ix_kb_sol_obs_transaction_observations_signature`.
|
||||
- Les contraintes de clés nommées utilisent un préfixe fonctionnel : `pk_` pour les clés primaires et `fk_` pour les clés étrangères. Les contraintes de validation peuvent utiliser `ck_`.
|
||||
- Les contraintes métier doivent rester strictes mais réversibles tant que les corpus ne sont pas stabilisés.
|
||||
|
||||
## Principe raw et cycle de vie
|
||||
|
||||
La couche `raw` conserve une seule représentation canonique de chaque transaction Solana. Elle ne conserve pas une copie complète par transport ou fournisseur.
|
||||
|
||||
Les adaptateurs `kb_rpc` convertissent JSON-RPC, Helius WebSocket ou Yellowstone Protobuf vers le même contrat `kb_model`. Le payload canonique reste la source rejouable pour l’extraction `core`, les décodeurs et les matérialisateurs.
|
||||
|
||||
La couche `obs` conserve les acquisitions successives : fournisseur, endpoint, protocole, méthode, commitment, timestamps, taille et statut. `kb_sol_obs_transaction_observations` ne contient pas de `raw_json` complet.
|
||||
|
||||
Les états de rétention s’appliquent à la transaction canonique. Les observations techniques ont une politique de conservation séparée, car elles sont petites et utiles aux mesures de couverture et de latence.
|
||||
|
||||
Le contrat détaillé est décrit dans `docs/TRANSACTION_ACQUISITION_MODEL.md`. Le cycle de vie est décrit dans `docs/RAW_STORAGE_LIFECYCLE.md`. Le découpage core destiné aux décodeurs est décrit dans `docs/CORE_EXTRACTION_CONTRACTS.md`.
|
||||
|
||||
## Replay partiel
|
||||
|
||||
Le replay ne doit pas être limité à la transaction complète. Après extraction `core`, l'unité de scheduling principale devient l'instruction normalisée. Cela permet de traiter uniquement les instructions `Pending`, `Failed` ou `ReplayRequested`, sans rejouer toute une transaction déjà extraite.
|
||||
|
||||
Le décodage ne doit cependant pas être limité au payload de l'instruction. Les décodeurs doivent recevoir une entrée contextualisée : instruction ciblée, comptes résolus, inner instructions, logs, deltas de balance et statut de transaction.
|
||||
|
||||
Les contrats de replay par instruction sont décrits dans `docs/INSTRUCTION_REPLAY_CONTRACTS.md`. Les contrats d'extraction core sont décrits dans `docs/CORE_EXTRACTION_CONTRACTS.md`.
|
||||
|
||||
Chaque table dérivée doit pouvoir être reconstruite par au moins un des axes suivants quand l'information existe :
|
||||
|
||||
- module et version ;
|
||||
- signature ;
|
||||
- plage de slots ;
|
||||
- `program_id` ;
|
||||
- surface candidate ;
|
||||
- discriminator ;
|
||||
- état dans `kb_sol_ops_processing_ledger`.
|
||||
|
||||
## Frontière Rust store
|
||||
|
||||
`kb_store_core` définit les contrats indépendants du backend : DTOs, entities, health, pagination, erreurs et traits repository.
|
||||
|
||||
`kb_store_pg` implémente PostgreSQL : pool, migrations, requêtes SQL, repositories concrets et diagnostics du schéma courant.
|
||||
|
||||
Les structures Rust ne doivent pas être placées dans `queries/`. Les requêtes SQL, les binds et l'exécution SQL restent dans `queries/`; les types proches des lignes SQL restent dans `entities/`; les contrats applicatifs restent dans `dtos/`; les APIs de stockage restent dans `repositories/`.
|
||||
|
||||
## Statut `0.2.0`
|
||||
|
||||
`0.2.0` est un jalon de cadrage. Il ne doit pas introduire un grand schéma SQL. Les migrations présentes avant cette convention doivent être neutralisées ou remplacées avant d'être exécutées sur une base réelle.
|
||||
|
||||
## Ajout `0.3.4` — ledger de traitement
|
||||
|
||||
La table `kb_sol_ops_processing_ledger` devient la source de vérité de l’idempotence par processor. Pour l’extraction core, elle mémorise la signature, le hash canonique, la version, le statut, le nombre de tentatives et le dernier diagnostic.
|
||||
|
||||
L’écriture du succès ledger, le passage raw à `core_extracted` et le graphe core sont commités ensemble. Un statut `failed` reste rejouable et ne doit jamais être interprété comme une transaction canonique invalide de façon définitive.
|
||||
@@ -0,0 +1,179 @@
|
||||
<!-- file: docs/DECODER_MATERIALIZATION_CONTRACTS.md -->
|
||||
<!-- version: 17 -->
|
||||
|
||||
# Contrats communs de décodage et de matérialisation
|
||||
|
||||
## Objet
|
||||
|
||||
La version `0.4.0` introduit l’infrastructure backend-agnostique utilisée par les futurs décodeurs Solana Core, SPL et protocolaires. Elle ne cherche pas encore à décoder maximalement un protocole précis.
|
||||
|
||||
Le flux commun est :
|
||||
|
||||
```text
|
||||
core instruction contextualisée
|
||||
-> dispatch déterministe
|
||||
-> observation décodée versionnée
|
||||
-> persistance atomique decode + couverture + ledger
|
||||
-> matérialisation optionnelle explicitement autorisée
|
||||
-> persistance atomique mat + ledger
|
||||
```
|
||||
|
||||
## Input contextualisé
|
||||
|
||||
`CoreInstructionReplayInput` reste l’unique contrat d’entrée commun. Son contrat passe à la version `2` dans `0.4.1-pre.014`. Il contient la signature, le slot, le statut et l’erreur on-chain, le chemin stable de l’instruction, le program ID, les comptes résolus dans leur ordre original, le payload brut déterministe et son hash, toutes les instructions outer ordonnées par index numérique, les inner instructions descendantes, les logs reliés prudemment, les changements de balances et la version du contrat core.
|
||||
|
||||
`outer_instructions_json` est un tableau stable dont chaque entrée contient `instructionIndex`, `instructionPath`, `programId`, `payloadJson` et `payloadHash`. L’instruction cible est incluse. Cette projection provient des tables core existantes et ne nécessite aucune migration SQL.
|
||||
|
||||
Le hash de replay est calculé à partir de la sérialisation JSON canonique de cet input. Le skip exige la même étape, le même processor, la même version, la même clé d’entrée, le même hash et un statut ledger réussi.
|
||||
|
||||
Le passage au contrat `2` modifie légitimement les hashes existants, car les payloads outer participent désormais à la sérialisation. Le pipeline reste en version `1` : la version du contrat core et le nouveau contexte suffisent à invalider les anciens skips. Un force replay est requis après mise à niveau.
|
||||
|
||||
## Contrat de décodeur
|
||||
|
||||
`InstructionDecoder` expose :
|
||||
|
||||
- une identité stable `name/version` ;
|
||||
- les programmes et surfaces supportés ;
|
||||
- une matrice de couverture déclarée ;
|
||||
- une reconnaissance déterministe ;
|
||||
- un résultat terminal `decoded`, `ignored`, `unsupported` ou `failed` ;
|
||||
- zéro ou plusieurs observations typées uniquement pour `decoded` ;
|
||||
- une preuve, une confiance et des diagnostics structurés.
|
||||
|
||||
Le dispatch est ordonné par compatibilité exacte, priorité déclarée, surface, code d’entrée et identité stable. Il ne dépend ni du nom de la crate ni de l’ordre d’enregistrement implicite.
|
||||
|
||||
Avant la lecture du store, le pipeline calcule le périmètre effectif des programmes à partir de l’union des `program_id` déclarés par les décodeurs activés. Lorsque l’opérateur fournit un filtre explicite, ce filtre doit être un sous-ensemble exact de ces programmes ; une valeur incompatible est refusée avant toute sélection SQL. Cette règle empêche un décodeur natif de consommer par défaut des instructions SPL ou protocolaires et évite de rejouer indéfiniment le même lot `unmatched` sans rapport avec les processors choisis.
|
||||
|
||||
Une instruction inconnue d’un programme reconnu doit être classée sans produire de faux événement. Un `unmatched` reste possible lorsque le program ID est supporté mais que la reconnaissance contextuelle refuse l’input ; il ne modifie pas globalement le lifecycle de l’instruction pour ne pas empêcher un autre décodeur futur de la traiter.
|
||||
|
||||
## Transactions on-chain échouées
|
||||
|
||||
Une transaction échouée reste décodable lorsqu’un input structurel existe. Toute observation conserve :
|
||||
|
||||
```text
|
||||
transaction_failed
|
||||
transaction_error
|
||||
observation_committed
|
||||
```
|
||||
|
||||
`observation_committed` doit être faux lorsque la transaction a été annulée. Ces observations peuvent décrire une instruction tentée, un événement loggé avant l’erreur, une classification d’échec, une consommation de compute ou une surface appelée.
|
||||
|
||||
Elles ne peuvent pas produire automatiquement un trade réussi, une modification de liquidité réussie, un changement confirmé de catalogue ou une candle normale. `EventMaterializer` applique une politique explicite par famille. Les familles `trade`, `liquidity` et `lifecycle` sont refusées avant l’appel au matérialiseur lorsque la source est échouée ou non commitée. Une seconde barrière valide ensuite les familles de sortie et n’autorise, dans ce contexte, que les matérialisations d’audit ou de risque.
|
||||
|
||||
## Persistance
|
||||
|
||||
Les tables actives sont :
|
||||
|
||||
- `kb_sol_decode_events` ;
|
||||
- `kb_sol_decode_coverage_declarations` ;
|
||||
- `kb_sol_decode_coverage_observations` ;
|
||||
- `kb_sol_mat_events` ;
|
||||
- `kb_sol_ops_processing_ledger`.
|
||||
|
||||
Aucun schéma PostgreSQL applicatif explicite n’est créé. Les payloads et preuves JSONB utilisent des colonnes suffixées par `_jsonb`.
|
||||
|
||||
Le décodage persiste dans une transaction unique les observations, la couverture, l’état lifecycle de l’instruction et le ledger. La matérialisation persiste également ses sorties et son ledger dans une transaction unique. Un rollback à la dernière étape doit donc supprimer toutes les écritures précédentes de la même tentative.
|
||||
|
||||
Toute exécution non skippée remplace atomiquement les sorties du même processor, de la même version et de la même clé d’entrée. Le force replay contourne uniquement le skip version/hash ; il ne supprime jamais les sorties d’un autre processor, d’une autre version ou d’une autre clé. Les versions antérieures restent disponibles pour l’audit.
|
||||
|
||||
Lorsqu’un force replay contient une liste explicite de signatures, le pipeline retire le filtre de lifecycle pour cette sélection bornée. Le même contournement est autorisé sans signatures uniquement lorsque l’opérateur active explicitement le mode « toutes les signatures » ; la sélection reste alors limitée par les programmes compatibles, les instruction paths, les slots éventuels et la limite. Sans signatures ni cette autorisation explicite, la requête est refusée.
|
||||
|
||||
Les déclarations de couverture sont synchronisées comme un snapshot du couple processor/version. Le résultat de persistance distingue les lignes réellement insérées, modifiées, supprimées du snapshot ou strictement inchangées ; une déclaration identique est comptée comme `skipped`, pas comme une nouvelle insertion.
|
||||
|
||||
## Couverture
|
||||
|
||||
La couverture compare les entrées déclarées et observées, puis agrège :
|
||||
|
||||
- reconnaissance ;
|
||||
- décodage ;
|
||||
- matérialisation ;
|
||||
- erreurs ;
|
||||
- inconnues ;
|
||||
- transactions réussies ;
|
||||
- transactions échouées.
|
||||
|
||||
Une surface ne peut pas être considérée comme clôturée tant que toutes les instructions, événements et discriminants connus, y compris historiques et non liés au trading, ne sont pas classifiés.
|
||||
|
||||
## Traçabilité structurée
|
||||
|
||||
Chaque campagne reçoit un `campaign_id` process-local stable, propagé de la démo jusqu’aux décisions de dispatch, de ledger, de décodage, de matérialisation et de persistance. Des spans imbriqués de campagne, input et processor transmettent aussi ce contexte aux événements émis par les décodeurs et les stores sans modifier leurs contrats. Elle doit permettre de reconstruire précisément les décisions prises sans requête SQL libre. Les événements `debug` utilisent un champ `action` stable et conservent au minimum, selon l’étape :
|
||||
|
||||
- le nombre de signatures, un échantillon borné et la sélection normalisée ;
|
||||
- les filtres demandés puis les `program_id` et états effectivement appliqués ;
|
||||
- la signature, le slot, l’instruction path, le program ID et la clé d’input ;
|
||||
- le processor et sa version ;
|
||||
- le hash déterministe pertinent ;
|
||||
- la décision de dispatch, y compris lorsque `recognize` n’est pas appelé à cause d’un program ID incompatible ;
|
||||
- le résultat de reconnaissance et de décodage ;
|
||||
- le contrôle du ledger, le motif exact du skip ou son contournement par force replay ;
|
||||
- le début et la validation de la persistance PostgreSQL ;
|
||||
- le statut terminal et les compteurs par processor.
|
||||
|
||||
Les targets stables sont :
|
||||
|
||||
```text
|
||||
kb_app_demo.demo_decode_replay
|
||||
kb_app_demo.frontend.demo_decode_replay
|
||||
kb_pipeline.decode_replay
|
||||
kb_store_pg.decode_pipeline
|
||||
kb_decoder_solana_core
|
||||
```
|
||||
|
||||
Les signatures, slots, paths et program IDs sont des identifiants publics on-chain. Les listes complètes de signatures ne sont pas répétées à chaque couche : les événements de campagne conservent un compteur et un petit échantillon, tandis que les événements par input conservent la signature exacte. Les traces ne doivent pas enregistrer de DSN, secret, clé privée ni payload brut complet. Le payload d’instruction est représenté par son hash déterministe.
|
||||
|
||||
## Orchestration
|
||||
|
||||
`kb_pipeline::execute_decode_replay` fournit :
|
||||
|
||||
- sélection bornée par signatures, états, slots, program IDs et instruction paths ;
|
||||
- zéro, un ou plusieurs décodeurs compatibles selon la politique choisie ;
|
||||
- concurrence bornée ;
|
||||
- arrêt coopératif ;
|
||||
- skip version/hash ;
|
||||
- force replay ;
|
||||
- résumé par processor et statut ;
|
||||
- matérialisation optionnelle après succès de la persistance decode.
|
||||
|
||||
La fenêtre Tauri `demo_decode_replay` ne contient aucune logique SQL libre. Elle construit une requête typée, appelle le pipeline et affiche les diagnostics read-only.
|
||||
|
||||
## Projections natives actives
|
||||
|
||||
`kb_materializer_lifecycle::LifecycleMaterializer` est enregistré dans `demo_decode_replay` à partir de `0.4.1-pre.003`. Son applicabilité est filtrée par surface, entrée et paramètres avant toute consultation du ledger ou application de politique. `pre.017` étend ses familles acceptées à `Lifecycle`, `Admin` et `Audit`, mais uniquement pour des observations exactes qui produisent réellement une mutation lifecycle. `pre.018` ajoute l’initialisation/la fermeture des rapports Slashing sous `slashing_violation_report`, sans transformer le rapport en pénalité de stake. Il applique toujours `SuccessfulCommittedOnly`.
|
||||
|
||||
Les sorties stables actives sont :
|
||||
|
||||
- `address_lookup_table:<operation>:0` pour les cinq mutations ALT ;
|
||||
- `program_loader:<surface>:<operation>:0` pour les mutations loader stables ;
|
||||
- `feature_gate:revoke_pending_activation:0` pour la révocation Feature Gate ;
|
||||
- `durable_nonce_account:<operation>:0` pour initialize, advance, authorize, upgrade et withdraw du System Program ;
|
||||
- `zk_proof_context:<operation>:0` pour l’initialisation d’un contexte ZK ElGamal demandée par une vérification réussie et pour sa fermeture.
|
||||
|
||||
`authorize_nonce_account` reste décodé en famille `Admin`, mais sa mutation d’autorité durable est promue en sortie `Lifecycle`. Les vérifications ZK ElGamal restent des événements `Audit`; elles ne sont acceptées par le matérialiseur que lorsque `contextStateRequested = true`. Une preuve sans contexte n’est jamais proposée au matérialiseur.
|
||||
|
||||
Le retrait d’un nonce account conserve une sémantique conditionnelle. Le runtime détruit le compte lorsque la totalité du solde est retirée ; l’instruction seule ne suffit pas à reconstruire de manière certaine l’état final transactionnel du compte. La projection décrit donc l’opération commitée, conserve le montant demandé et indique explicitement que l’état final n’est pas capturé. Les deltas SOL ne sont pas dupliqués.
|
||||
|
||||
La création d’un contexte ZK conserve le compte cible, son autorité et le type de preuve, sans matérialiser les octets de preuve. La fermeture conserve le compte, la destination des lamports et l’autorité, puis décrit le reset vers le System Program. L’ancien ZK Token Proof Program n’est jamais matérialisé : son runtime actuel est un stub sans effet et sa sémantique historique n’est pas attribuable à une transaction sans preuve de version.
|
||||
|
||||
Le hash d’entrée matérialiseur reste dérivé de l’observation décodée complète. Le ledger conserve séparément les processors `solana_native_lifecycle`, `solana_native_admin` et `solana_native_compliance_audit`, leur version, la clé d’input et le hash. Une transaction échouée ou une observation non commitée est refusée avant toute sortie mutable.
|
||||
|
||||
`pre.020` répartit les responsabilités : create/allocate System restent dans lifecycle ; assignations System, Config `store` et changements d’autorité Loader appartiennent à `kb_materializer_admin` ; writes/copies de bytecode Loader appartiennent à `kb_materializer_compliance_audit`. Les transferts SOL ne sont pas rematérialisés, car les balance changes core en sont la source canonique.
|
||||
|
||||
## Projections natives restantes
|
||||
|
||||
La couverture maximale d’un décodeur n’implique pas une matérialisation systématique. Une projection n’est ajoutée que lorsqu’elle possède une identité stable, un état cible explicite, une politique d’idempotence et suffisamment de contexte pour ne pas reconstruire une mutation fictive.
|
||||
|
||||
Les prochaines projections sont réparties par propriétaire :
|
||||
|
||||
- `kb_materializer_lifecycle` : créations et allocations System, sans dupliquer les deltas SOL ;
|
||||
- `kb_materializer_admin` : assignations System, écritures Config opaques commitées et changements d’autorité Loader ;
|
||||
- `kb_materializer_compliance_audit` : écritures et copies de bytecode Loader, avec hash et préfixe borné sans payload complet ;
|
||||
- `kb_materializer_staking` : intentions/transitions Stake et Vote commitées. `pre.021` active des projections instructionnelles pour comptes Stake/Vote, autorités, lockup, vote state, retraits et rewards ; un snapshot final exige toujours l’état antérieur/suivant du compte, les crédits cumulés et les sysvars ;
|
||||
- un matérialiseur transactionnel Compute Budget : profil fusionnant toutes les instructions Compute Budget du message.
|
||||
|
||||
Les surfaces suivantes restent volontairement en decode/audit :
|
||||
|
||||
- précompiles de signature, qui décrivent une vérification runtime sans état métier durable ;
|
||||
- preuves ZK sans compte de contexte, qui n’ont pas de mutation persistante à projeter ;
|
||||
- ancien ZK Token Proof Program, dont le runtime Agave `v4.1.1` est un stub sans effet.
|
||||
|
||||
Chaque ajout futur doit indiquer le matérialiseur propriétaire, la source d’état et la politique de transaction, au lieu d’étendre automatiquement `solana_native_lifecycle`.
|
||||
@@ -0,0 +1,49 @@
|
||||
<!-- file: docs/DECODER_SURFACE_AUDIT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Audit des surfaces de décodage
|
||||
|
||||
Ce document suit les noms de crates de décodeurs réservés, les aliases probables et les surfaces à consolider. Il s'appuie sur les IDL locales du dossier `idls/` et sur les matrices de conception historiques importées dans l'archive de travail.
|
||||
|
||||
## Règle de décision
|
||||
|
||||
Un nom de décodeur devient canonique seulement si au moins un des critères suivants est vérifié :
|
||||
|
||||
- une IDL locale existe avec un `program_id` explicite ;
|
||||
- le `program_id` est prouvé par corpus local ;
|
||||
- le rôle de la surface est stable : DEX effectif, router, orderbook, launchpad, vault, lending, staking, etc. ;
|
||||
- le nom respecte la nomenclature `<protocol>_<surface>` sans alias marketing ambigu.
|
||||
|
||||
Un nom historique sans `program_id` local reste en statut `alias_candidate` ou `watchlist`. Il ne doit pas recevoir de logique métier avant consolidation.
|
||||
|
||||
## Aliases et doublons à traiter
|
||||
|
||||
| Groupe | Canonique recommandé | Crates ou noms concurrents | Statut | Décision provisoire |
|
||||
|------------------------|-----------------------------------------------------|-------------------------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| OKX Labs | `kb_decoder_okx_lab_v1` | `kb_decoder_okx_v1`, `kb_decoder_onchain_labs_dex_v1` | `alias_candidate` | L'IDL locale existe pour `okx_lab_v1` avec le programme `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma`. Les noms `okx_v1` et `onchain_labs_dex_v1` ne doivent pas être implémentés séparément sans preuve contraire. |
|
||||
| OKX Router | `kb_decoder_okx_dex_router` | `kb_decoder_okx_v2`, `kb_decoder_onchain_labs_dex_v2` | `alias_candidate` | L'IDL locale existe pour `okx_dex_router` avec le programme `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u`. Les noms `okx_v2` et `onchain_labs_dex_v2` restent des noms historiques à vérifier. |
|
||||
| GooseFX | `kb_decoder_goosefx_v2`, `kb_decoder_goosefx_gamma` | `kb_decoder_goosefx_v1` | `watchlist` | `goosefx_v2` et `goosefx_gamma` ont des IDL locales et des programmes distincts. `goosefx_v1` reste une entrée historique sans IDL locale dans l'archive courante. |
|
||||
| Fusion AMM | `kb_decoder_fusion_amm` | `kb_decoder_fusionamm` | `alias_candidate` | L'IDL locale se nomme `fusion_amm`, mais son metadata interne peut utiliser `fusionamm`. Le crate canonique doit rester lisible : `fusion_amm`. |
|
||||
| PancakeSwap | `kb_decoder_pancakeswap` | `kb_decoder_pancake_swap` | `alias_candidate` | L'IDL locale se nomme `pancakeswap`. Le crate avec underscore supplémentaire doit rester non canonique. |
|
||||
| Orca Wavebreak | `kb_decoder_orca_wavebreak` | `kb_decoder_wavebreak` | `alias_candidate` | L'IDL locale se nomme `orca_wavebreak`. Le nom sans protocole est trop ambigu. |
|
||||
| Jupiter Limit Order v2 | `kb_decoder_jupiter_limit_order_v2` | `kb_decoder_jupiter_limit_order_2` | `alias_candidate` | L'IDL locale utilise `jupiter_limit_order_v2`. Le suffixe `_2` ne doit pas être utilisé. |
|
||||
| dFlow | `kb_decoder_dflow_v4` | `kb_decoder_dflow_aggregator_v4` | `alias_candidate` | L'IDL locale se nomme `dflow_v4`. Le nom `dflow_aggregator_v4` peut rester comme alias historique mais ne doit pas porter une implémentation distincte sans `program_id` différent. |
|
||||
| Meteora DAMM v1 | `kb_decoder_meteora_damm_v1` | `kb_decoder_meteora_pools_amm` | `alias_candidate` | Les matrices historiques indiquent que `meteora_pools` peut être un alias de DAMM v1. La consolidation doit être décidée par `program_id`, corpus et naming final. |
|
||||
| Raydium LaunchLab | `kb_decoder_raydium_launchlab` | `kb_decoder_raydium_launchpad` | `naming_mismatch` | L'IDL locale se nomme `raydium_launchlab`. L'ancien nom `launchpad` décrit la famille métier, mais pas forcément la surface canonique. |
|
||||
| Raydium Lock | `kb_decoder_raydium_lock` | `kb_decoder_raydium_liquidity_locking` | `naming_mismatch` | L'IDL locale se nomme `raydium_lock`. Le nom `liquidity_locking` est descriptif. La surface canonique doit être confirmée avant implémentation. |
|
||||
| Raydium Pool v4 | `kb_decoder_raydium_amm_v4` | `kb_decoder_raydium_pool_v4` | `audit_only` | La note historique indique que `raydium_pool_v4` ne doit pas être promu comme décodeur autonome sans `program_id` distinct et corpus local. |
|
||||
|
||||
## Actions recommandées
|
||||
|
||||
- Ne pas supprimer immédiatement les crates aliases tant que le squelette sert de matrice de réservation.
|
||||
- Ne pas implémenter deux décodeurs pour le même `program_id`.
|
||||
- Ajouter dans chaque décodeur un mapping explicite `surface_code`, `program_code` et `program_id` dès que le corpus commence.
|
||||
- Déplacer les aliases historiques vers une table de catalogue ou un document de watchlist quand le support réel commence.
|
||||
- Supprimer ou fusionner les crates aliases seulement dans un delta dédié, avec validation `cargo build` et manifeste de suppression.
|
||||
|
||||
## Surfaces à ajouter ou renommer plus tard
|
||||
|
||||
- Ajouter `kb_decoder_raydium_launchlab` si la décision est de refléter strictement le nom de l'IDL locale.
|
||||
- Vérifier si `kb_decoder_raydium_launchpad` doit devenir un alias documentaire ou rester une surface métier.
|
||||
- Vérifier si `kb_decoder_meteora_pools_amm` doit être fusionné dans `kb_decoder_meteora_damm_v1`.
|
||||
- Vérifier si les crates `okx_v1`, `okx_v2`, `onchain_labs_dex_v1` et `onchain_labs_dex_v2` doivent être supprimés après confirmation des deux programmes OKX canoniques.
|
||||
58
migration/khadhroony-bot2-reference/docs/DELTA_WORKFLOW.md
Normal file
58
migration/khadhroony-bot2-reference/docs/DELTA_WORKFLOW.md
Normal file
@@ -0,0 +1,58 @@
|
||||
<!-- file: docs/DELTA_WORKFLOW.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Workflow delta
|
||||
|
||||
Après le squelette initial, les livraisons doivent être faites sous forme de zip delta.
|
||||
|
||||
## Nommage des zips
|
||||
|
||||
Un delta qui touche la racine du workspace ou plusieurs modules doit utiliser :
|
||||
|
||||
```text
|
||||
khadhroony-bot2_vX.Y.Z-pre.abc.zip
|
||||
```
|
||||
|
||||
Un delta qui ne touche qu'un seul module Rust doit utiliser :
|
||||
|
||||
```text
|
||||
kb_modulename_vX.Y.Z-pre.abc.zip
|
||||
```
|
||||
|
||||
Exemples :
|
||||
|
||||
```text
|
||||
khadhroony-bot2_v0.1.0-pre.001.zip
|
||||
kb_logging_v0.1.0-pre.002.zip
|
||||
```
|
||||
|
||||
## Fichier `delta.md`
|
||||
|
||||
Chaque zip delta doit contenir un fichier `delta.md` non versionné à la racine du zip.
|
||||
|
||||
`delta.md` doit indiquer :
|
||||
|
||||
- les fichiers ajoutés ;
|
||||
- les fichiers modifiés ;
|
||||
- les fichiers à supprimer manuellement ;
|
||||
- les validations exécutées ;
|
||||
- les validations non exécutées ;
|
||||
- les remarques de compatibilité ou d'application du delta.
|
||||
|
||||
## Contenu d'un delta
|
||||
|
||||
Un delta contient uniquement :
|
||||
|
||||
- les fichiers ajoutés ;
|
||||
- les fichiers modifiés ;
|
||||
- `delta.md`.
|
||||
|
||||
Il ne doit pas contenir de fichiers inchangés.
|
||||
|
||||
## Règles
|
||||
|
||||
- Ne pas renvoyer un zip complet sauf demande explicite.
|
||||
- Ne pas modifier `CHANGELOG.md` avant validation d'une version.
|
||||
- Placer les évolutions prévues dans `ROADMAP.md`.
|
||||
- Garder `README.md` descriptif.
|
||||
- Documenter les suppressions dans `delta.md`, car un zip ne supprime pas les anciens fichiers à l'extraction.
|
||||
@@ -0,0 +1,91 @@
|
||||
<!-- file: docs/DEVELOPMENT_ORDER.md -->
|
||||
<!-- version: 9 -->
|
||||
|
||||
# Ordre de développement
|
||||
|
||||
## Principe
|
||||
|
||||
La priorité est de rendre chaque transaction exploitable avant de payer un flux temps réel enrichi. Le développement suit donc l’ordre : acquisition gratuite, transaction canonique, extraction core, décodeurs, matérialisations, puis sources live payantes.
|
||||
|
||||
## Ordre cible après `0.2.x`
|
||||
|
||||
- [x] `0.1.x` : logging, configuration, clients HTTP/WS standard, rôles et démos RPC.
|
||||
- [x] `0.2.0` à `0.2.5` : conventions PostgreSQL, stores raw/core historiques et diagnostics SQL.
|
||||
- [x] `0.3.0` : cadrage Agave local historique, ensuite abandonné comme axe actif après mesures réelles.
|
||||
- [x] `0.3.1` : transaction canonique, observations légères, validation PostgreSQL réelle et consolidation de la baseline SQL.
|
||||
- [x] `0.3.2` : modèle canonique et adaptateur HTTP `getTransaction`.
|
||||
- [x] `0.3.3` : backfill gratuit par signatures, programme, token et pool, avec parcours avant/après et arrêt coopératif borné.
|
||||
- [~] `0.3.4` : extraction core, navigateur de candidats, intégrité et replay validés ; derniers tests automatisés de rollback/annulation à exécuter.
|
||||
- [ ] `0.4.0` : infrastructure commune de décodage et de matérialisation.
|
||||
- [ ] `0.4.1+` : décodeurs et matérialisations Solana Core/SPL.
|
||||
- [ ] `0.5.x` : Pump.
|
||||
- [ ] `0.6.x` : Meteora.
|
||||
- [ ] `0.7.x` : Raydium.
|
||||
- [ ] `0.8.x` : Orca.
|
||||
- [ ] `0.9.x` : Jupiter, routeurs et surfaces complémentaires.
|
||||
- [ ] `0.10.x` : Helius `transactionSubscribe`, Yellowstone gRPC, fallback standard et comparaison fournisseur.
|
||||
- [ ] `0.11.x` : wallet, sécurité et démos devnet.
|
||||
- [ ] `0.12.x` : listeners métier et trading assisté.
|
||||
|
||||
## Ordre technique de stockage
|
||||
|
||||
1. Maintenir une baseline SQL courante ne contenant que les tables actives.
|
||||
2. Normaliser chaque source vers un contrat canonique dans `kb_model`.
|
||||
3. Écrire une transaction unique dans `kb_sol_raw_transactions`.
|
||||
4. Écrire une observation légère dans `kb_sol_obs_transaction_observations`.
|
||||
5. Extraire les tables `kb_sol_core_*` depuis la transaction canonique.
|
||||
6. Ajouter les observations programme/instruction nécessaires aux décodeurs.
|
||||
7. Ajouter les tables `kb_sol_decode_*` et `kb_sol_mat_*` par surface.
|
||||
8. Ajouter les sources live payantes seulement lorsque les décodeurs peuvent exploiter immédiatement le flux.
|
||||
|
||||
## Séquence par surface
|
||||
|
||||
```text
|
||||
program ids
|
||||
-> corpus de signatures
|
||||
-> backfill gratuit
|
||||
-> decodeur maximal
|
||||
-> événements stables
|
||||
-> matérialisation complète
|
||||
-> replay et anti-régression
|
||||
-> listener live
|
||||
```
|
||||
|
||||
## Démos
|
||||
|
||||
`kb_app_demo` reste le shell unique pour :
|
||||
|
||||
- diagnostics DB ;
|
||||
- backfills ;
|
||||
- inspection canonique/core ;
|
||||
- couverture des décodeurs ;
|
||||
- comparaison future des sources live ;
|
||||
- wallet et sécurité.
|
||||
|
||||
## Contraintes permanentes
|
||||
|
||||
- Ne pas créer de schémas PostgreSQL applicatifs explicites.
|
||||
- Utiliser le format `kb_sol_<domain>_<name>`.
|
||||
- Ne pas placer de structures Rust dans `queries/`.
|
||||
- Ne pas faire dépendre les décodeurs du store concret.
|
||||
- Ne pas faire dépendre les décodeurs du fournisseur d’acquisition.
|
||||
- Intégrer Helius et Yellowstone dans `kb_rpc`, sans crate provider séparée.
|
||||
- Ne pas modifier `CHANGELOG.md` avant validation locale du jalon.
|
||||
|
||||
## État de clôture `0.3.4-pre.011`
|
||||
|
||||
L’ordre technique est concrétisé jusqu’à l’écriture core et aux outils de replay :
|
||||
|
||||
```text
|
||||
backfill HTTP
|
||||
-> transaction canonique
|
||||
-> sélection raw bornée
|
||||
-> extraction core pure
|
||||
-> commit PostgreSQL atomique
|
||||
-> ledger version/hash
|
||||
-> sélection et export de corpus
|
||||
```
|
||||
|
||||
Les validations réelles couvrent 70 transactions core, l’intégrité SQL, l’écriture CSV, le skip et le force replay sur 14 signatures. `pre.011` automatise le dernier contrôle de rollback PostgreSQL et rend les attentes réseau/retry annulables.
|
||||
|
||||
L’ancien jalon `0.3.5` est supprimé. Après validation de `pre.011` et mise à jour du changelog, la prochaine version active est directement `0.4.0`.
|
||||
119
migration/khadhroony-bot2-reference/docs/DEVNET_EXECUTION.md
Normal file
119
migration/khadhroony-bot2-reference/docs/DEVNET_EXECUTION.md
Normal file
@@ -0,0 +1,119 @@
|
||||
<!-- file: docs/DEVNET_EXECUTION.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Validation d’exécution Devnet
|
||||
|
||||
Ce document décrit le premier parcours réseau réel de la couche d’exécution native et sa fenêtre Tauri Devnet. Le test backend reste opt-in et aucune activation Mainnet n’est autorisée.
|
||||
|
||||
## Périmètre
|
||||
|
||||
Le parcours exécute une instruction System transfer depuis le wallet temporaire persistant du profil `local_devnet` vers un destinataire éphémère créé uniquement pour le test.
|
||||
|
||||
```text
|
||||
wallet persistant
|
||||
-> vérification du genesis hash Devnet
|
||||
-> contrôle du destinataire et du minimum rent-exempt
|
||||
-> lecture du solde
|
||||
-> airdrop plafonné si nécessaire
|
||||
-> plan System transfer
|
||||
-> blockhash et estimation des frais
|
||||
-> simulation exacte
|
||||
-> signature locale
|
||||
-> envoi avec preflight
|
||||
-> confirmation bornée
|
||||
-> getTransaction
|
||||
-> insertion canonique
|
||||
-> extraction core
|
||||
-> decode replay System
|
||||
```
|
||||
|
||||
Les clés privées restent dans `kb_wallet`. Elles ne sont ni sérialisées dans les résumés, ni envoyées à une WebView, ni écrites dans PostgreSQL ou les logs.
|
||||
|
||||
## Préconditions
|
||||
|
||||
- le profil `local_devnet` doit rester configuré sur le genesis hash Devnet officiel ;
|
||||
- `temporary_wallet_enabled`, `temporary_wallet_persist` et `devnet_send_enabled` doivent être actifs ;
|
||||
- la simulation et la confirmation opérateur doivent rester obligatoires ;
|
||||
- PostgreSQL doit être disponible pour la validation canonique/core/decode ;
|
||||
- le rôle `http_queries` doit accepter `getTransaction` et les lectures d’exécution ;
|
||||
- le rôle `http_transactions` doit accepter simulation, envoi et lecture des statuts.
|
||||
|
||||
Le wallet persistant par défaut est créé sous :
|
||||
|
||||
```text
|
||||
wallets/temporary/local_devnet/local-devnet-operator.json
|
||||
```
|
||||
|
||||
Ce fichier n’est pas chiffré. Il est exclusivement destiné à Localnet/Devnet et ne doit jamais recevoir de fonds réels.
|
||||
|
||||
## Exécution du test réel
|
||||
|
||||
Depuis la racine du workspace :
|
||||
|
||||
```bash
|
||||
KB_DEVNET_EXECUTION_TEST=1 \
|
||||
KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \
|
||||
KB_DEVNET_WALLET_DIR='./wallets/temporary/local_devnet' \
|
||||
KB_DEVNET_AIRDROP_LAMPORTS=0 \
|
||||
KB_DEVNET_TRANSFER_LAMPORTS=1000000 \
|
||||
cargo test -p kb_pipeline optional_devnet_system_transfer_from_env -- --nocapture
|
||||
```
|
||||
|
||||
Variables :
|
||||
|
||||
- `KB_DEVNET_EXECUTION_TEST=1` autorise explicitement le test réseau ;
|
||||
- `KB_POSTGRES_TEST_URL` sélectionne la base de validation ;
|
||||
- `KB_DEVNET_WALLET_DIR` peut isoler le wallet utilisé par le test ;
|
||||
- `KB_DEVNET_AIRDROP_LAMPORTS` fixe le montant maximal demandé au faucet lorsque le solde est insuffisant ;
|
||||
- `KB_DEVNET_TRANSFER_LAMPORTS` fixe le transfert System réel.
|
||||
|
||||
Les valeurs restent soumises aux plafonds de `ExecutionConfig`. Une valeur d’airdrop supérieure à `devnet_airdrop_max_lamports` ou un transfert supérieur à `devnet_max_spend_lamports` est refusé avant tout appel mutable.
|
||||
|
||||
## Résultat attendu
|
||||
|
||||
Pour un destinataire inexistant, le pipeline appelle `getAccountInfo`, puis `getMinimumBalanceForRentExemption` avec une longueur de données nulle. Le transfert est refusé avant simulation lorsqu’il est inférieur au minimum retourné. Lorsqu’une simulation échoue malgré ces contrôles, l’erreur remontée conserve l’erreur runtime structurée et les vingt premières lignes de logs.
|
||||
|
||||
Le test doit confirmer :
|
||||
|
||||
- cluster classifié `Devnet` ;
|
||||
- simulation exacte réussie ;
|
||||
- signature locale vérifiée ;
|
||||
- signature RPC identique à la signature calculée ;
|
||||
- statut `confirmed` ou `finalized` ;
|
||||
- transaction canonique présente ou déjà existante ;
|
||||
- extraction core terminée ou déjà courante ;
|
||||
- decode replay System terminé sans input unmatched ou failed.
|
||||
|
||||
L’airdrop peut être absent lorsque le wallet possède déjà un solde suffisant. Le destinataire reste éphémère et sa clé privée n’est pas persistée. Après signature, tout échec ultérieur conserve la signature dans le résumé avec un diagnostic de l’étape concernée.
|
||||
|
||||
## Fenêtre Tauri
|
||||
|
||||
`demo_execution_solana_core` est accessible depuis la fenêtre principale. Elle expose uniquement les profils Devnet utilisant un wallet temporaire persistant. Elle permet de générer une adresse destinataire jetable, de simuler exactement le message ou de signer/envoyer après confirmation opérateur. Le plan, les comptes, les signataires, la simulation, la confirmation et les résultats canonical/core/decode sont affichés en lecture seule. La clé privée du wallet source reste exclusivement dans le backend Rust.
|
||||
|
||||
Un airdrop demandé par la fenêtre reste soumis au plafond du profil et peut échouer lorsque le faucet public est limité. Un wallet déjà financé peut être utilisé avec une valeur d’airdrop nulle.
|
||||
|
||||
## Limites
|
||||
|
||||
- le faucet Devnet peut appliquer des limites temporaires ;
|
||||
- `getTransaction` peut devenir disponible après la confirmation de statut, d’où les retries bornés ;
|
||||
- le parcours utilise actuellement une transaction legacy avec recent blockhash ;
|
||||
- durable nonce, ALT et transactions v0 seront validés dans des tranches dédiées ;
|
||||
- aucune opération Mainnet n’est autorisée par ce test.
|
||||
|
||||
## Validation de clôture `0.4.2`
|
||||
|
||||
Le 13 juillet 2026, la commande complète a été exécutée avec :
|
||||
|
||||
```bash
|
||||
KB_DEVNET_EXECUTION_TEST=1 \
|
||||
KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \
|
||||
KB_DEVNET_WALLET_DIR='./wallets/temporary/local_devnet' \
|
||||
KB_DEVNET_AIRDROP_LAMPORTS=0 \
|
||||
KB_DEVNET_TRANSFER_LAMPORTS=1000000 \
|
||||
cargo test -p kb_pipeline -- --nocapture
|
||||
```
|
||||
|
||||
Les 56 tests de `kb_pipeline` ont réussi, y compris `optional_devnet_system_transfer_from_env`. Le wallet était déjà financé, aucun airdrop n’a été demandé et le parcours a confirmé la chaîne complète jusqu’au replay post-exécution. Les 45 tests de `kb_store_pg` ont également réussi avec PostgreSQL réel.
|
||||
|
||||
Cette validation ne généralise pas l’autorisation à toutes les opérations administratives stateful. Elle prouve le parcours d’orchestration commun ; ALT, Config, Feature, Slashing, ZK, Stake, Vote et loaders restent soumis à leurs contrôles spécifiques avant toute future activation mutable.
|
||||
|
||||
387
migration/khadhroony-bot2-reference/docs/EXECUTION_MODEL.md
Normal file
387
migration/khadhroony-bot2-reference/docs/EXECUTION_MODEL.md
Normal file
@@ -0,0 +1,387 @@
|
||||
<!-- file: docs/EXECUTION_MODEL.md -->
|
||||
<!-- version: 29 -->
|
||||
|
||||
# Modèle d'exécution
|
||||
|
||||
Ce document décrit la couche d'exécution universelle du workspace.
|
||||
|
||||
## Séparation des responsabilités
|
||||
|
||||
Un décodeur lit une transaction existante. Un exécuteur prépare une opération future. Les deux couches peuvent partager une surface canonique, mais elles ne doivent pas dépendre l'une de l'autre.
|
||||
|
||||
```text
|
||||
kb_decoder_amm_pump_swap -> observation, décodage, audit
|
||||
kb_executor_amm_pump_swap -> construction future d'instructions d'achat/vente
|
||||
```
|
||||
|
||||
## Bibliothèques universelles
|
||||
|
||||
Le trading est le premier consommateur du workspace, pas la limite de ses crates. Les exécuteurs doivent pouvoir être réutilisés par une CLI, un worker, un outil d'administration, une application de jeu, une application Tauri ou un service autonome.
|
||||
|
||||
Cette universalité implique :
|
||||
|
||||
- un intent typé indépendant de l'interface utilisateur ;
|
||||
- un plan déterministe sans I/O ;
|
||||
- des builders couvrant les opérations officiellement appelables, y compris administratives ou dangereuses ;
|
||||
- des politiques de sécurité séparées du builder ;
|
||||
- une exposition UI volontairement plus étroite que la capacité de la bibliothèque ;
|
||||
- aucune dépendance vers les décodeurs pour construire ou signer une transaction.
|
||||
|
||||
Une opération dangereuse ne doit pas être supprimée de l'exécuteur. Elle doit être implémentée, testée, classifiée et soumise à une politique renforcée. `kb_app_demo` peut ne jamais l'exposer.
|
||||
|
||||
## Couches
|
||||
|
||||
- `kb_execution_api` définit les contrats communs des exécuteurs.
|
||||
- `kb_execution_safety` regroupe les validations avant simulation, signature ou envoi.
|
||||
- `kb_execution_solana` assemble les plans en messages/transactions Solana et orchestre la signature sans RPC.
|
||||
- `kb_executor_solana_core` construit les instructions des programmes natifs.
|
||||
- `kb_wallet` isole les secrets et fournit des signataires.
|
||||
- `kb_rpc` fournit simulation, envoi et confirmation.
|
||||
- `kb_pipeline` peut orchestrer la validation post-exécution par réingestion et replay.
|
||||
- `kb_app_demo` ne fait qu'orchestrer une sélection sûre de ces capacités.
|
||||
|
||||
## Pipeline obligatoire
|
||||
|
||||
```text
|
||||
intent
|
||||
-> execution plan
|
||||
-> policy checks
|
||||
-> transaction build
|
||||
-> simulation / dry-run
|
||||
-> signature
|
||||
-> send
|
||||
-> confirmation
|
||||
-> post-execution decode replay validation
|
||||
```
|
||||
|
||||
Construction, signature, envoi et validation ne doivent jamais être fusionnés dans une fonction opaque.
|
||||
|
||||
## Politique de couverture
|
||||
|
||||
`Supported` signifie que le builder et son contrat de comptes/signataires sont implémentés. `Unsupported(reason)` reste acceptable uniquement lorsqu'une surface n'est pas appelable par transaction, a été retirée du runtime, ne dispose pas encore d'une source officielle suffisante ou constitue une dette temporaire explicitement planifiée.
|
||||
|
||||
L'objectif de clôture de `kb_executor_solana_core` est de ne laisser aucune instruction native officiellement appelable oubliée. Les variantes historiques uniquement décodables ne doivent pas être artificiellement réémises.
|
||||
|
||||
## Adaptateurs RPC de simulation
|
||||
|
||||
`0.4.2-pre.004` ajoute dans `kb_rpc` les premières lectures nécessaires avant signature :
|
||||
|
||||
```text
|
||||
getGenesisHash
|
||||
getLatestBlockhash
|
||||
getFeeForMessage
|
||||
simulateTransaction
|
||||
```
|
||||
|
||||
Le genesis hash sert à classifier les clusters publics connus. Le réseau de production est nommé Mainnet dans le contrat public du workspace ; le hash est inchangé et certains endpoints/CLI conservent encore l’alias historique `mainnet-beta`. Un hash inconnu reste explicite et n’est pas automatiquement assimilé à Devnet ou Mainnet. Le blockhash et sa dernière block height valide sont conservés séparément. L’estimation des frais accepte une valeur nulle lorsque le message référence un blockhash expiré.
|
||||
|
||||
La simulation accepte une transaction base64 non signée lorsque `sigVerify = false`; `replaceRecentBlockhash = true` permet au nœud de remplacer le blockhash avant simulation. Les erreurs runtime restent un résultat de simulation typé et ne sont pas confondues avec une erreur HTTP ou JSON-RPC.
|
||||
|
||||
`kb_rpc` ne fabrique pas le contexte de sécurité : cluster attendu, âge du blockhash, nonce account et nonce authority sont fournis explicitement lors de la conversion vers `ExecutionSimulationResult`. Depuis `pre.013`, `getAccountInfo` possède aussi un mode données complètes borné : le mode metadata-only conserve `dataSlice.length = 0`, tandis que `confirmed_with_data(max_data_bytes)` exige un tuple base64 complet dont la longueur décodée égale `space`.
|
||||
|
||||
## Assemblage et signature Solana
|
||||
|
||||
`0.4.2-pre.005` introduit `kb_execution_solana`, frontière commune entre les exécuteurs et les adaptateurs RPC. La crate convertit les `PlannedInstruction` en instructions SDK, compile le message avec son fee payer et sa source de blockhash, puis vérifie que les signataires réellement compilés correspondent exactement au contrat du plan.
|
||||
|
||||
La transaction non signée fournit deux sorties distinctes :
|
||||
|
||||
```text
|
||||
message base64 -> getFeeForMessage
|
||||
transaction non signée -> simulateTransaction(sigVerify=false, replaceRecentBlockhash=false)
|
||||
```
|
||||
|
||||
Le résultat de simulation est lié au hash exact du message et à la valeur de blockhash ou nonce compilée. Une simulation avec remplacement de blockhash reste utile au diagnostic, mais ne peut pas autoriser la signature du message original. La signature refuse un résultat provenant d’un autre message ou d’une autre valeur nonce, réévalue `kb_execution_safety`, exige la décision `Allow`, puis résout tous les signataires via l’interface Solana `Signer`. Les signataires manquants, supplémentaires ou dupliqués sont refusés. La transaction signée reste en mémoire/backend et n’est ni envoyée ni exposée automatiquement à Tauri.
|
||||
|
||||
La sérialisation transactionnelle utilise le schéma officiel `wincode` fourni par les types Solana et applique la limite réseau de 1 232 octets. Aucun appel direct à `bincode` n’est ajouté.
|
||||
|
||||
### Chemin durable nonce réel — `pre.013`
|
||||
|
||||
Le lifecycle d’un compte nonce et la consommation d’un durable nonce sont deux contrats distincts. Les opérations `initialize`, `advance`, `authorize`, `withdraw` et `upgrade` administrent le compte avec une politique `Latest`. Une transaction métier utilisant un nonce durable suit le chemin stateful suivant :
|
||||
|
||||
```text
|
||||
getAccountInfo complet et borné à la taille nonce officielle
|
||||
-> validation owner System Program / non-executable / space exact
|
||||
-> décodage wincode Versions::Current(State::Initialized)
|
||||
-> concordance compte et autorité avec ExecutionBlockhashPolicy
|
||||
-> Message::new_with_nonce
|
||||
-> AdvanceNonceAccount en instruction 0
|
||||
-> valeur nonce comme recent_blockhash du message
|
||||
-> simulation exacte sans remplacement
|
||||
-> signature du même hash de message et de la même valeur nonce
|
||||
```
|
||||
|
||||
`kb_executor_solana_core` ajoute l’autorité nonce aux signataires requis du plan métier. `kb_execution_solana` refuse les états legacy ou non initialisés, conserve le plan source immuable, expose un plan effectif avec l’avance injectée et exige que le SDK reconnaisse la transaction comme durable nonce. La lecture RPC, l’assemblage, la simulation et la signature restent des étapes séparées.
|
||||
|
||||
|
||||
### Builders Address Lookup Table — `pre.015`
|
||||
|
||||
`kb_executor_solana_core` couvre les cinq opérations client de l’interface Address Lookup Table : création, extension, gel, désactivation et fermeture. Le builder stateless garantit le program ID, le payload, l’ordre des comptes et les signataires exacts, mais ne prétend pas connaître l’état courant du compte ni le coût de rent calculé par le runtime.
|
||||
|
||||
```text
|
||||
intent ALT typé
|
||||
-> builder officiel exact
|
||||
-> plan avec autorité/payer explicites
|
||||
-> lecture RPC stateful du compte et du slot
|
||||
-> contrôle owner / autorité / état / capacité / cooldown
|
||||
-> calcul du top-up rent-exempt et simulation exacte
|
||||
-> politique de dépense
|
||||
-> signature et envoi
|
||||
```
|
||||
|
||||
La création dérive la table depuis l’autorité et un slot récent. L’extension conserve l’ordre des adresses, exige une liste non vide et borne l’input à la capacité maximale officielle ; la capacité restante et le top-up effectif doivent être contrôlés sur le compte réel. Le gel est irréversible. La fermeture n’est autorisée qu’après désactivation et expiration du cooldown lié aux slots. `requested_spend_lamports` reste nul dans le plan ALT parce qu’aucun montant de rent n’est encodé directement dans l’instruction ; l’orchestrateur doit calculer le top-up à partir du compte réel et du minimum rent-exempt, le comparer au plafond de dépense, puis confirmer l’instruction par simulation.
|
||||
|
||||
|
||||
### Précompiles de signature — `pre.016`
|
||||
|
||||
Les précompiles Ed25519, secp256k1 et secp256r1 sont des vérifications natives, pas des signatures de transaction. Leur plan ne déclare aucun compte applicatif ni signataire propre ; le fee payer signe uniquement la transaction. Le programme métier qui consomme la preuve doit inspecter l’instruction correspondante et appliquer lui-même son contrat d’autorisation.
|
||||
|
||||
```text
|
||||
message + signature + clé/adresse
|
||||
-> builder inline ou table d’offsets officielle
|
||||
-> instruction précompile sans comptes
|
||||
-> positionnement transactionnel exact
|
||||
-> programme consommateur qui inspecte le sysvar d’instructions
|
||||
-> simulation exacte
|
||||
-> signature Solana du fee payer et des signataires métier distincts
|
||||
```
|
||||
|
||||
Les formes inline Ed25519 et secp256r1 utilisent `u16::MAX` comme référence à leurs propres données. secp256k1 encode des index d’instruction `u8` sans sentinelle ; la forme inline et les références locales du builder exigent donc que l’instruction secp256k1 soit à l’index transactionnel `0`. Un futur assembleur multi-plans doit refuser ou réécrire explicitement toute combinaison qui violerait cette position.
|
||||
|
||||
Les tables avancées acceptent des références externes et un buffer local ajouté après les offsets. Le builder borne le nombre d’entrées à 255, la taille totale à 65 535 octets et les plages locales connues. Il ne lit pas les données des autres instructions : leur cohérence cryptographique est vérifiée par le runtime et leur signification métier par le programme consommateur.
|
||||
|
||||
La signature secp256r1 est fournie au format compact `r || s`; le builder exige des composants non nuls et la forme low-S imposée par le runtime. Le builder secp256k1 reçoit la signature compacte, le recovery ID et l’adresse Ethereum déjà dérivée ; il ne manipule aucune clé privée et n’active aucun helper `bincode`. Le runtime secp256k1 ne garantit pas la canonicalité low-S : lorsqu’elle est requise, cette politique appartient au programme consommateur ou à une validation métier explicite.
|
||||
|
||||
|
||||
### Config, Feature, Slashing et ZK ElGamal — `pre.017`
|
||||
|
||||
Les opérations administratives natives restent des plans déterministes sans accès RPC implicite.
|
||||
|
||||
```text
|
||||
état/rent fourni par l’orchestrateur
|
||||
-> builder exact
|
||||
-> plan et signataires
|
||||
-> lecture stateful de confirmation
|
||||
-> simulation exacte
|
||||
```
|
||||
|
||||
Config encode manuellement le contrat `ConfigKeys + données sérialisées` afin de ne pas activer le helper `bincode` de l’interface. Feature reproduit les séquences officielles d’activation et de révocation. Les montants de rent sont des entrées explicites comptabilisées dans le plafond de dépense.
|
||||
|
||||
La preuve de bloc dupliqué Slashing est indivisible : transfert de préfinancement, instruction Ed25519 et instruction Slashing doivent conserver leurs positions. Le plan refuse donc durable nonce et exige un recent blockhash normal. Le compte de preuve, la fenêtre temporelle, le montant rent-exempt et la rétention avant fermeture relèvent de l’orchestrateur stateful.
|
||||
|
||||
ZK ElGamal accepte une preuve inline ou une preuve dans un compte, avec création optionnelle d’un contexte déjà préalloué. La fermeture exige l’autorité comme signataire. Le builder vérifie discriminant, taille et comptes, mais ne crée pas le contexte et ne recalcule pas la preuve cryptographique. L’ancien ZK Token Proof est historique et reste `decode-only`.
|
||||
|
||||
## Stake et Vote dans la bibliothèque
|
||||
|
||||
`pre.018` ajoute les vingt-quatre helpers clients actuels de `solana-stake-interface 4.3.1` sous forme de plans déterministes. Les formes composées conservent l’ordre officiel : création System puis initialisation Stake, délégation ajoutée en dernier, allocation/assignation avant split. Les rôles staker/withdrawer sont typés par `StakeAuthorizationKind`.
|
||||
|
||||
`pre.019` ajoute les vingt variantes wire Vote actuelles et quatre créations composées legacy/V2. Les autorités Ed25519/BLS, commissions, collecteurs, votes, switch proofs, compact updates et TowerSync sont encodés avec l’enum officielle `VoteInstruction` et `wincode`. Les créations, retraits et dépôts de récompenses déclarent leurs lamports dans le plafond de dépense.
|
||||
|
||||
La construction reste stateless. Avant simulation puis envoi, l’orchestrateur doit charger et valider l’état des comptes Stake/Vote, les autorités, le lockup, le rent, le minimum de délégation, les epochs d’activation/désactivation, la compatibilité merge/split/move, la version Vote, les collecteurs et l’admissibilité runtime de la tour. `Redelegate` reste historique `decode-only`, car l’interface officielle le déclare déprécié et non activable.
|
||||
|
||||
## Loaders dans la bibliothèque
|
||||
|
||||
`pre.020` ajoute onze plans Loader v3 et neuf plans Loader v4. Loader v3 utilise les helpers de `solana-loader-v3-interface 8.x` et leur schéma `wincode` pour créer des buffers, écrire, déployer, upgrader, modifier les autorités, fermer et étendre. Loader v4 conserve le contrat officiel des sept discriminants, mais son wire est construit localement et explicitement parce que les helpers publiés sont conditionnés par `bincode` et qu’aucun schéma `wincode` n’est exposé.
|
||||
|
||||
Les plans de création conservent l’ordre atomique System puis Loader. Les écritures sont bornées, les offsets contrôlés contre les dépassements, les comptes/signataires reproduisent l’interface officielle et les lamports explicitement transférés sont inclus dans `requested_spend_lamports`. Les loyers calculés dynamiquement par le runtime ne sont pas inventés par le builder.
|
||||
|
||||
Avant simulation, l’orchestrateur stateful doit vérifier owner, état courant, autorité, taille/capacité, rent, existence des comptes Program/ProgramData/buffer, relations dérivées et admissibilité de fermeture ou d’extension. Les opérations Loader restent hors de la démo opérateur. BPF Loader v1/v2 sont historiques `decode-only`; Native Loader correspond au déploiement du logiciel validator et n’expose pas d’instruction client autonome.
|
||||
|
||||
|
||||
## Préflight stateful natif — `pre.022`
|
||||
|
||||
Les builders restent déterministes et sans I/O. `kb_pipeline::inspect_solana_core_stateful_readiness` ajoute une étape séparée avant la simulation pour les opérations dont l’admissibilité dépend de comptes ou de l’époque courante.
|
||||
|
||||
```text
|
||||
SolanaCoreOperation
|
||||
-> plan stateless
|
||||
-> préflight stateful Localnet/Devnet
|
||||
-> rapport Ready / Blocked / NotRequired
|
||||
-> simulation exacte
|
||||
```
|
||||
|
||||
Le préflight vérifie le genesis hash attendu et utilise uniquement des RPC typés. Il couvre :
|
||||
|
||||
- ALT : PDA de création, slot récent, owner, autorité, capacité, rent top-up, état actif/désactivé et cooldown de fermeture ;
|
||||
- Config : absence/réutilisation contrôlée, allocation calculée, owner et espace disponible ;
|
||||
- Feature : compte absent ou System réutilisable, rent mesuré et état pending avant révocation ;
|
||||
- Slashing : compte de preuve complet et borné, deux shreds length-prefixed, fenêtre d’un epoch, rent du rapport, destination retenue et délai de fermeture ;
|
||||
- ZK ElGamal : plage de preuve, owner/space/rent du contexte préalloué, état zéro avant écriture, puis autorité et discriminant avant fermeture.
|
||||
|
||||
Le rapport contient des codes de contrôle stables, des faits mesurés et le plus haut slot de contexte observé. Il ne signe, ne simule et n’envoie aucune transaction. Testnet et Mainnet sont refusés dans cette couche tant que les parcours Localnet/Devnet ne sont pas validés.
|
||||
|
||||
## Préflight stateful SPL Token classique — `0.4.4-pre.005`
|
||||
|
||||
Le même découpage s’applique à SPL Token : `kb_executor_spl_token` construit un plan universel sans I/O, puis `kb_pipeline::inspect_spl_token_stateful_readiness` vérifie l’état Localnet ou Devnet avant simulation. Le décodeur ne réalise aucune lecture RPC.
|
||||
|
||||
Le préflight lit des données complètes bornées et reproduit les layouts classiques Mint, Account et Multisig. Il contrôle notamment owner programme, initialisation, mint/decimals, état frozen, solde et réserve native, delegate/allowance, autorités spécialisées, seuil multisig et rent des comptes préparés. Les montants restent des entiers bruts exacts. Une précondition métier invalide produit `Blocked`; une erreur de transport ou de forme reste une erreur `kb_core`.
|
||||
|
||||
La preuve réseau est séparée en deux niveaux. Le test opt-in de `pre.005` vérifie en lecture seule un `TransferChecked` sur des comptes Devnet fournis explicitement. Le scénario opérateur complet doit ensuite créer des comptes classiques sans ATA, simuler par défaut, et ne signer/envoyer qu’avec une autorisation distincte ; après confirmation, il doit réutiliser hydratation canonique, extraction core, replay, projections et second replay idempotent.
|
||||
|
||||
`pre.005-delta-fix-001` relie ce préflight au chemin d’exécution commun. La simulation constitue une API autonome sans store et conserve le plan, le rapport stateful, le blockhash, les frais et le résultat runtime. La soumission est une seconde API : elle refuse tout signataire absent du wallet de profil, puis applique confirmation, hydratation, extraction, decode replay, requête matérialisée bornée et preuve d’idempotence. Cette restriction de signataire appartient à l’orchestrateur actuel, pas à l’exécuteur universel ni au wire multisig.
|
||||
|
||||
Le 15 juillet 2026, ce parcours a réussi en simulation puis avec un envoi `TransferChecked` réel sur Devnet, entre deux comptes auxiliaires classiques créés sans dépendance ATA. `pre.005-delta-fix-002` durcit la preuve opt-in : résultat d’envoi présent, confirmation `confirmed` ou `finalized`, premier replay sans échec ni refus, matérialisation Token présente, second replay sans échec, refus ou nouvelle sortie, et signature imprimée pour l’audit.
|
||||
|
||||
`0.4.4-pre.006` compose ce contrat sans le contourner. La préparation interne génère trois keypairs éphémères, mesure le rent exact puis construit trois `SystemCreateAccount` officiels avec le propriétaire Token classique et les tailles 82/165/165. Chaque création est simulée, signée par le payeur et le nouveau compte, confirmée, puis relue pour vérifier owner, rent, taille, absence d'executable et données entièrement nulles. Les clés de création peuvent ensuite disparaître : les initialisations Token n'en ont pas besoin. Le lifecycle n'utilise pas ATA. Après autorisation destructive explicite, il exécute onze transactions séparées : initialisation du mint, initialisation des deux comptes, mint checked, transfer checked, approve checked, revoke, burn checked des deux soldes puis fermeture des deux comptes. Une étape incomplète arrête la séquence ; la suivante n'est construite qu'après confirmation, hydratation, extraction, décodage, matérialisation et second replay idempotent de la précédente.
|
||||
|
||||
Une confirmation RPC interrompue ne rend pas la séquence aveuglément rejouable : les initialisations Token ne sont pas idempotentes. La reprise exige donc l'index de la prochaine étape et la signature confirmée de son prédécesseur. Le pipeline hydrate cette signature, extrait le core, impose le decode Token commité, vérifie l'opération matérialisée attendue et un second replay sans sortie avant de construire l'étape suivante. Un délai borné entre étapes réduit les rafales vers le RPC public ; il ne transforme pas un statut inconnu en succès.
|
||||
|
||||
Cette reprise a été exercée sur Devnet le 15 juillet 2026 après un throttling `429` puis une confirmation momentanément incomplète. L'initialisation du compte destination déjà finalisée a été récupérée depuis sa signature canonique ; les étapes 3 à 10 ont ensuite achevé `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` et deux `CloseAccount`. Les neuf étapes récupérées ou envoyées ont chacune validé une matérialisation et un second replay idempotent.
|
||||
|
||||
`0.4.4-pre.007` n'ajoute aucune nouvelle couche d'exécution. La fenêtre Tauri existante construit un intent `TransferChecked` mince, puis appelle le même `execute_devnet_spl_token`. Le journal opérateur interroge au maximum 500 lignes via `MaterializedEventFilter`; les filtres exacts mint, compte et opération sont appliqués au payload typé en mémoire. La frontière UI conserve slots et montants bruts sous forme de chaînes et ne revendique aucun snapshot final ni agrégat OHLC.
|
||||
|
||||
`docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` constitue l’inventaire machine-readable de clôture. Un test Rust de `kb_executor_solana_core` charge cette matrice et impose dix-huit surfaces, 109 opérations appelables exactement égales à `SOLANA_CORE_OPERATION_CODES`, ainsi qu’une justification non vide pour chaque surface sans builder client. Le validateur Python provisoire a été supprimé.
|
||||
|
||||
## Préflight stateful ATA — `0.4.5-pre.004`
|
||||
|
||||
`kb_executor_spl_associated_token_account` construit exactement un builder officiel parmi
|
||||
`Create`, `CreateIdempotent` et `RecoverNested`. Le Token Program classique ou Token-2022 fait
|
||||
partie de l’intent et de la dérivation ; la bibliothèque ne lie toujours pas un plan à un endpoint
|
||||
ou à un wallet concret.
|
||||
|
||||
`kb_pipeline::inspect_spl_associated_token_account_stateful_readiness` est la frontière I/O
|
||||
Localnet/Devnet. Elle vérifie le genesis, le Program ID ATA, la simulation obligatoire, les mints,
|
||||
les PDA, l’absence stricte pour `Create`, la compatibilité absence/compte existant pour
|
||||
`CreateIdempotent`, les trois comptes Token de `RecoverNested`, tous les signataires et le solde du
|
||||
payer couvrant le plafond rent-plus-frais.
|
||||
|
||||
Les comptes Token-2022 peuvent dépasser 165 octets. Le préflight valide leur préfixe Token Account
|
||||
commun et laisse les extensions opaques ; il ne commence donc pas `0.4.6`. Le rent minimal du
|
||||
compte de base et le plafond sont contrôlés avant simulation. La taille et le rent exacts imposés
|
||||
par des extensions restent vérifiés par le runtime pendant la simulation obligatoire.
|
||||
|
||||
Le rapport `Ready`/`Blocked` ne signe, ne simule et n’envoie rien. L’orchestration de soumission,
|
||||
confirmation et replay reste une tranche séparée afin qu’un statut RPC inconnu ne provoque jamais
|
||||
une recréation ATA aveugle.
|
||||
|
||||
## Orchestration ATA Devnet — `0.4.5-pre.005`
|
||||
|
||||
`execute_devnet_spl_associated_token_account` réutilise les frontières communes validées : wallet
|
||||
persistant de profil, plan typé, préflight stateful, message exact, frais, simulation liée au hash,
|
||||
signature après autorisation, envoi et confirmation. Après confirmation, la signature déjà connue
|
||||
est hydratée par tentatives bornées ; chaque tentative RPC autorise deux reprises internes pour les
|
||||
réponses transitoires telles que `429`, sans reconstruire ni renvoyer l’instruction ATA.
|
||||
|
||||
Le replay sélectionne toujours le Program ID ATA. Pour une cible SPL Token classique, il sélectionne
|
||||
aussi le Program ID Token afin de décoder les CPI internes, tout en laissant chaque matérialiseur
|
||||
posséder uniquement ses faits. Pour Token-2022, il ne prétend pas décoder les instructions ou
|
||||
extensions CPI générales réservées à `0.4.6`. La seconde passe inclut l’état matérialisé et doit ne
|
||||
produire aucune nouvelle sortie.
|
||||
|
||||
La bibliothèque d’exécution ATA reste indépendante du cluster et du wallet. L’orchestrateur Devnet
|
||||
fourni ne peut signer qu’avec le wallet persistant du profil ; un `RecoverNested` exigeant une autre
|
||||
clé est donc refusé comme signataire indisponible. La fenêtre Tauri existante expose seulement les
|
||||
modes représentatifs `Create` et `CreateIdempotent`, la dérivation readonly et un journal ATA borné.
|
||||
|
||||
Le 16 juillet 2026, le parcours classique `CreateIdempotent` a été confirmé deux fois sur le même
|
||||
ATA Devnet. La première signature `2H9V…CPbP` et la seconde `3wWT…fv6q` ont chacune réussi
|
||||
simulation, confirmation, hydratation canonique, extraction, décodage, projection lifecycle unique
|
||||
et second replay sans nouvelle sortie. La seconde exécution prouve le chemin compte existant
|
||||
compatible ; elle reste un fait transactionnel distinct et non un doublon de replay.
|
||||
|
||||
Le garde-fou Token-2022 a également refusé correctement le Program ID `TokenzQd…` lorsqu’il était
|
||||
fourni à la place d’un compte mint : owner, layout Mint et état initialisé ne correspondaient pas.
|
||||
La preuve positive utilise ensuite le mint contrôlé `3DxK…KTE`, initialisé sur 82 octets et possédé
|
||||
par Token-2022. `CreateIdempotent` a créé puis réutilisé l’ATA `6WfC…C9Y`, compte de 170 octets
|
||||
initialisé pour `DwuA…g8B2` avec l’extension `ImmutableOwner`. Les signatures `bMMj…QAJ` au slot
|
||||
476702448 et `4RZK…VSG` au slot 476703343 ont chacune été simulées, confirmées, hydratées, décodées
|
||||
et matérialisées une fois, puis ignorées au second replay. L’état préalable observé établit la
|
||||
réutilisation de la seconde transaction, tandis que la projection parent conserve correctement
|
||||
`created_or_reused` car l’instruction seule ne distingue pas ces branches.
|
||||
|
||||
`RecoverNested` reste volontairement hors UI. Son parcours contrôlé opt-in reçoit deux mints et une
|
||||
fixture nested déjà préparée, puis réutilise le même orchestrateur afin de vérifier simulation,
|
||||
confirmation, fermeture du nested ATA, destination cohérente, deux projections ATA/risk et second
|
||||
replay idempotent sans introduire de décodeur Token-2022 général.
|
||||
|
||||
La preuve Devnet classique du 16 juillet 2026 utilise `3tdX…FbF5`, ATA wrapped SOL du wallet,
|
||||
comme owner du nested ATA `AAyv…gti5`. Celui-ci contenait exactement 1 token, soit
|
||||
1 000 000 000 unités brutes du mint `3fd5…hC9A`, et la destination `69TP…fyWp` était vide. La
|
||||
signature `37LG…HskM` a transféré le montant complet, fermé le nested ATA, conservé l’owner ATA et
|
||||
la destination initialisés, produit exactement les deux sorties parent `nested_ata_recovered` et
|
||||
`nested_ata_anti_pattern_recovered`, puis réussi le second replay sans doublon. Les mutations CPI
|
||||
Token de transfert et fermeture ne sont pas recopiées dans les projections parent ATA.
|
||||
|
||||
## Soumission et confirmation RPC
|
||||
|
||||
`0.4.2-pre.008` complète la frontière réseau sans fusionner les étapes :
|
||||
|
||||
```text
|
||||
SignedSolanaTransaction
|
||||
-> sendTransaction avec preflight obligatoire
|
||||
-> contrôle de la signature primaire retournée
|
||||
-> getSignatureStatuses borné
|
||||
-> getBlockHeight pour détecter l’expiration
|
||||
-> confirmed / finalized / failed / expired / timed_out
|
||||
```
|
||||
|
||||
`sendTransaction` signifie uniquement que le nœud a accepté la transaction signée pour relay. La confirmation reste une opération séparée. La politique borne le nombre de polls et leur intervalle, conserve le dernier slot et la dernière block height observée, puis termine explicitement sur succès, erreur runtime, expiration du recent blockhash ou timeout.
|
||||
|
||||
`requestAirdrop` et `getBalance` sont ajoutés comme primitives génériques de laboratoire. Le plafond d’airdrop Devnet appartient à la configuration et sera appliqué par l’orchestrateur du parcours réel ; la méthode RPC reste indépendante de Tauri et du wallet concret.
|
||||
|
||||
## Wallet temporaire persistant
|
||||
|
||||
`0.4.2-pre.003` introduit un signer de laboratoire dans `kb_wallet` :
|
||||
|
||||
```text
|
||||
wallets/temporary/<profile>/<alias>.json
|
||||
```
|
||||
|
||||
Il peut être généré en mémoire ou persisté afin de conserver la même adresse entre plusieurs démarrages, recevoir un airdrop devnet, créer des comptes et signer les transactions de validation. Le fichier utilise le format JSON Solana standard, reste exclu du dépôt et reçoit des permissions privées sur Unix.
|
||||
|
||||
Ce backend n'est pas un wallet de production : il n'est pas chiffré et les profils fournis l'interdisent sur mainnet. Les futurs backends chiffrés, coffre système et hardware wallet devront conserver la même frontière de signature afin que les exécuteurs ne changent pas.
|
||||
|
||||
## Garde-fous obligatoires
|
||||
|
||||
- dry-run activé par défaut ;
|
||||
- simulation RPC obligatoire avant envoi ;
|
||||
- cluster réel comparé au cluster attendu ;
|
||||
- contrôle du wallet source et des signataires requis ;
|
||||
- plafond de dépense, de frais et de compute-unit price ;
|
||||
- fraîcheur du blockhash ou état nonce durable complet, courant, initialisé et concordant ;
|
||||
- confirmation explicite pour tout envoi mainnet ;
|
||||
- journalisation séparée des plans et résultats ;
|
||||
- validation post-exécution par transaction canonique, extraction core, décodage et matérialisation.
|
||||
|
||||
## Exposition dans `kb_app_demo`
|
||||
|
||||
La démo exposera uniquement un sous-ensemble opérationnel sûr : transfert, création/allocate/assign contrôlés, Compute Budget et durable nonce après ajout d’un orchestrateur stateful validé sur cluster. Stake, Vote, loaders, changement d'autorité, Feature, Slashing et opérations ZK peuvent être disponibles dans les crates sans apparaître dans la fenêtre Tauri.
|
||||
|
||||
|
||||
## Orchestration Devnet et validation post-exécution
|
||||
|
||||
`0.4.2-pre.009` ajoute dans `kb_pipeline` une orchestration backend bornée pour le premier parcours System transfer sur Devnet. La fonction publique ne remplace aucune couche : elle appelle successivement les contrats existants et conserve leurs résultats séparés.
|
||||
|
||||
```text
|
||||
classification du genesis hash
|
||||
-> wallet temporaire persistant
|
||||
-> solde / airdrop plafonné
|
||||
-> PreparedExecutionPlan System transfer
|
||||
-> getLatestBlockhash
|
||||
-> getFeeForMessage
|
||||
-> simulateTransaction exact
|
||||
-> signature locale
|
||||
-> sendTransaction
|
||||
-> getSignatureStatuses + getBlockHeight
|
||||
-> getTransaction
|
||||
-> transaction canonique
|
||||
-> extraction core
|
||||
-> decode replay ciblé
|
||||
```
|
||||
|
||||
Le mode par défaut reste une simulation seule. Il ne signe pas, n’envoie pas et ne fabrique pas de signature vide. Le parcours soumis exige simultanément l’autorisation `submit`, la confirmation opérateur lorsque le profil l’impose, l’activation Devnet du wallet, un plafond de dépense et une simulation exacte réussie.
|
||||
|
||||
Le financement par faucet est une étape distincte, uniquement disponible sur Devnet et bornée par `devnet_airdrop_max_lamports`. Il est déclenché seulement lorsque le solde du wallet persistant ne couvre pas le transfert plus le plafond de frais. L’airdrop est lui-même confirmé avant de poursuivre.
|
||||
|
||||
`0.4.2-pre.010` ajoute une précondition liée au destinataire. Le mode metadata-only de `getAccountInfo` détermine si l’adresse existe déjà. Lorsqu’elle est absente, `getMinimumBalanceForRentExemption(0)` fournit le minimum nécessaire à la création implicite d’un compte System sans données ; un transfert inférieur est refusé avant simulation. Les échecs de simulation conservent désormais l’erreur runtime et un extrait borné des logs. La fenêtre `demo_execution_solana_core` ne réimplémente aucune de ces règles : elle sélectionne un profil Devnet, construit la requête et relaie les événements du pipeline.
|
||||
|
||||
Après une confirmation terminale, le pipeline hydrate exclusivement la signature envoyée. Une transaction on-chain échouée reste éligible à l’insertion canonique et au décodage comme intention échouée. Une expiration ou un timeout arrête la validation post-exécution sans prétendre que l’envoi a été confirmé.
|
||||
|
||||
Une erreur avant signature reste un `Err`. Dès qu’une signature locale existe, l’orchestrateur conserve cette signature et transforme les erreurs d’envoi, de confirmation ou de replay en diagnostics dans le résumé. Cette règle évite qu’un appelant perde la référence d’une transaction potentiellement diffusée.
|
||||
|
||||
L’orchestrateur est une API de bibliothèque et ne dépend pas de Tauri. La fenêtre `demo_execution_solana_core` expose uniquement une sélection Devnet réduite de ce backend ; les opérations administratives natives restent disponibles à terme dans les bibliothèques sans être nécessairement proposées dans l’interface.
|
||||
|
||||
## État de clôture `0.4.2`
|
||||
|
||||
Le parcours représentatif Devnet a validé la chaîne plan → simulation exacte → signature → envoi → confirmation → hydratation canonique → extraction core → decode replay. Les builders administratifs supplémentaires restent disponibles dans la bibliothèque, mais ne reçoivent aucune autorisation Mainnet implicite.
|
||||
|
||||
La règle de clôture est donc la suivante : complétude du wire et des plans offline, politique de sécurité et préflight stateful présents, puis preuve cluster obligatoire avant toute exposition mutable supplémentaire. Un statut `future` dans la matrice désigne cette preuve d’activation, pas une instruction manquante.
|
||||
@@ -0,0 +1,130 @@
|
||||
<!-- file: docs/EXECUTOR_SURFACE_MATRIX.md -->
|
||||
<!-- version: 10 -->
|
||||
|
||||
# Matrice des exécuteurs
|
||||
|
||||
## Matrice canonique machine-readable — `pre.022`
|
||||
|
||||
Le fichier `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` est désormais la source vérifiable pour la clôture de l’exécuteur natif. Il contient exactement :
|
||||
|
||||
- 18 surfaces natives ;
|
||||
- 14 surfaces `callable` ;
|
||||
- 4 surfaces `non_invocable` ou historiques ;
|
||||
- 109 opérations callable, chacune munie de sa source de construction, politique, préflight stateful, contrat de test, décision cluster et exposition UI.
|
||||
|
||||
Les quatre classifications sans builder client sont BPF Loader v1, BPF Loader v2, Native Loader et l’ancien ZK Token Proof historique. Un test Rust charge la matrice et la compare à `SOLANA_CORE_OPERATION_CODES`; toute omission, duplication ou opération inconnue fait échouer `cargo test -p kb_executor_solana_core`. Le validateur Python provisoire a été supprimé.
|
||||
|
||||
Le préflight stateful est actif pour Address Lookup Table, Config, Feature, Slashing et ZK ElGamal. Stake, Vote et Loaders restent marqués `future` dans la matrice pour leur preuve d’activation stateful approfondie ; cela ne retire aucun builder de la surface appelable, mais interdit toute exposition mutable sans validation cluster dédiée.
|
||||
|
||||
|
||||
Cette matrice suit la symétrie entre surfaces de décodage et surfaces de construction d’instructions. Elle ne confond pas réservation d’une crate, disponibilité d’un builder, autorisation d’envoi et exposition dans une interface.
|
||||
|
||||
## Socle d’exécution
|
||||
|
||||
| Crate | Rôle |
|
||||
|-----------------------|------------------------------------------------------------------------------|
|
||||
| `kb_execution_api` | Contrats provider-neutral des capacités, politiques, plans et résultats. |
|
||||
| `kb_execution_safety` | Garde-fous stateless avant simulation, signature ou envoi. |
|
||||
| `kb_execution_solana` | Compilation Solana, blockhash/nonce, preuve de simulation et signature. |
|
||||
| `kb_rpc` | Acquisition, simulation, envoi et confirmation JSON-RPC ; aucune clé privée. |
|
||||
| `kb_wallet` | Résolution des signataires derrière un backend explicite. |
|
||||
|
||||
## Règle mécanique
|
||||
|
||||
Pour chaque décodeur de programme ou de protocole classifié :
|
||||
|
||||
```text
|
||||
kb_decoder_<surface> -> kb_executor_<surface>
|
||||
```
|
||||
|
||||
Deux crates de décodage ne suivent volontairement pas cette règle :
|
||||
|
||||
- `kb_decoder_api` est le contrat commun des décodeurs, pas un programme appelable ;
|
||||
- `kb_decoder_anchor` est un helper technique partagé, pas une surface on-chain autonome.
|
||||
|
||||
L’audit du workspace `0.4.2-pre.014` trouve :
|
||||
|
||||
```text
|
||||
104 crates kb_decoder_*
|
||||
102 crates kb_executor_*
|
||||
102 paires mécaniques exactes
|
||||
2 exceptions techniques documentées
|
||||
0 exécuteur orphelin
|
||||
```
|
||||
|
||||
## Répartition des 102 surfaces appariées
|
||||
|
||||
| Famille | Paires |
|
||||
|---------------------------------------------------------------------------------------------------------------|----------:|
|
||||
| AMM | 26 |
|
||||
| Launchpad | 9 |
|
||||
| SPL | 8 |
|
||||
| Lending | 7 |
|
||||
| CLMM | 6 |
|
||||
| Router | 6 |
|
||||
| Vault | 6 |
|
||||
| Bridge | 4 |
|
||||
| Stable | 4 |
|
||||
| Orderbook | 3 |
|
||||
| Perpetuals | 3 |
|
||||
| Staking | 3 |
|
||||
| Admin | 2 |
|
||||
| Fees | 2 |
|
||||
| Adapter, CPMM, DLMM, Governance, Lock, Metadata, NFT, RWA, Solana Core, Treasury, Vesting, Wallet et Weighted | 1 chacune |
|
||||
|
||||
## Niveaux de maturité
|
||||
|
||||
Une paire doit utiliser un statut explicite :
|
||||
|
||||
| Statut | Signification |
|
||||
|------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `reserved` | Crate et identité réservées ; aucun builder utilisable. |
|
||||
| `implementing` | Contrat officiel et politiques en cours de validation. |
|
||||
| `complete` | Toutes les opérations client officiellement appelables du périmètre sont construites et testées. |
|
||||
| `not_applicable` | Surface non invocable, retirée ou purement technique, avec justification officielle. |
|
||||
|
||||
`Unsupported(reason)` est acceptable pendant l’implémentation ou pour une impossibilité officielle précise. Il ne doit pas masquer une opération callable oubliée.
|
||||
|
||||
## Politique de jalon
|
||||
|
||||
Chaque version qui active un décodeur doit également :
|
||||
|
||||
1. identifier la crate `kb_executor_<surface>` correspondante ;
|
||||
2. attribuer un statut de maturité à l’exécuteur ;
|
||||
3. lister les opérations client appelables et les opérations historiques decode-only ;
|
||||
4. définir autorités, signataires, coûts, slippage ou autres garde-fous ;
|
||||
5. comparer les payloads et comptes à un builder, une IDL ou un layout officiel ;
|
||||
6. ajouter les tests offline puis localnet/devnet nécessaires ;
|
||||
7. documenter l’API publique de la crate dans son README ;
|
||||
8. décider séparément quelles opérations sont visibles dans `kb_app_demo` ou activables par une stratégie.
|
||||
|
||||
Une opération dangereuse peut rester hors UI, mais elle reste dans le périmètre de la bibliothèque lorsqu’elle est officiellement appelable.
|
||||
|
||||
## Programme Solana Core
|
||||
|
||||
`kb_executor_solana_core` est la première surface passée de `reserved` à une implémentation opérationnelle. Après `pre.021`, elle expose toujours 109 opérations : 17 System, 4 Compute Budget, 5 Address Lookup Table, 6 précompiles de signature, 2 Config, 2 Feature, 2 Slashing, 3 ZK ElGamal, 24 Stake, 24 Vote, 11 Loader v3 et 9 Loader v4.
|
||||
|
||||
| Surface native | Statut à la clôture `0.4.2` | Décision |
|
||||
|-------------------------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------|
|
||||
| System, Compute Budget | `complete` pour les builders client actuels | orchestration durable nonce séparée ; exposition mutable toujours soumise aux politiques |
|
||||
| Address Lookup Table | `complete` stateless | autorité, rent, capacité et cooldown contrôlés par le préflight stateful |
|
||||
| Ed25519, secp256k1, secp256r1 | `complete` | formes inline et offsets ; aucune autorisation métier implicite |
|
||||
| Config | `complete` builder | wire local vérifié sans helper `bincode`; rent/état contrôlés avant envoi |
|
||||
| Feature | `complete` builder | activation et révocation ; état/rent contrôlés avant envoi |
|
||||
| Slashing | `complete` builder | close et plan atomique duplicate-block ; compte de preuve et fenêtres epoch stateful |
|
||||
| ZK ElGamal Proof | `complete` builder | douze preuves inline/account et fermeture de contexte |
|
||||
| ZK Token Proof historique | `not_applicable` | programme retiré/stub runtime, conservé uniquement pour décodage historique |
|
||||
| Stake | `complete` pour les 24 helpers clients actuels | validations compte/rent/epoch exigées avant exposition ; Redelegate historique `decode-only` |
|
||||
| Vote | `complete` pour les 20 variantes wire + 4 créations | validations account/rent/autorités/BLS/tour exigées avant exposition |
|
||||
| Loader v3 upgradeable | `complete` pour 11 opérations client | interface officielle `wincode`; état, rent, owner et capacité restent stateful |
|
||||
| Loader v4 | `complete` pour 9 plans client | wire officiel reproduit sans activer les helpers `bincode`; état/autorité/rent restent stateful |
|
||||
| BPF Loader v1/v2 | `historical_decode_only` | builders dépréciés historiques non promus dans l’exécuteur moderne |
|
||||
| Native Loader direct | `non_invocable_by_client_instruction` | déploiement lié au logiciel validator, aucun builder transactionnel inventé |
|
||||
|
||||
`pre.021` ne change aucune capacité : elle centralise les validations communes `pub(crate)` et élimine les implémentations identiques détectées dans les modules System, ALT, Config/Feature, Slashing, ZK, Stake, Vote et Loader.
|
||||
|
||||
La clôture `0.4.2` confirme la matrice machine-readable des dix-huit surfaces natives, avec pour chaque opération : builder/layout officiel, politique, tests offline, statut de validation cluster et décision d’exposition UI.
|
||||
|
||||
## Versions futures
|
||||
|
||||
Le `ROADMAP.md` associe désormais explicitement les exécuteurs aux séries Pump, Meteora, Raydium, Orca, Jupiter/routeurs et à chaque jalon ultérieur de décodeur. La symétrie de crates ne vaut pas activation : les listeners et stratégies ne peuvent demander que des capacités explicitement validées et autorisées.
|
||||
@@ -0,0 +1,78 @@
|
||||
<!-- file: docs/FOUNDATION_CLOSURE.md -->
|
||||
<!-- version: 2 -->
|
||||
|
||||
# Clôture de la fondation `0.3.x`
|
||||
|
||||
## Décision de version
|
||||
|
||||
Le jalon `0.3.5` n’est plus conservé comme version autonome. Ses outils d’intégrité, de sélection de corpus et de replay ont été réalisés pendant `0.3.4`.
|
||||
|
||||
La séquence retenue est :
|
||||
|
||||
```text
|
||||
0.3.4 -> clôture raw/canonical/core/replay
|
||||
0.4.0 -> infrastructure de décodage et matérialisation
|
||||
```
|
||||
|
||||
## Validations réelles acquises
|
||||
|
||||
| Contrôle | Résultat |
|
||||
|-------------------------|--------------------------------------------------|
|
||||
| Extraction pending | 70 extraites, 0 échec |
|
||||
| Intégrité SQL | 7 contrôles sans ligne anormale |
|
||||
| Ledger initial | 70 succès, 70 tentatives |
|
||||
| Skip sur échantillon | 14 skips, 0 extraction |
|
||||
| Force replay | 14 extractions, 0 skip |
|
||||
| Skip après replay | 14 skips, 0 extraction |
|
||||
| Cardinalité finale | 70 transactions core |
|
||||
| Ledger final | 70 succès, 84 tentatives |
|
||||
| CSV natif | écrit dans `data/exports_csv/` sous Tauri/Linux |
|
||||
| Tests application | `kb_app_demo` 60 tests, `kb_program_ids` 2 tests |
|
||||
| Tests pipeline finaux | `kb_pipeline` 24 tests |
|
||||
| Tests PostgreSQL finaux | `kb_store_pg` 39 tests, rollback inclus |
|
||||
| Clippy final | `cargo clippy --all-targets` sans avertissement |
|
||||
|
||||
Ces résultats confirment que le force-replay remplace le graphe ciblé sans créer de transaction supplémentaire et que le ledger comptabilise la tentative additionnelle.
|
||||
|
||||
## Contrôles automatisés finaux validés
|
||||
|
||||
### Rollback PostgreSQL
|
||||
|
||||
Le test `optional_postgres_core_extraction_rolls_back_partial_graph_from_env` injecte une violation d’unicité après le début de la persistance. Il vérifie :
|
||||
|
||||
- absence de transaction core partielle ;
|
||||
- absence d’account key partielle ;
|
||||
- raw toujours `received` ;
|
||||
- aucune entrée ledger réussie ;
|
||||
- replay corrigé possible ;
|
||||
- nettoyage des fixtures.
|
||||
|
||||
### Annulation backfill
|
||||
|
||||
Les appels réseau et attentes suivantes sont maintenant annulables :
|
||||
|
||||
- `getSignaturesForAddress` en vol ;
|
||||
- `getTransaction` en vol ;
|
||||
- attente du pacer ;
|
||||
- pause de retry standard ;
|
||||
- pause configurée après 429.
|
||||
|
||||
Les tests offline couvrent une attente déjà annulée et l’abandon coopératif d’une future longue.
|
||||
|
||||
## Clôture acquise
|
||||
|
||||
Les commandes finales ont été exécutées avec succès le 7 juillet 2026 :
|
||||
|
||||
```text
|
||||
kb_pipeline: 24 passed, 0 failed
|
||||
kb_store_pg avec PostgreSQL réel: 39 passed, 0 failed
|
||||
cargo clippy --all-targets: aucun avertissement
|
||||
```
|
||||
|
||||
`0.3.4` est inscrite dans `CHANGELOG.md`, le jalon `0.3.x` est clôturé et `0.4.0` devient le jalon actif. La session suivante doit démarrer avec `prompts/019_v0_4_0_decoder_infrastructure.md`.
|
||||
|
||||
## Éléments reportés sans blocage
|
||||
|
||||
Les corpus exhaustifs de protocoles ne sont pas nécessaires pour prouver l’indépendance de l’extracteur core. Ils sont constitués dans leurs versions respectives : Pump `0.5.x`, Meteora `0.6.x`, Raydium `0.7.x` et Orca `0.8.x`.
|
||||
|
||||
Le rejeu manuel d’un backfill `before` depuis `resume_before_signature` reste recommandé lors d’une prochaine campagne longue. La logique de frontière contiguë, les compteurs d’arrêt et une campagne réelle interrompue sont déjà validés.
|
||||
@@ -0,0 +1,355 @@
|
||||
<!-- file: docs/HISTORICAL_DATA_ACQUISITION_PLAN.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Plan différé — acquisition historique gratuite et worker de campagnes
|
||||
|
||||
## Statut
|
||||
|
||||
Ce document conserve un projet futur volontairement retiré du `ROADMAP.md` actif.
|
||||
|
||||
Aucune version n'est attribuée à ce chantier. Il ne doit pas retarder les décodeurs, matérialisateurs et exécuteurs Solana Core, SPL, AMM, launchpads, orderbooks, routers ou autres surfaces prioritaires.
|
||||
|
||||
Le nom **W2** est provisoire. Il désigne ici un worker manuel d'acquisition historique, distinct du worker temps réel destiné au trading. Le nom final des crates et binaires sera décidé au moment de l'activation du chantier.
|
||||
|
||||
## 1. Motivation
|
||||
|
||||
Le projet aura besoin de deux voies d'acquisition complémentaires :
|
||||
|
||||
```text
|
||||
W1 — temps réel
|
||||
-> fournisseurs fiables ou payants
|
||||
-> faible latence
|
||||
-> détection de créations de tokens, pools et pairs
|
||||
-> changements de prix/liquidité
|
||||
-> déclencheurs d'achat, vente et gestion du risque
|
||||
|
||||
W2 — historique manuel
|
||||
-> sources gratuites ou très économiques
|
||||
-> campagnes lentes et reprenables
|
||||
-> corpus vieux d'un an, deux ans ou davantage
|
||||
-> données destinées à l'analyse, aux tests et à la détection de patterns
|
||||
-> aucune consommation implicite du budget réservé au trading
|
||||
```
|
||||
|
||||
W2 ne remplace ni `kb_rpc`, ni le pipeline canonique, ni les fournisseurs temps réel. Il sert à alimenter progressivement PostgreSQL avec des données historiques dont la latence n'est pas critique.
|
||||
|
||||
## 2. Principes non négociables
|
||||
|
||||
- `kb_rpc` reste la frontière des protocoles Solana : JSON-RPC HTTP/WS standard, extensions fournisseur officiellement prises en charge, Yellowstone/gRPC et transports similaires.
|
||||
- Une source historique parlant JSON-RPC standard peut réutiliser `kb_rpc`, mais sa politique de campagne, son coût, sa provenance et ses fallbacks restent hors de `kb_rpc`.
|
||||
- Les APIs REST indexées, exports, datasets analytiques, archives CAR ou outils externes appartiennent à des crates de sources historiques séparées.
|
||||
- W2 fonctionne en `free_only` par défaut. Aucun fournisseur payant ne doit être interrogé sans autorisation explicite de la campagne.
|
||||
- Les endpoints et crédits de W1 ne doivent jamais être utilisés comme fallback silencieux de W2.
|
||||
- Toute donnée destinée au pipeline doit finir par être normalisée dans le contrat canonique existant.
|
||||
- Une source externe peut découvrir une signature, un slot ou un candidat ; elle ne devient pas pour autant la source canonique de la transaction.
|
||||
- Les campagnes doivent être bornées, annulables, reprenables, dédupliquées et auditées.
|
||||
- Une page vide ou une réponse `null` provenant d'une source best-effort ne prouve pas nécessairement l'absence historique de la donnée.
|
||||
|
||||
## 3. Séparation découverte / hydratation
|
||||
|
||||
W2 doit séparer deux responsabilités.
|
||||
|
||||
### 3.1 Découverte
|
||||
|
||||
Trouver des candidats à partir de :
|
||||
|
||||
- adresses ou Program IDs ;
|
||||
- signatures explicites ;
|
||||
- plages de slots ou périodes ;
|
||||
- pools, vaults ou token accounts ;
|
||||
- programmes DEX/launchpads ;
|
||||
- datasets indexés capables de filtrer par comptes, instructions ou timestamps.
|
||||
|
||||
### 3.2 Hydratation
|
||||
|
||||
Récupérer la transaction ou le bloc brut correspondant, puis suivre le pipeline existant :
|
||||
|
||||
```text
|
||||
candidate
|
||||
-> transaction ou bloc brut
|
||||
-> insertion raw canonique
|
||||
-> core extraction
|
||||
-> decode replay
|
||||
-> materialization
|
||||
-> agrégations et corpus d'analyse
|
||||
```
|
||||
|
||||
La source de découverte et la source d'hydratation peuvent être différentes.
|
||||
|
||||
Exemple :
|
||||
|
||||
```text
|
||||
BigQuery découvre les signatures d'une période
|
||||
-> Old Faithful hydrate les anciennes transactions
|
||||
-> un RPC public hydrate les transactions encore disponibles
|
||||
-> PostgreSQL ignore les signatures déjà présentes
|
||||
-> aucun crédit Helius n'est consommé
|
||||
```
|
||||
|
||||
## 4. Architecture candidate
|
||||
|
||||
Les noms ci-dessous sont provisoires et ne constituent pas encore des réservations de crates :
|
||||
|
||||
```text
|
||||
kb_historical_api
|
||||
-> capacités, campagnes, candidats, provenance, coût et complétude
|
||||
|
||||
kb_historical_source_rpc
|
||||
-> profils RPC publics/best-effort utilisant les contrats de kb_rpc
|
||||
|
||||
kb_historical_source_old_faithful
|
||||
-> intégration avec un processus faithful-cli externe ou une archive locale
|
||||
|
||||
kb_historical_source_bigquery
|
||||
-> découverte indexée par requêtes analytiques bornées
|
||||
|
||||
kb_historical_source_solscan
|
||||
-> API officielle optionnelle, avec clé et budget explicites
|
||||
|
||||
kb_worker_historical
|
||||
-> orchestration manuelle, reprise, quotas, déduplication et import canonique
|
||||
```
|
||||
|
||||
Une alternative consiste à garder les contrats et l'orchestration dans des modules internes d'une crate plus compacte. Ce choix devra être tranché après les prototypes et mesures, pas avant.
|
||||
|
||||
## 5. Sources candidates
|
||||
|
||||
### 5.1 RPC utilisé par l'Explorer Solana
|
||||
|
||||
L'Explorer officiel repose sur des appels JSON-RPC Solana standards. Une source `rpc_best_effort` peut donc utiliser des endpoints publics ou communautaires pour :
|
||||
|
||||
- `getSignaturesForAddress` ;
|
||||
- `getTransaction` ;
|
||||
- `getBlocks` ;
|
||||
- `getBlock` ;
|
||||
- réparations ciblées par signature ou slot.
|
||||
|
||||
Cette source convient aux campagnes étroites et lentes. Elle n'offre pas de SLA, d'index arbitraire ni de garantie de rétention complète. Le site HTML ne doit pas être scrapé.
|
||||
|
||||
Références de recherche :
|
||||
|
||||
- <https://github.com/solana-foundation/explorer>
|
||||
- <https://solana.com/docs/rpc/http/getsignaturesforaddress>
|
||||
- <https://solana.com/docs/rpc/http/gettransaction>
|
||||
|
||||
### 5.2 Old Faithful
|
||||
|
||||
Old Faithful est la piste prioritaire pour l'histoire profonde. L'intégration initiale doit privilégier un processus externe `faithful-cli` ou un serveur JSON-RPC local, afin d'éviter un couplage prématuré au format CAR, à l'implémentation Go et à sa licence.
|
||||
|
||||
Usages visés :
|
||||
|
||||
- hydratation de transactions anciennes ;
|
||||
- scans bornés par époque ou slots ;
|
||||
- constitution progressive d'un corpus local durable ;
|
||||
- fallback gratuit lorsque les RPC récents ont purgé les données.
|
||||
|
||||
Références de recherche :
|
||||
|
||||
- <https://github.com/rpcpool/yellowstone-faithful>
|
||||
- <https://docs.triton.one/project-yellowstone/old-faithful-historical-archive>
|
||||
|
||||
### 5.3 BigQuery ou dataset analytique équivalent
|
||||
|
||||
Un dataset indexé peut servir de moteur de découverte et réduire massivement le nombre d'appels RPC. Il doit être évalué pour :
|
||||
|
||||
- schéma et fraîcheur ;
|
||||
- instructions outer/inner disponibles ;
|
||||
- comptes et balances pré/post ;
|
||||
- précision des filtres ;
|
||||
- coût réel des requêtes ;
|
||||
- capacité à exporter des signatures et slots de manière reprenable.
|
||||
|
||||
Il ne remplace pas PostgreSQL ni le pipeline canonique.
|
||||
|
||||
Référence de recherche :
|
||||
|
||||
- <https://cloud.google.com/blockchain-analytics/docs/supported-datasets>
|
||||
|
||||
### 5.4 Solscan
|
||||
|
||||
Solscan fournit un index fonctionnellement intéressant, mais l'intégration durable doit utiliser uniquement son API officielle et respecter ses quotas et conditions.
|
||||
|
||||
Cette source serait :
|
||||
|
||||
- optionnelle ;
|
||||
- désactivée par défaut ;
|
||||
- configurée par clé ;
|
||||
- classée `free_quota` ou `paid` selon le plan ;
|
||||
- interdite dans une campagne `free_only` si elle entraîne un coût.
|
||||
|
||||
Les endpoints privés du site, cookies, jetons internes et scraping HTML ne doivent pas être utilisés.
|
||||
|
||||
Références de recherche :
|
||||
|
||||
- <https://solscan.io/apis>
|
||||
- <https://pro-api.solscan.io/pro-api-docs/v2.0>
|
||||
|
||||
### 5.5 Fournisseurs payants
|
||||
|
||||
Helius, Shyft, Chainstack, Triton ou équivalents peuvent être ajoutés ultérieurement comme fallback explicitement autorisé. Ils ne doivent pas être activés dans la politique par défaut de W2 et ne doivent jamais consommer les crédits de W1 à l'insu de l'opérateur.
|
||||
|
||||
## 6. Stablecoins et corpus de marché
|
||||
|
||||
Une campagne naïve `getSignaturesForAddress(MINT)` ne suffit pas à reconstruire toute l'activité d'un stablecoin. Les transferts classiques peuvent référencer les token accounts sans inclure systématiquement le mint dans les comptes de l'instruction.
|
||||
|
||||
Pour constituer des séries utiles à un analyseur de patterns, la stratégie prioritaire doit être :
|
||||
|
||||
```text
|
||||
stablecoin
|
||||
-> pools et pairs pertinents
|
||||
-> vaults et token accounts des pools
|
||||
-> Program IDs des AMM/CLMM/DLMM/routers
|
||||
-> transactions candidates
|
||||
-> décodage des swaps et changements de liquidité
|
||||
-> matérialisation trades/pools
|
||||
-> bougies et séries temporelles
|
||||
```
|
||||
|
||||
Les transferts génériques du token restent un corpus séparé, beaucoup plus volumineux et moins directement utile au prix.
|
||||
|
||||
## 7. Contrats fonctionnels candidats
|
||||
|
||||
### 7.1 Capacités
|
||||
|
||||
```rust
|
||||
pub struct HistoricalSourceCapabilities {
|
||||
pub signatures_by_address: bool,
|
||||
pub transactions_by_signature: bool,
|
||||
pub blocks_by_slot: bool,
|
||||
pub slot_ranges: bool,
|
||||
pub time_ranges: bool,
|
||||
pub program_filter: bool,
|
||||
pub token_filter: bool,
|
||||
pub instruction_filter: bool,
|
||||
pub indexed_results: bool,
|
||||
pub full_history: bool,
|
||||
}
|
||||
```
|
||||
|
||||
### 7.2 Budget
|
||||
|
||||
```rust
|
||||
pub enum HistoricalSourceCost {
|
||||
Free,
|
||||
FreeQuota,
|
||||
Paid,
|
||||
}
|
||||
|
||||
pub enum HistoricalBudgetPolicy {
|
||||
FreeOnly,
|
||||
FreeThenPaid,
|
||||
ExplicitSourcesOnly,
|
||||
}
|
||||
```
|
||||
|
||||
`FreeOnly` doit être la valeur de départ de toute campagne manuelle.
|
||||
|
||||
### 7.3 Complétude et provenance
|
||||
|
||||
```rust
|
||||
pub enum HistoricalCompleteness {
|
||||
Complete,
|
||||
Partial,
|
||||
Unknown,
|
||||
Pruned,
|
||||
}
|
||||
|
||||
pub struct HistoricalProvenance {
|
||||
pub source_name: String,
|
||||
pub source_kind: String,
|
||||
pub method: String,
|
||||
pub fetched_at_unix_ms: i64,
|
||||
pub paid: bool,
|
||||
}
|
||||
```
|
||||
|
||||
Les types définitifs devront respecter les conventions du workspace, les bornes PostgreSQL et les règles TS-rs si une UI est ajoutée.
|
||||
|
||||
## 8. Campagnes et observabilité
|
||||
|
||||
Une campagne doit conserver au minimum :
|
||||
|
||||
- identifiant stable ;
|
||||
- cible et filtres ;
|
||||
- source de découverte et source d'hydratation ;
|
||||
- politique de coût ;
|
||||
- curseur et checkpoint de reprise ;
|
||||
- plage temporelle ou de slots ;
|
||||
- concurrence, rate limit et backoff ;
|
||||
- candidats découverts ;
|
||||
- signatures déjà présentes ;
|
||||
- transactions hydratées, absentes, pruned ou invalides ;
|
||||
- erreurs et fallbacks ;
|
||||
- volume réseau et coût estimé ;
|
||||
- état `running`, `paused`, `cancelled`, `completed` ou `failed`.
|
||||
|
||||
Les événements `tracing` doivent être émis par les crates responsables. Les secrets, clés API, payloads complets non bornés et URLs contenant des credentials sont interdits dans les logs.
|
||||
|
||||
## 9. Étude préalable obligatoire
|
||||
|
||||
Avant toute activation dans le ROADMAP, exécuter un benchmark reproductible avec :
|
||||
|
||||
- une transaction récente ;
|
||||
- une transaction vieille d'environ un an ;
|
||||
- une transaction vieille d'environ deux ans ;
|
||||
- une adresse peu active ;
|
||||
- un Program ID très actif ;
|
||||
- un pool stablecoin ;
|
||||
- une plage de slots bornée.
|
||||
|
||||
Mesurer pour chaque source :
|
||||
|
||||
- profondeur historique ;
|
||||
- taux de réponse `null` ou pruned ;
|
||||
- cohérence de pagination ;
|
||||
- latence et débit soutenable ;
|
||||
- limitations et `Retry-After` ;
|
||||
- filtres disponibles ;
|
||||
- complétude des transactions et métadonnées ;
|
||||
- coût ;
|
||||
- conditions d'utilisation ;
|
||||
- capacité de reprise et stabilité du contrat.
|
||||
|
||||
## 10. Phases futures sans numéro de version
|
||||
|
||||
### Phase R0 — recherche
|
||||
|
||||
- valider les sources, licences, conditions d'utilisation, coûts et corpus ;
|
||||
- produire une matrice comparative et un prototype jetable ;
|
||||
- choisir le nom final de W2.
|
||||
|
||||
### Phase R1 — contrats et ledger de campagne
|
||||
|
||||
- définir les capacités, budgets, candidats, provenance et checkpoints ;
|
||||
- ajouter les migrations PostgreSQL seulement après stabilisation du contrat.
|
||||
|
||||
### Phase R2 — source RPC gratuite
|
||||
|
||||
- réutiliser `kb_rpc` pour les méthodes standards ;
|
||||
- ajouter quotas, backoff, déduplication et frontières de complétude.
|
||||
|
||||
### Phase R3 — archive profonde
|
||||
|
||||
- intégrer `faithful-cli` comme processus externe ou service local ;
|
||||
- valider les imports par époque/slots et la cohérence avec le canonique.
|
||||
|
||||
### Phase R4 — découverte indexée
|
||||
|
||||
- intégrer BigQuery ou une source analytique équivalente après benchmark ;
|
||||
- exporter uniquement les candidats nécessaires.
|
||||
|
||||
### Phase R5 — sources commerciales facultatives et UI
|
||||
|
||||
- ajouter Solscan ou d'autres APIs officielles avec budget explicite ;
|
||||
- ajouter une UI de campagne manuelle seulement lorsque les contrats backend sont stables.
|
||||
|
||||
## 11. Conditions avant retour dans le ROADMAP
|
||||
|
||||
Ce chantier ne revient dans le `ROADMAP.md` que lorsque :
|
||||
|
||||
- les décodeurs/matérialisateurs/exécuteurs prioritaires ont suffisamment progressé ;
|
||||
- au moins deux sources gratuites ont été testées réellement ;
|
||||
- une stratégie de licence et de conditions d'utilisation est validée ;
|
||||
- la frontière avec `kb_rpc`, `kb_pipeline` et PostgreSQL est décidée ;
|
||||
- la politique `free_only` est testable et empêche réellement tout fallback payant ;
|
||||
- un prompt de session dédié peut être écrit sans hypothèse majeure non vérifiée.
|
||||
@@ -0,0 +1,107 @@
|
||||
<!-- file: docs/IDL_SURFACE_CLASSIFICATION.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Classification des surfaces IDL
|
||||
|
||||
Ce fichier résume les reclassements déduits des fichiers présents dans `idls/`. Il ne remplace pas une validation on-chain ; il sert à stabiliser la nomenclature et les crates réservés.
|
||||
|
||||
## Décisions principales
|
||||
|
||||
- `program_ids.rs` est remplacé par `constants.rs` dans les crates core/SPL, afin de regrouper plus tard les discriminants, sélecteurs et constantes internes non publiques.
|
||||
- Les constantes réexportées par le `lib.rs` d’une crate doivent être appelées via `crate::CONSTANT_NAME` depuis les modules internes de cette même crate.
|
||||
- Les surfaces IDL classifiées reçoivent un nom canonique fonctionnel : `amm_*`, `clmm_*`, `router_*`, `vault_*`, `lending_*`, etc.
|
||||
- Les comptes non prouvés comme `program_id`, comme `BAGSB...`, restent hors workspace de décodage. Les vrais programmes Bags Fee Share V1/V2 sont classés séparément dans `docs/BAGS_FM.md`.
|
||||
|
||||
## Reclassements notables
|
||||
|
||||
| Canonique | Ancien nom/source | Justification IDL |
|
||||
|--------------------------------------|-------------------------------|----------------------------------------------------------------------------------------|
|
||||
| `vault_carrot_defi` | `carrot_defi` | IDL orientée vault : `initVault`, `issue`, `redeem`, `addAsset`, `removeStrategy`. |
|
||||
| `lending_clone` | `clone` | IDL orientée lending : borrow positions, collateral, pool parameters, oracle updates. |
|
||||
| `clmm_fusion` | `fusion_amm` | IDL avec positions, liquidity, fees et limit orders ; plus proche CLMM que simple AMM. |
|
||||
| `stable_swap_hylo_exchange` | `hylo_exchange` | IDL stable/levercoin : mint, redeem, swap stable/lever. |
|
||||
| `vault_hylo_stability_pool` | `hylo_stability_pool` | IDL de stability pool : user deposit/withdraw et rebalances. |
|
||||
| `wallet_jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | IDL smart wallet avec approvals, `postSwap` et withdraw. |
|
||||
| `vault_kamino_yvaults` | `kamino` | IDL `yvaults`, stratégies, collateral info et shares metadata. |
|
||||
| `governance_metadao_bid_wall` | `metadao_bid_wall` | Surface MetaDAO liée à la gouvernance/futarchy plutôt qu’à un AMM direct. |
|
||||
| `stable_swap_numeraire` | `numeraire` | IDL avec pool stable virtuel, add/remove liquidity et metadata LP. |
|
||||
| `rwa_ondo_global_markets` | `ondo_global_markets` | IDL RWA/roles/attestation/burn ; pas un DEX. |
|
||||
| `clmm_pancake_swap` | `pancake_swap` | IDL `amm_v3` avec positions et liquidity : classé CLMM. |
|
||||
| `adapter_saber_decimal_wrapper` | `sabre_decimal_wrapper` | IDL wrapper d’adaptation de décimales : initializeWrapper, deposit, withdraw. |
|
||||
|
||||
## Table IDL triée par canonique
|
||||
|
||||
| Canonique | Source | Program ID | IDL | Crate cible | Statut |
|
||||
|------------------------------------------------|----------------------------------|------------------------------------------------|---------------------------------------------------------------------------------|-----------------------------------------------------------|---------------------|
|
||||
| `adapter_saber_decimal_wrapper` | `sabre_decimal_wrapper` | `DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB` | `saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json` | `kb_decoder_adapter_saber_decimal_wrapper` | `canonical_current` |
|
||||
| `admin_jupiter_lock` | `jupiter_lock` | `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn` | `jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json` | `kb_decoder_admin_jupiter_lock` | `canonical_current` |
|
||||
| `admin_pump_fees` | `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | `pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json` | `kb_decoder_admin_pump_fees` | `canonical_current` |
|
||||
| `amm_bonk_swap` | `bonk_swap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | `bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json` | `kb_decoder_amm_bonk_swap` | `canonical_current` |
|
||||
| `amm_goosefx_gamma` | `goose_fx_gamma` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | `goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json` | `kb_decoder_amm_goosefx_gamma` | `canonical_current` |
|
||||
| `amm_goosefx_v2` | `goose_fx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | `goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json` | `kb_decoder_amm_goosefx_v2` | `canonical_current` |
|
||||
| `amm_guac_swap` | `guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | `guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json` | `kb_decoder_amm_guac_swap` | `canonical_current` |
|
||||
| `amm_lifinity_swap_v2` | `lifinity_swap_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | `lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json` | `kb_decoder_amm_lifinity_swap_v2` | `canonical_current` |
|
||||
| `amm_metadao_futarchy_amm` | `metadao_futarchy_amm` | `FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq` | `metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json` | `kb_decoder_amm_metadao_futarchy_amm` | `canonical_current` |
|
||||
| `amm_metadao_v0_5` | `metadao_amm_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | `metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json` | `kb_decoder_amm_metadao_v0_5` | `canonical_current` |
|
||||
| `amm_meteora_damm_v1` | `meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | `meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json` | `kb_decoder_amm_meteora_damm_v1` | `canonical_current` |
|
||||
| `amm_meteora_damm_v2` | `meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | `meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json` | `kb_decoder_amm_meteora_damm_v2` | `canonical_current` |
|
||||
| `amm_pump_swap` | `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | `pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json` | `kb_decoder_amm_pump_swap` | `canonical_current` |
|
||||
| `amm_vertigo` | `vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | `vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json` | `kb_decoder_amm_vertigo` | `canonical_current` |
|
||||
| `amm_virtuals` | `virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | `virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json` | `kb_decoder_amm_virtuals` | `canonical_current` |
|
||||
| `amm_woofi` | `woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | `woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json` | `kb_decoder_amm_woofi` | `canonical_current` |
|
||||
| `bridge_circle_cctp_token_messenger_minter` | `cctp_token_messenger_minter` | `CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3` | `cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `canonical_current` |
|
||||
| `bridge_circle_cctp_token_messenger_minter_v2` | `cctp_token_messenger_minter_v2` | `CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe` | `cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `canonical_current` |
|
||||
| `bridge_layer_zero_endpoint` | `layer_zero_endpoint` | `76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6` | `layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json` | `kb_decoder_bridge_layer_zero_endpoint` | `canonical_current` |
|
||||
| `bridge_layer_zero_executor` | `layer_zero_executor` | `6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn` | `layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json` | `kb_decoder_bridge_layer_zero_executor` | `canonical_current` |
|
||||
| `clmm_byreal` | `byreal_clmm` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | `byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json` | `kb_decoder_clmm_byreal` | `canonical_current` |
|
||||
| `clmm_fusion` | `fusion_amm` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | `fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json` | `kb_decoder_clmm_fusion` | `canonical_current` |
|
||||
| `clmm_orca_whirlpool` | `orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json` | `kb_decoder_clmm_orca_whirlpool` | `canonical_current` |
|
||||
| `clmm_orca_whirlpool` | `orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json` | `kb_decoder_clmm_orca_whirlpool` | `canonical_current` |
|
||||
| `clmm_pancake_swap` | `pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | `pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json` | `kb_decoder_clmm_pancake_swap` | `canonical_current` |
|
||||
| `clmm_raydium` | `raydium_clmm` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | `raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json` | `kb_decoder_clmm_raydium` | `canonical_current` |
|
||||
| `clmm_stabble` | `stabble_clmm` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | `stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json` | `kb_decoder_clmm_stabble` | `canonical_current` |
|
||||
| `cpmm_raydium` | `raydium_cpmm` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | `raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json` | `kb_decoder_cpmm_raydium` | `canonical_current` |
|
||||
| `dlmm_meteora` | `meteora_dlmm` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | `meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json` | `kb_decoder_dlmm_meteora` | `canonical_current` |
|
||||
| `governance_metadao_bid_wall` | `metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | `metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json` | `kb_decoder_governance_metadao_bid_wall` | `canonical_current` |
|
||||
| `launchpad_boop_fun` | `boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | `boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json` | `kb_decoder_launchpad_boop_fun` | `canonical_current` |
|
||||
| `launchpad_metadao_ico` | `metadao_ico` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | `metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json` | `kb_decoder_launchpad_metadao_ico` | `canonical_current` |
|
||||
| `launchpad_meteora_dbc` | `meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | `meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json` | `kb_decoder_launchpad_meteora_dbc` | `canonical_current` |
|
||||
| `launchpad_moonit` | `moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | `moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json` | `kb_decoder_launchpad_moonit` | `canonical_current` |
|
||||
| `launchpad_orca_wavebreak` | `orca_wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | `orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json` | `kb_decoder_launchpad_orca_wavebreak` | `canonical_current` |
|
||||
| `launchpad_printr` | `printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | `printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json` | `kb_decoder_launchpad_printr` | `canonical_current` |
|
||||
| `launchpad_pump_fun` | `pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | `pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json` | `kb_decoder_launchpad_pump_fun` | `canonical_current` |
|
||||
| `launchpad_pump_pumpup_ai` | `pumpup_ai` | `PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd` | `pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json` | `kb_decoder_launchpad_pump_pumpup_ai` | `canonical_current` |
|
||||
| `launchpad_raydium_launchlab` | `raydium_launchlab` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | `raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json` | `kb_decoder_launchpad_raydium_launchlab` | `canonical_current` |
|
||||
| `lending_clone` | `clone` | `C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr` | `clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json` | `kb_decoder_lending_clone` | `canonical_current` |
|
||||
| `lending_kamino` | `kamino_lending` | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` | `kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json` | `kb_decoder_lending_kamino` | `canonical_current` |
|
||||
| `lending_marginfi_v2` | `marginfi_v2` | `MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA` | `marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json` | `kb_decoder_lending_marginfi_v2` | `canonical_current` |
|
||||
| `lock_raydium_lp` | `raydium_lock_lp` | `LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE` | `raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json` | `kb_decoder_lock_raydium_lp` | `canonical_current` |
|
||||
| `nft_metaplex_bubblegum` | `bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | `bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json` | `kb_decoder_nft_metaplex_bubblegum` | `canonical_current` |
|
||||
| `orderbook_jupiter_limit_order` | `jupiter_limit_order` | `jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu` | `jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json` | `kb_decoder_orderbook_jupiter_limit_order` | `canonical_current` |
|
||||
| `orderbook_jupiter_limit_order_v2` | `jupiter_limit_order_v2` | `j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X` | `jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `canonical_current` |
|
||||
| `orderbook_openbook_v2` | `openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | `openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json` | `kb_decoder_orderbook_openbook_v2` | `canonical_current` |
|
||||
| `perpetuals_drift_v2` | `drift_v2` | `dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH` | `drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json` | `kb_decoder_perpetuals_drift_v2` | `canonical_current` |
|
||||
| `perpetuals_jupiter` | `jupiter_perpetuals` | `PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu` | `jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json` | `kb_decoder_perpetuals_jupiter` | `canonical_current` |
|
||||
| `perpetuals_zeta` | `zeta` | `ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD` | `zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json` | `kb_decoder_perpetuals_zeta` | `canonical_current` |
|
||||
| `router_jupiter_aggregator_v4` | `jupiter_agregator_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | `jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json` | `kb_decoder_router_jupiter_aggregator_v4` | `canonical_current` |
|
||||
| `router_jupiter_aggregator_v6` | `jupiter_agregator_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | `jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json` | `kb_decoder_router_jupiter_aggregator_v6` | `canonical_current` |
|
||||
| `router_jupiter_dca` | `jupiter_dca` | `DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M` | `jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json` | `kb_decoder_router_jupiter_dca` | `canonical_current` |
|
||||
| `router_okx_labs_v1` | `okx_labs_v1` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | `okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json` | `kb_decoder_router_okx_labs_v1` | `canonical_current` |
|
||||
| `router_okx_labs_v2` | `okx_labs_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | `okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json` | `kb_decoder_router_okx_labs_v2` | `canonical_current` |
|
||||
| `rwa_ondo_global_markets` | `ondo_global_markets` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | `ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json` | `kb_decoder_rwa_ondo_global_markets` | `canonical_current` |
|
||||
| `stable_swap_hylo_exchange` | `hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | `hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json` | `kb_decoder_stable_swap_hylo_exchange` | `canonical_current` |
|
||||
| `stable_swap_jupiter_stable` | `jupiter_stable` | `JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr` | `jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json` | `kb_decoder_stable_swap_jupiter_stable` | `canonical_current` |
|
||||
| `stable_swap_numeraire` | `numeraire` | `NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P` | `numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json` | `kb_decoder_stable_swap_numeraire` | `canonical_current` |
|
||||
| `stable_swap_stabble` | `stabble_stable_swap` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | `stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json` | `kb_decoder_stable_swap_stabble` | `canonical_current` |
|
||||
| `staking_kamino_farm` | `kamino_farm` | `FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr` | `kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json` | `kb_decoder_staking_kamino_farm` | `canonical_current` |
|
||||
| `staking_marinade_finance` | `marinade_finance` | `MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD` | `marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json` | `kb_decoder_staking_marinade_finance` | `canonical_current` |
|
||||
| `treasury_helium_treasury_management` | `helium_treasury_management` | `treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5` | `helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json` | `kb_decoder_treasury_helium_treasury_management` | `canonical_current` |
|
||||
| `vault_carrot_defi` | `carrot_defi` | `CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s` | `carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json` | `kb_decoder_vault_carrot_defi` | `canonical_current` |
|
||||
| `vault_hylo_stability_pool` | `hylo_stability_pool` | `HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ` | `hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json` | `kb_decoder_vault_hylo_stability_pool` | `canonical_current` |
|
||||
| `vault_kamino` | `kamino_vault` | `kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr` | `kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json` | `kb_decoder_vault_kamino` | `canonical_current` |
|
||||
| `vault_kamino_v2` | `kamino_vault_v2` | `KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd` | `kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json` | `kb_decoder_vault_kamino_v2` | `canonical_current` |
|
||||
| `vault_kamino_yvaults` | `kamino` | `6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc` | `kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json` | `kb_decoder_vault_kamino_yvaults` | `canonical_current` |
|
||||
| `vault_meteora` | `meteora_vault` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | `meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json` | `kb_decoder_vault_meteora` | `canonical_current` |
|
||||
| `vesting_streamflow` | `streamflow` | `strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m` | `streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json` | `kb_decoder_vesting_streamflow` | `canonical_current` |
|
||||
| `wallet_jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | `JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw` | `jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json` | `kb_decoder_wallet_jupiter_apepro_smart_wallet` | `canonical_current` |
|
||||
| `weighted_swap_stabble` | `stabble_weighted_swap` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | `stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json` | `kb_decoder_weighted_swap_stabble` | `canonical_current` |
|
||||
@@ -0,0 +1,112 @@
|
||||
<!-- file: docs/INSTRUCTION_REPLAY_CONTRACTS.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Contrats de replay par instruction
|
||||
|
||||
Ce document complète le cadrage `0.2.1`. Il ne crée pas encore le schéma SQL complet, mais il verrouille l'idée suivante : le scheduling de replay doit cibler les instructions normalisées, tandis que le décodage doit recevoir une instruction avec son contexte Solana extrait.
|
||||
|
||||
## Motivation
|
||||
|
||||
Une transaction Solana peut contenir plusieurs instructions top-level et plusieurs inner instructions. Certaines instructions peuvent être déjà décodées, d'autres non. Rejouer toute la transaction à chaque correction de décodeur serait plus coûteux et moins précis.
|
||||
|
||||
Le pipeline doit donc pouvoir faire :
|
||||
|
||||
```text
|
||||
raw transaction
|
||||
→ extraction core transaction
|
||||
→ extraction account keys, instructions, inner instructions, logs, balances
|
||||
→ sélection des instructions non traitées
|
||||
→ reconstruction d'un replay input contextualisé
|
||||
→ decode/materialize uniquement sur ces inputs
|
||||
```
|
||||
|
||||
## Scheduling instruction-level
|
||||
|
||||
L'état `CoreInstructionProcessingState` prépare cette granularité.
|
||||
|
||||
| État | Sens |
|
||||
|-------------------|---------------------------------------------------------------|
|
||||
| `Pending` | Instruction disponible pour premier traitement. |
|
||||
| `Decoded` | Instruction décodée par au moins un décodeur. |
|
||||
| `Materialized` | Instruction ayant produit une projection métier. |
|
||||
| `Ignored` | Instruction ignorée volontairement par la politique courante. |
|
||||
| `Failed` | Instruction en échec, à diagnostiquer. |
|
||||
| `ReplayRequested` | Instruction à rejouer même si un traitement précédent existe. |
|
||||
|
||||
Les états ne remplacent pas le ledger par module/version. Ils servent de vue rapide et de filtre de scheduling.
|
||||
|
||||
## Décodage contextualisé
|
||||
|
||||
Le décodeur ne doit pas être limité à `CoreInstructionRow.payload_json`.
|
||||
|
||||
Certains protocoles Solana exigent de lire :
|
||||
|
||||
- les inner instructions ;
|
||||
- les logs Anchor ou non Anchor ;
|
||||
- les balances avant/après ;
|
||||
- les comptes résolus ;
|
||||
- les loaded addresses ;
|
||||
- les erreurs de transaction ;
|
||||
- des séquences de logs liées à un CPI ;
|
||||
- des instructions voisines de la même transaction.
|
||||
|
||||
`CoreInstructionReplayInput` représente ce contrat de lecture : une instruction ciblée plus un contexte extrait depuis les tables `core`. Depuis le contrat `2`, ce contexte inclut également toutes les instructions outer de la signature, ordonnées numériquement et munies de leur payload retenu et de son hash.
|
||||
|
||||
## Sélection de replay
|
||||
|
||||
`CoreInstructionReplayFilter` permet de sélectionner les instructions par :
|
||||
|
||||
- état de traitement ;
|
||||
- `program_id` ;
|
||||
- plage de slots.
|
||||
|
||||
L'usage standard pour un worker de decode sera :
|
||||
|
||||
```text
|
||||
processing_state = Pending ou ReplayRequested
|
||||
program_id = programme ciblé par le décodeur
|
||||
plage de slots = optionnelle
|
||||
```
|
||||
|
||||
Le repository charge les logs, balances, account keys et instructions outer nécessaires pour retourner `CoreInstructionReplayInput`. La liste outer inclut la cible et utilise les champs stables `instructionIndex`, `instructionPath`, `programId`, `payloadJson` et `payloadHash`. Cette extension lit les tables existantes et ne demande aucune migration.
|
||||
|
||||
## Marquage de cycle de vie
|
||||
|
||||
`CoreInstructionLifecycleMark` permet à un module de changer l'état d'une instruction après traitement.
|
||||
|
||||
Exemples :
|
||||
|
||||
- un décodeur reconnu marque l'instruction `Decoded` ;
|
||||
- un materializer complet marque l'instruction `Materialized` ;
|
||||
- un décodeur non concerné peut marquer `Ignored` dans un contexte contrôlé ;
|
||||
- une erreur de parsing marque `Failed` ;
|
||||
- une correction de décodeur peut remettre `ReplayRequested`.
|
||||
|
||||
## Relation avec `kb_sol_ops_processing_ledger`
|
||||
|
||||
Le processing ledger garde le détail par module, version et input. L'état sur l'instruction reste le dernier état opérationnel visible.
|
||||
|
||||
Pour éviter les doublons, l'input key du ledger devra être stable, par exemple :
|
||||
|
||||
```text
|
||||
signature:instruction_path:program_id:processor_name:processor_version
|
||||
```
|
||||
|
||||
La forme exacte n'est pas figée en `0.2.1`, mais elle doit rester déterministe.
|
||||
|
||||
## Rétention du payload d'instruction et des logs
|
||||
|
||||
Le payload JSON d'une instruction et le texte des logs sont utiles au début. Plus tard, après décodage et matérialisation fiables, ils pourront être compactés ou purgés avec conservation d'un hash.
|
||||
|
||||
Les entities `CoreInstructionRow` et `CoreLogRow` préparent ce cas en autorisant :
|
||||
|
||||
- payload ou texte présent ;
|
||||
- payload ou texte absent ;
|
||||
- hash conservé ;
|
||||
- état de traitement consultable pour replay.
|
||||
|
||||
La purge effective ne doit pas être implémentée avant que les diagnostics de replay soient fiables.
|
||||
|
||||
## Décision pour `0.2.1`
|
||||
|
||||
`0.2.1` ajoute les contrats Rust nécessaires, mais ne crée pas encore les tables SQL. Les migrations réelles restent prévues pour `0.2.3` et `0.2.4`.
|
||||
104
migration/khadhroony-bot2-reference/docs/LIVE_SOURCE_STRATEGY.md
Normal file
104
migration/khadhroony-bot2-reference/docs/LIVE_SOURCE_STRATEGY.md
Normal file
@@ -0,0 +1,104 @@
|
||||
<!-- file: docs/LIVE_SOURCE_STRATEGY.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Stratégie des sources de transactions
|
||||
|
||||
## Décision actuelle
|
||||
|
||||
Les sources temps réel payantes ne sont plus l’axe actif de `0.3.x`.
|
||||
|
||||
La priorité est :
|
||||
|
||||
```text
|
||||
backfills HTTP sur comptes gratuits
|
||||
-> transaction canonique
|
||||
-> extraction core
|
||||
-> décodeurs Core/Pump/Meteora/Raydium/Orca/Jupiter
|
||||
```
|
||||
|
||||
Helius Developer, Triton, Chainstack, Shyft ou un autre provider seront testés seulement lorsque le pipeline pourra décoder et matérialiser immédiatement les transactions reçues.
|
||||
|
||||
## Sources futures
|
||||
|
||||
### HTTP JSON-RPC
|
||||
|
||||
Usage :
|
||||
|
||||
- backfill historique ;
|
||||
- hydratation de signatures ;
|
||||
- réparation ;
|
||||
- validation ponctuelle.
|
||||
|
||||
Méthodes principales :
|
||||
|
||||
```text
|
||||
getSignaturesForAddress
|
||||
getTransaction
|
||||
getSignatureStatuses
|
||||
getSlot
|
||||
```
|
||||
|
||||
### Helius WebSocket enrichi
|
||||
|
||||
`transactionSubscribe` est une extension Helius, pas une méthode Solana standard.
|
||||
|
||||
Elle sera implémentée dans des modules internes de `kb_rpc`, sans créer de crate séparée, puis convertie vers le modèle canonique commun.
|
||||
|
||||
### Yellowstone gRPC
|
||||
|
||||
Yellowstone fournit l’équivalent fonctionnel d’un flux de transactions complètes filtrées. Les endpoints Triton, Chainstack, Shyft ou compatibles doivent être supportés par configuration dans `kb_rpc`.
|
||||
|
||||
### Solana WebSocket standard
|
||||
|
||||
`logsSubscribe` par mention ou avec filtre `"all"` reste une solution de probe, de fallback ou de comparaison. Une notification de logs ne devient durable qu’après hydratation de la transaction ou enregistrement d’une observation technique légère.
|
||||
|
||||
## Stockage commun
|
||||
|
||||
Toutes les sources convergent vers :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions
|
||||
kb_sol_obs_transaction_observations
|
||||
```
|
||||
|
||||
Les décodeurs ne connaissent ni le fournisseur, ni le protocole, ni la méthode d’acquisition.
|
||||
|
||||
## Ordre de mise en œuvre
|
||||
|
||||
```text
|
||||
0.3.x transaction canonique, observations, backfill gratuit, extraction core
|
||||
0.4.x décodeurs Core/SPL
|
||||
0.5.x Pump
|
||||
0.6.x Meteora
|
||||
0.7.x Raydium
|
||||
0.8.x Orca
|
||||
0.9.x Jupiter et routeurs
|
||||
0.10.x Helius transactionSubscribe, Yellowstone gRPC et comparaison
|
||||
```
|
||||
|
||||
## Sécurité de reprise
|
||||
|
||||
Pendant toute perte du flux principal :
|
||||
|
||||
```text
|
||||
trading = disabled
|
||||
```
|
||||
|
||||
Le replay ou le backfill après reconnexion sert à remettre la base en cohérence. Un événement `replayed`, `backfilled` ou `repaired` ne doit pas déclencher rétroactivement un ordre.
|
||||
|
||||
## Critères de choix futur
|
||||
|
||||
La décision provider devra comparer :
|
||||
|
||||
- couverture par programme et CPI ;
|
||||
- latence par signature ;
|
||||
- coût par Go, crédit ou forfait ;
|
||||
- nombre de filtres et streams ;
|
||||
- régions ;
|
||||
- reconnexion et fenêtre de replay ;
|
||||
- pertes et backpressure ;
|
||||
- portabilité du protocole ;
|
||||
- qualité du support.
|
||||
|
||||
Les timings seront mesurés dans `kb_sol_obs_transaction_observations`.
|
||||
|
||||
106
migration/khadhroony-bot2-reference/docs/LOGGING.md
Normal file
106
migration/khadhroony-bot2-reference/docs/LOGGING.md
Normal file
@@ -0,0 +1,106 @@
|
||||
<!-- file: docs/LOGGING.md -->
|
||||
<!-- version: 8 -->
|
||||
|
||||
# Logging
|
||||
|
||||
La journalisation est centralisée dans `kb_logging` et repose sur des événements `tracing` structurés.
|
||||
|
||||
## Objectifs
|
||||
|
||||
- séparer console, fichiers globaux et fichiers par crate ;
|
||||
- conserver un fichier JSONL dédié aux erreurs ;
|
||||
- retrouver immédiatement un échec de décodage ou de matérialisation ;
|
||||
- corréler RPC, pipeline, store et processor sans dupliquer les décisions dans Tauri ;
|
||||
- activer le debug d’une crate sans dépendre de son découpage interne en modules.
|
||||
|
||||
## Arborescence d’un profil
|
||||
|
||||
Pour un profil stocké sous `logs/<profile>/`, la configuration crée :
|
||||
|
||||
```text
|
||||
logs/<profile>/debug.log
|
||||
logs/<profile>/info.log
|
||||
logs/<profile>/error.jsonl
|
||||
logs/<profile>/app.log
|
||||
logs/<profile>/<crate>/debug.log
|
||||
logs/<profile>/<crate>/info.log
|
||||
logs/<profile>/<crate>/error.jsonl
|
||||
```
|
||||
|
||||
Les routes sont en rotation quotidienne. `tracing-appender` insère la date dans le nom physique produit à partir du préfixe et du suffixe configurés.
|
||||
|
||||
Les niveaux sont cumulatifs :
|
||||
|
||||
- `debug.log` reçoit `debug`, `info`, `warn` et `error` ;
|
||||
- `info.log` reçoit `info`, `warn` et `error` ;
|
||||
- `error.jsonl` reçoit uniquement `error` ;
|
||||
- `app.log` reçoit les événements `kb_app_demo` à partir de `debug`.
|
||||
|
||||
`error.jsonl` utilise le format JSON pour permettre la recherche par champ. Les autres fichiers utilisent le format humain sans ANSI.
|
||||
|
||||
## Targets canoniques
|
||||
|
||||
Le target est exactement le nom de la crate et provient de `src/constants.rs` :
|
||||
|
||||
```text
|
||||
kb_app_demo
|
||||
kb_decoder_solana_core
|
||||
kb_executor_metadata_spl_name_service
|
||||
kb_executor_solana_core
|
||||
kb_executor_spl_account_compression
|
||||
kb_executor_spl_memo
|
||||
kb_executor_spl_noop
|
||||
kb_executor_spl_single_pool
|
||||
kb_logging
|
||||
kb_materializer_admin
|
||||
kb_materializer_compliance_audit
|
||||
kb_materializer_lifecycle
|
||||
kb_materializer_staking
|
||||
kb_pipeline
|
||||
kb_rpc
|
||||
kb_store_pg
|
||||
```
|
||||
|
||||
La granularité passe par les champs `action`, `stage`, `window`, `campaign_id`, `signature`, `instruction_path`, `program_id`, `processor_name`, `status` et `error_code`.
|
||||
|
||||
## Corrélation de campagne
|
||||
|
||||
Les campagnes de backfill, extraction core et replay decode utilisent des identifiants stables :
|
||||
|
||||
```text
|
||||
capture_session_id
|
||||
campaign_id
|
||||
signature
|
||||
slot
|
||||
instruction_path
|
||||
program_id
|
||||
input_key
|
||||
input_hash
|
||||
processor_name
|
||||
processor_version
|
||||
```
|
||||
|
||||
Le pipeline journalise la sélection et l’agrégation. Le RPC journalise le transport et l’endpoint sélectionné sans exposer l’URL secrète. Le store journalise la persistance et les rollbacks. Le décodeur et chacun des matérialiseurs lifecycle, admin et compliance audit journalisent leur décision propre.
|
||||
|
||||
## Fichier d’erreurs
|
||||
|
||||
Les erreurs de décodage et de matérialisation sont émises au niveau `error` par la crate responsable, puis éventuellement par la frontière qui constate l’échec terminal. Elles apparaissent donc dans :
|
||||
|
||||
- `logs/<profile>/error.jsonl` ;
|
||||
- `logs/<profile>/<crate>/error.jsonl`.
|
||||
|
||||
Cette duplication volontaire fournit une vue globale et une vue isolée par composant. Les champs de corrélation permettent de regrouper les événements d’un même défaut.
|
||||
|
||||
Une transaction on-chain échouée mais correctement décodée n’est pas écrite dans `error.jsonl` uniquement à cause de `meta.err`. En revanche, un payload déclaré compatible mais `failed` ou `unsupported` indique une lacune du code ou une évolution du protocole et doit être visible dans le fichier d’erreurs.
|
||||
|
||||
## Frontend et Tauri
|
||||
|
||||
Le frontend peut continuer à transmettre un identifiant logique tel que `kb_app_demo.frontend.demo_decode_replay`. Le backend le valide et le conserve dans le champ `frontend_target`, mais l’événement est émis avec le target canonique `kb_app_demo`.
|
||||
|
||||
`kb_app_demo` ne doit pas recopier les détails internes d’un retry RPC, d’un parsing binaire ou d’un commit SQL. Il journalise l’invocation, la progression visible et le résumé reçu de la crate opérationnelle.
|
||||
|
||||
## Sécurité
|
||||
|
||||
Les logs ne contiennent jamais de clé privée, seed phrase, DSN non masqué, URL fournisseur secrète ou payload complet non borné. Pour un payload problématique, conserver la taille, un préfixe borné et un SHA-256.
|
||||
|
||||
Le contrat normatif complet se trouve dans `docs/TRACING_CONTRACT.md`.
|
||||
68
migration/khadhroony-bot2-reference/docs/MATERIALIZATIONS.md
Normal file
68
migration/khadhroony-bot2-reference/docs/MATERIALIZATIONS.md
Normal file
@@ -0,0 +1,68 @@
|
||||
<!-- file: docs/MATERIALIZATIONS.md -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Catalogue des matérialisations
|
||||
|
||||
Les matérialisations transforment des événements décodés en événements métier exploitables par la base de données, les validations, les agrégations et les futurs signaux de stratégie.
|
||||
|
||||
## Matérialisations prioritaires
|
||||
|
||||
- `trade` : swaps, buys, sells, fills et conversions entre actifs.
|
||||
- `liquidity` : dépôts, retraits, ajout ou retrait de liquidité, bootstrap de pool.
|
||||
- `lifecycle` : création, initialisation, migration, fermeture ou changement d'état d'un pool, marché, compte ou vault.
|
||||
- `fee` : collecte, claim, sweep, buyback, mise à jour de configuration de frais.
|
||||
- `admin` : changement d'autorité, update de config, pause, unpause, transfert de rôle.
|
||||
- `token_account` : création ATA, transfert, mint, burn, close account, approve, revoke.
|
||||
- `pool_state` : snapshots de réserves, ticks, bins, prix, liquidité active ou virtual reserves.
|
||||
- `orderbook` : place order, cancel order, fill, settle funds, consume events.
|
||||
- `reward` : émission, claim, distribution, farming, incentives.
|
||||
- `risk` : signaux dérivés à partir des événements admin, liquidité, metadata, authority ou anomalies.
|
||||
|
||||
## Projections natives actives dans `0.4.1`
|
||||
|
||||
`kb_materializer_lifecycle` produit des événements lifecycle idempotents à partir d'observations exactes, réussies et commitées :
|
||||
|
||||
| Domaine | Sortie stable | Entrées couvertes |
|
||||
|----------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------|
|
||||
| Address Lookup Table | `address_lookup_table:<operation>:0` | create, freeze, extend, deactivate, close |
|
||||
| Program loaders | `program_loader:<surface>:<operation>:0` | finalize immuable, initialize/deploy/upgrade/close/extend upgradeable, set length/deploy/retract/finalize v4 |
|
||||
| Feature Gate | `feature_gate:revoke_pending_activation:0` | revoke pending activation |
|
||||
| Comptes System | `system_account:<operation>:0` | create, create with seed, create allow prefund, allocate, allocate with seed |
|
||||
| Durable nonce System | `durable_nonce_account:<operation>:0` | initialize, advance, authorize, upgrade, withdraw |
|
||||
| Contexte ZK ElGamal | `zk_proof_context:<operation>:0` | initialisation demandée après vérification réussie, fermeture |
|
||||
| Rapport Slashing | `slashing_violation_report:<operation>:0` | initialisation après preuve duplicate block acceptée, fermeture après rétention |
|
||||
|
||||
La famille décodée n'impose pas à elle seule la famille matérialisée. `authorize_nonce_account` est un événement `Admin`, tandis qu'une vérification ZK ElGamal est un événement `Audit`; ces observations ne produisent une sortie `Lifecycle` que lorsque leur mutation durable exacte est reconnue. Une transaction échouée ou non commitée est toujours refusée.
|
||||
|
||||
`kb_materializer_admin` produit des sorties `Admin` pour les assignations System, les écritures Config opaques et les changements d’autorité Loader. `kb_materializer_compliance_audit` produit des sorties `ComplianceAudit` pour les écritures/copies de bytecode Loader. `kb_materializer_staking` produit les projections instructionnelles Stake/Vote commitées : comptes stake/vote, autorités, lockup, délégation, vote state, retraits et dépôt de rewards. Les données complètes ne sont jamais recopiées : seules les clés, autorités, offsets, tailles, hashes, préfixes et paramètres bornés disponibles sont conservés.
|
||||
|
||||
Le retrait d'un nonce account conserve une sémantique conditionnelle : le runtime ferme le compte uniquement lorsque la totalité du solde est retirée. La projection décrit l'opération commitée et ne prétend pas reconstruire l'état final transactionnel du compte. Les deltas SOL restent la responsabilité du graphe core.
|
||||
|
||||
Une preuve ZK n'est jamais matérialisée. Seul le lifecycle du compte de contexte est projeté lorsqu'il existe. Un rapport Slashing conserve le signal de violation et son lifecycle, mais pas le contenu du compte de preuve externe ; `penaltyAppliedByProgram` reste faux car le programme ne retire pas lui-même du stake. L'ancien ZK Token Proof Program reste en decode/audit, car le runtime actuel est sans effet et aucun historique mainnet fiable n'a été observé.
|
||||
|
||||
## Matérialisations spécialisées à prévoir
|
||||
|
||||
- `nft` : NFT classiques, programmables et compressés.
|
||||
- `metadata` : metadata Metaplex, creators, collection, update authority, URI et symbol.
|
||||
- `oracle` : prix, publisher, confidence, staleness et sources d'oracle.
|
||||
- `lending` : borrow, repay, collateral, liquidation et health factor.
|
||||
- `staking` avancé : snapshots de comptes Stake/Vote avant/après, activation par epoch, crédits Vote cumulés et réconciliation avec sysvars.
|
||||
- `governance` : proposal, vote, execute et update de realm/config.
|
||||
- `bridge` : lock, mint wrapped asset, burn, redeem et message verification.
|
||||
- `perpetuals` : position, funding, liquidation, collateral et PnL.
|
||||
- `vault` : share mint/burn, deposit, withdraw, rebalance et fee collection.
|
||||
- `routing` : route, route leg, quote, slippage, aggregator hop.
|
||||
- `compliance_audit` : événements d'audit non directement matérialisables en trading.
|
||||
- `token_metadata_risk` : signaux de risque issus des métadonnées de tokens, par exemple autorité mutable, creators suspects, symbol/URI incohérents ou changements de metadata.
|
||||
|
||||
## Projections natives encore à terminer avant clôture
|
||||
|
||||
Toutes les sémantiques matérialisables ne sont pas encore projetées. Les lots restants sont explicites :
|
||||
|
||||
- Compute Budget : profil transactionnel agrégé regroupant toutes les instructions Compute Budget du message.
|
||||
|
||||
Les précompiles de signature et les preuves ZK sans contexte restent des observations d'audit déjà suffisantes. Une projection supplémentaire n'est justifiée que si un consommateur `compliance_audit` exige une table dédiée. L'ancien ZK Token Proof no-op reste sans matérialisation.
|
||||
|
||||
## Règle de conception
|
||||
|
||||
Une matérialisation spécialisée ne doit être ajoutée que si la table générique ne suffit pas pour représenter correctement la sémantique du protocole. Le modèle commun reste prioritaire, puis les tables spécialisées complètent ce modèle. Chaque projection doit posséder une identité stable, un état cible explicite, une politique d'idempotence et une règle documentée pour les transactions échouées.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,113 @@
|
||||
<!-- file: docs/NATIVE_SOLANA_CLOSURE_AUDIT.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Audit de clôture Solana natif `0.4.1`
|
||||
|
||||
## Objet
|
||||
|
||||
Ce document fixe l’état final du jalon `0.4.1` : couverture native, preuves synthétiques, corpus mainnet observés et limites assumées.
|
||||
|
||||
L’absence de transaction mainnet n’autorise ni à supprimer une surface officielle, ni à prétendre qu’elle a été observée. Elle impose une validation synthétique documentée et une tentative de découverte bornée lorsque la surface est officiellement déclarée.
|
||||
|
||||
## Surfaces couvertes
|
||||
|
||||
`kb_decoder_solana_core` couvre 18 surfaces exécutables natives ou assimilées :
|
||||
|
||||
1. System Program ;
|
||||
2. Vote Program ;
|
||||
3. Stake Program ;
|
||||
4. Config Program ;
|
||||
5. Compute Budget Program ;
|
||||
6. Address Lookup Table Program ;
|
||||
7. ZK ElGamal Proof Program ;
|
||||
8. Feature Program ;
|
||||
9. ancien ZK Token Proof Program ;
|
||||
10. Native Loader ;
|
||||
11. BPF Loader v1 ;
|
||||
12. BPF Loader v2 ;
|
||||
13. BPF Loader Upgradeable ;
|
||||
14. Loader v4 ;
|
||||
15. Ed25519 precompile ;
|
||||
16. secp256k1 precompile ;
|
||||
17. secp256r1 precompile ;
|
||||
18. Slashing Program.
|
||||
|
||||
`StakeConfig11111111111111111111111111111111` est volontairement classé comme compte natif historique non exécutable.
|
||||
|
||||
## Divergence Slashing corrigée
|
||||
|
||||
Agave `v4.1.1` déclare le programme stateless Slashing sous :
|
||||
|
||||
```text
|
||||
S1ashing11111111111111111111111111111111111
|
||||
```
|
||||
|
||||
La surface a été ajoutée dans `pre.018` : deux instructions exactes, dispatch sans fallback, couverture déclarée et projection de rapport. Aucun corpus public observable n’a été trouvé ; la validation repose donc sur les sources officielles et les fixtures synthétiques.
|
||||
|
||||
## Invariants automatiques
|
||||
|
||||
Les tests garantissent que :
|
||||
|
||||
- chaque entrée de `kb_program_ids::native_program_ids()` possède exactement une surface de décodeur ;
|
||||
- chaque surface possède au moins une déclaration de couverture ;
|
||||
- aucune déclaration n’utilise le fallback `unclassified_native_instruction` ;
|
||||
- chaque déclaration référence une surface réellement déclarée ;
|
||||
- les identités programme/surface/entrée sont uniques ;
|
||||
- le Slashing Program est dispatché vers son parseur exact ;
|
||||
- ZK Token Proof historique passe par le fallback actuel no-op sans prétendre vérifier une preuve.
|
||||
|
||||
## Corpus et replays validés
|
||||
|
||||
| Programme | Résultat de clôture |
|
||||
|---------------------------|------------------------------------------------------------------------------|
|
||||
| System | 817 inputs décodés, 258 sorties, 52 refus attendus sur transactions échouées |
|
||||
| Config | 96 inputs décodés, 96 sorties admin |
|
||||
| Stake | 448 inputs décodés, 447 sorties, 1 refus attendu |
|
||||
| Vote | 600 inputs décodés, 600 sorties |
|
||||
| Compute Budget | 2 746 inputs décodés, 1 603 profils transactionnels |
|
||||
| Address Lookup Table | 104 inputs décodés, 104 sorties lifecycle |
|
||||
| ZK ElGamal | 151 inputs décodés, 139 sorties de contexte |
|
||||
| Slashing | zéro corpus public observable, validation synthétique |
|
||||
| Feature | zéro corpus observable dans les campagnes adressées, validation synthétique |
|
||||
| Loaders rares | zéro corpus observable pour certaines générations, validation synthétique |
|
||||
| ZK Token Proof historique | zéro corpus public observable, runtime actuel no-op, validation synthétique |
|
||||
|
||||
Les campagnes de clôture rapportées ont `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et ne produisent pas d’erreurs opérationnelles dans les journaux fournis.
|
||||
|
||||
## Backfills
|
||||
|
||||
Aucun backfill supplémentaire n’est requis pour clôturer `0.4.1`.
|
||||
|
||||
Le mode `program_latest` ajouté dans `pre.019` reste disponible pour de futures recherches sans signature d’ancrage. Il ne doit pas être utilisé pour inventer un corpus absent sur des surfaces feature-gated ou historiquement rares.
|
||||
|
||||
## Matérialisations
|
||||
|
||||
La clôture instructionnelle est documentée dans `docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md`.
|
||||
|
||||
Aucune projection native stable immédiatement dérivable du graphe core actuel n’est identifiée comme manquante. Les snapshots finaux de comptes et métriques runtime restent différés.
|
||||
|
||||
## Validations finales
|
||||
|
||||
Validations utilisateur finales rapportées pour la clôture :
|
||||
|
||||
```text
|
||||
kb_program_ids 5 tests
|
||||
kb_decoder_solana_core 114 tests
|
||||
kb_materializer_lifecycle 16 tests
|
||||
kb_materializer_admin 7 tests
|
||||
kb_materializer_compliance_audit 7 tests
|
||||
kb_materializer_staking 8 tests
|
||||
kb_config 36 tests
|
||||
kb_pipeline 45 tests
|
||||
kb_store_pg 45 tests avec PostgreSQL réel
|
||||
kb_app_demo 78 tests
|
||||
cargo clippy --all-targets propre
|
||||
```
|
||||
|
||||
Le démarrage Tauri validé après `pre.021-fix.001` confirme les 53 routes de logging et l’initialisation PostgreSQL. `pre.022` ne modifie pas la configuration Tauri.
|
||||
|
||||
## Décision
|
||||
|
||||
`0.4.1` est clôturée.
|
||||
|
||||
La prochaine session doit utiliser `prompts/022_v0_4_2_executor_solana_core.md` et démarrer `0.4.2` sur l’infrastructure d’exécution et `kb_executor_solana_core`.
|
||||
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"file": "docs/NATIVE_SOLANA_DECODER_MATRIX.json",
|
||||
"version": 1,
|
||||
"release": "0.4.2-pre.023",
|
||||
"surface_count": 18,
|
||||
"coverage_entry_count": 121,
|
||||
"registry_contract": "Exact equality with kb_program_ids::native_program_ids and SolanaCoreDecoder::surfaces",
|
||||
"completeness_contract": "Each surface has exact declared coverage count plus a dedicated official-enum or audited-wire completeness test",
|
||||
"surfaces": [
|
||||
{
|
||||
"surface_code": "solana_native_address_lookup_table",
|
||||
"program_id": "AddressLookupTab1e1111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 5,
|
||||
"source_contract": "solana-address-lookup-table-interface@3.1.x official instruction enum",
|
||||
"completeness_test": "address_lookup_table::tests::coverage_uses_official_encoder_discriminants"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_bpf_loader_deprecated",
|
||||
"program_id": "BPFLoader1111111111111111111111111111111111",
|
||||
"runtime_status": "historical",
|
||||
"expected_coverage_entries": 2,
|
||||
"source_contract": "solana-loader-v2-interface@3.0.0 immutable write/finalize layout",
|
||||
"completeness_test": "loaders::tests::coverage_declares_every_loader_entry"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_bpf_loader",
|
||||
"program_id": "BPFLoader2111111111111111111111111111111111",
|
||||
"runtime_status": "historical",
|
||||
"expected_coverage_entries": 2,
|
||||
"source_contract": "solana-loader-v2-interface@3.0.0 immutable write/finalize layout",
|
||||
"completeness_test": "loaders::tests::coverage_declares_every_loader_entry"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_bpf_loader_upgradeable",
|
||||
"program_id": "BPFLoaderUpgradeab1e11111111111111111111111",
|
||||
"runtime_status": "current_with_historical_variants",
|
||||
"expected_coverage_entries": 8,
|
||||
"source_contract": "solana-loader-v3-interface@8.0.1 with wincode exact wire layout",
|
||||
"completeness_test": "loaders::tests::coverage_declares_every_loader_entry"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_compute_budget",
|
||||
"program_id": "ComputeBudget111111111111111111111111111111",
|
||||
"runtime_status": "current_with_historical_variants",
|
||||
"expected_coverage_entries": 6,
|
||||
"source_contract": "solana-compute-budget-interface@3.0.0 plus historical RequestUnitsDeprecated",
|
||||
"completeness_test": "compute_budget::tests::coverage_declares_current_and_historical_entries"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_config",
|
||||
"program_id": "Config1111111111111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 1,
|
||||
"source_contract": "solana-config-interface@2.0.0 generic bounded store contract",
|
||||
"completeness_test": "config::tests::coverage_declares_one_generic_store_surface"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_ed25519",
|
||||
"program_id": "Ed25519SigVerify111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 1,
|
||||
"source_contract": "Agave Ed25519 precompile exact offset-table layout",
|
||||
"completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_feature",
|
||||
"program_id": "Feature111111111111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 1,
|
||||
"source_contract": "solana-feature-gate-interface revoke-pending-activation instruction contract",
|
||||
"completeness_test": "feature::tests::coverage_declares_the_single_official_instruction"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_loader_v4",
|
||||
"program_id": "LoaderV411111111111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 7,
|
||||
"source_contract": "solana-loader-v4-interface@3.1.0 official instruction enum",
|
||||
"completeness_test": "loaders::tests::coverage_declares_every_loader_entry"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_loader",
|
||||
"program_id": "NativeLoader1111111111111111111111111111111",
|
||||
"runtime_status": "runtime_dispatch",
|
||||
"expected_coverage_entries": 1,
|
||||
"source_contract": "Agave native-loader runtime direct-invocation behavior",
|
||||
"completeness_test": "loaders::tests::coverage_declares_every_loader_entry"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_secp256k1",
|
||||
"program_id": "KeccakSecp256k11111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 1,
|
||||
"source_contract": "Agave secp256k1 precompile exact offset-table layout",
|
||||
"completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_secp256r1",
|
||||
"program_id": "Secp256r1SigVerify1111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 1,
|
||||
"source_contract": "Agave secp256r1 precompile exact offset-table layout",
|
||||
"completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_slashing",
|
||||
"program_id": "S1ashing11111111111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 2,
|
||||
"source_contract": "SIMD-0204 and Agave Slashing builtin instruction contract",
|
||||
"completeness_test": "slashing::tests::coverage_declares_exactly_two_official_instructions"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_stake",
|
||||
"program_id": "Stake11111111111111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 18,
|
||||
"source_contract": "solana-stake-interface@4.3.x official StakeInstruction variants",
|
||||
"completeness_test": "stake::tests::coverage_declares_every_official_stake_instruction"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_system",
|
||||
"program_id": "11111111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 14,
|
||||
"source_contract": "solana-system-interface@3.2.x official SystemInstruction variants",
|
||||
"completeness_test": "system::tests::coverage_declares_all_current_system_variants"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_vote",
|
||||
"program_id": "Vote111111111111111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 20,
|
||||
"source_contract": "solana-vote-interface@6.0.x official VoteInstruction variants",
|
||||
"completeness_test": "vote::tests::coverage_declares_every_official_vote_instruction"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_zk_elgamal_proof",
|
||||
"program_id": "ZkE1Gama1Proof11111111111111111111111111111",
|
||||
"runtime_status": "current",
|
||||
"expected_coverage_entries": 13,
|
||||
"source_contract": "solana-zk-elgamal-proof-interface official ProofInstruction variants",
|
||||
"completeness_test": "zk_elgamal::tests::coverage_declares_all_thirteen_official_instructions"
|
||||
},
|
||||
{
|
||||
"surface_code": "solana_native_zk_token_proof",
|
||||
"program_id": "ZkTokenProof1111111111111111111111111111111",
|
||||
"runtime_status": "historical_with_current_noop",
|
||||
"expected_coverage_entries": 18,
|
||||
"source_contract": "audited historical ZK Token Proof wire table plus current runtime no-op behavior",
|
||||
"completeness_test": "zk_token_proof::tests::coverage_declares_seventeen_historical_entries_and_current_noop_fallback"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
<!-- file: docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# Audit des matérialisations Solana natives
|
||||
|
||||
## Conclusion
|
||||
|
||||
`0.4.1` clôt les matérialisations natives instructionnelles stables. Les projections actives couvrent les mutations ou profils qui peuvent être déduits de façon déterministe depuis le graphe core transactionnel et les événements décodés.
|
||||
|
||||
Les sorties ne sont pas des snapshots finaux de comptes ou de runtime. Les états finaux qui exigent l’état précédent/suivant d’un compte, les sysvars historiques ou des métriques runtime restent différés jusqu’à enrichissement explicite du contrat core.
|
||||
|
||||
## Projections actives par matérialiseur
|
||||
|
||||
| Matérialiseur | Surfaces | Projection |
|
||||
|------------------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||
| `kb_materializer_lifecycle` | Address Lookup Table | lifecycle create/freeze/extend/deactivate/close |
|
||||
| `kb_materializer_lifecycle` | Feature | révocation de feature gate |
|
||||
| `kb_materializer_lifecycle` | Loaders | déploiement, finalisation et transitions lifecycle stables |
|
||||
| `kb_materializer_lifecycle` | System | durable nonce, create account et allocate |
|
||||
| `kb_materializer_lifecycle` | ZK ElGamal | création ou fermeture d’un compte de contexte |
|
||||
| `kb_materializer_lifecycle` | Slashing | initialisation ou fermeture d’un rapport de violation |
|
||||
| `kb_materializer_admin` | System | assign et assign with seed |
|
||||
| `kb_materializer_admin` | Config | écriture opaque bornée avec clés, taille, SHA-256 et préfixe |
|
||||
| `kb_materializer_admin` | Loaders | changements d’autorité |
|
||||
| `kb_materializer_compliance_audit` | Loaders | écritures/copies de bytecode bornées par offsets, tailles, SHA-256 et préfixes |
|
||||
| `kb_materializer_compliance_audit` | Compute Budget | profil transactionnel agrégé, last-write-wins, une seule sortie par transaction |
|
||||
| `kb_materializer_staking` | Stake | compte, délégation, désactivation, split, merge, withdraw, move stake/lamports, autorités et lockup instructionnels |
|
||||
| `kb_materializer_staking` | Vote | compte, autorités, identité, commission, vote state, retraits et dépôt de rewards instructionnels |
|
||||
|
||||
## Politique des transactions échouées
|
||||
|
||||
Les transactions on-chain échouées restent décodables comme intentions ou diagnostics. Les matérialiseurs mutables utilisent la politique `SuccessfulCommittedOnly` et refusent les observations non commitées au lieu de produire une mutation réussie fictive.
|
||||
|
||||
Le profil Compute Budget est une sortie `ComplianceAudit`, pas une mutation d’état ; il peut donc être matérialisé même lorsque la transaction échoue. Il décrit le budget demandé par les instructions, pas les unités réellement consommées.
|
||||
|
||||
## Absences volontaires
|
||||
|
||||
Les surfaces suivantes ne produisent pas de projection dédiée dans `kb_sol_mat_events` à la clôture de `0.4.1` :
|
||||
|
||||
- Ed25519 ;
|
||||
- secp256k1 ;
|
||||
- secp256r1 ;
|
||||
- preuves ZK sans compte de contexte ;
|
||||
- ancien ZK Token Proof historique/no-op.
|
||||
|
||||
Leur décodage est déjà un événement d’audit borné. Une matérialisation supplémentaire serait une duplication sans consommateur métier dédié.
|
||||
|
||||
## États finaux différés
|
||||
|
||||
Les éléments suivants ne doivent pas être présentés comme disponibles dans `0.4.1` :
|
||||
|
||||
- état final exact d’un compte Stake ou Vote ;
|
||||
- activation/désactivation Stake par epoch ;
|
||||
- crédits Vote cumulés ;
|
||||
- rewards recalculées par epoch ;
|
||||
- état final d’un compte System après lamport drain conditionnel ;
|
||||
- bytecode final complet d’un loader ;
|
||||
- unités compute réellement consommées ;
|
||||
- frais finaux réellement facturés ;
|
||||
- résultat cryptographique recalculé pour signature ou preuve ZK.
|
||||
|
||||
Ces états exigent une extension du contexte core ou un audit de comptes/snapshots dédié.
|
||||
|
||||
## Validations finales `0.4.1`
|
||||
|
||||
Les validations utilisateur finales couvrent :
|
||||
|
||||
| Surface | Corpus / preuve | Résultat |
|
||||
|----------------------|--------------------------------|----------------------------------------------------------------|
|
||||
| System | replay ciblé | 817 inputs décodés, 258 sorties, 52 refus attendus |
|
||||
| Config | replay ciblé | 96 inputs décodés, 96 sorties admin |
|
||||
| Stake | replay ciblé | 448 inputs décodés, 447 sorties, 1 refus attendu |
|
||||
| Vote | replay ciblé | 600 inputs décodés, 600 sorties |
|
||||
| Compute Budget | replay ciblé | 2 746 inputs décodés, 1 603 profils |
|
||||
| ZK ElGamal | replay ciblé | 151 inputs décodés, 139 sorties de contexte |
|
||||
| Address Lookup Table | inventaire/replay ciblé | 104 inputs décodés, 104 sorties lifecycle |
|
||||
| Slashing | sources officielles + fixtures | aucun corpus public observable, validation synthétique assumée |
|
||||
|
||||
Toutes les campagnes de clôture rapportées se terminent avec `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et des journaux `error.jsonl` vides.
|
||||
|
||||
## Décision de clôture
|
||||
|
||||
Aucune projection native instructionnelle stable n’est identifiée comme manquante dans le périmètre `0.4.1`.
|
||||
|
||||
La suite `0.4.2` peut se concentrer sur l’exécution : plans, simulation, signature, envoi et validation post-exécution, sans rouvrir le jalon de décodage/matérialisation natif.
|
||||
@@ -0,0 +1,233 @@
|
||||
<!-- file: docs/NATIVE_SOLANA_PROGRAMS.md -->
|
||||
<!-- version: 18 -->
|
||||
|
||||
# Inventaire et couverture des programmes Solana natifs
|
||||
|
||||
## Périmètre
|
||||
|
||||
Le registre `kb_program_ids::native_program_ids()` contient dix-huit surfaces exécutables : programmes runtime natifs, loaders, précompiles et programmes historiques encore observables. Les sysvars et comptes natifs non exécutables restent suivis séparément dans `registry/core_program_id_seed.toml`.
|
||||
|
||||
Memo ne fait pas partie de ce périmètre. Les IDs officiels v1 `Memo1U...`, v3 `MemoSq...` et v4 `Memo4c...` sont réservés dans `kb_decoder_spl_memo`.
|
||||
|
||||
## État du phasage
|
||||
|
||||
| Phase | Contenu | État |
|
||||
|-------|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
|
||||
| A | modèle d’événement, lecture bornée, validation des comptes et diagnostics | implémenté dans `0.4.1-pre.001` |
|
||||
| B | System Program et Compute Budget | implémenté, replay mainnet validé dans `pre.005` |
|
||||
| C | Address Lookup Table et loaders | implémenté dans `pre.003` puis `pre.006` |
|
||||
| D | Vote, Stake, Config et programmes historiques | implémenté : Config/Feature `pre.008`, Vote `pre.010` validé dans `pre.011`, Stake `pre.013` validé mainnet |
|
||||
| E | précompiles et preuves ZK | précompiles signature `pre.014`, ZK ElGamal Proof `pre.015`, ancien ZK Token Proof `pre.016` |
|
||||
| F | matérialisations justifiées, diagnostics globaux et clôture | nonce System/contextes ZK `pre.017`; Slashing et invariants de couverture `pre.018`; corpus final à valider |
|
||||
|
||||
Le processor conserve l’identité `solana_native_classifier@0.4.1` pendant les deltas préparatoires. Les nouveaux décodeurs nécessitent donc un force replay ciblé ou global ; la version ne sera relevée qu’avec un changement explicite du contrat de processor.
|
||||
|
||||
## Sources normatives couvertes
|
||||
|
||||
| Surface | Source officielle | Sérialisation retenue | Couverture |
|
||||
|---------------------------|---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------------|
|
||||
| System Program | `solana-system-interface 3.2.0`, enum `SystemInstruction` | schéma `wincode` officiel compatible avec le format bincode historique, lecture exacte sans octets suffixes | 14 variantes |
|
||||
| Compute Budget actuel | `solana-compute-budget-interface 3.0.0` + runtime `solana-compute-budget-instruction 4.1.1` | tag `u8` puis entier little-endian/Borsh ; désérialisation runtime unchecked, suffixe accepté et audité | 5 entrées, dont le tag réservé `Unused` |
|
||||
| Compute Budget historique | ancien `solana-sdk`, `RequestUnitsDeprecated` | tag `0`, `u32 units`, `u32 additional_fee` | 1 variante historique |
|
||||
| Address Lookup Table | `solana-address-lookup-table-interface 3.1.0`, enum `ProgramInstruction` | schéma `wincode` officiel, lecture exacte | 5 variantes |
|
||||
| BPF Loader immuable | `solana-loader-v2-interface 3.0.0`, enum `LoaderInstruction` | tag `u32 LE`, offset `u32`, longueur de vector `u64`, lecture exacte | write, finalize sur deux générations |
|
||||
| BPF Loader upgradeable | `solana-loader-v3-interface 8.0.1`, enum `UpgradeableLoaderInstruction` | wire format historique compatible bincode, champs booléens suffixes avec valeurs historiques documentées | 8 variantes |
|
||||
| Loader v4 | `solana-loader-v4-interface 3.1.0`, enum `LoaderV4Instruction` | tag et entiers little-endian, tailles exactes | 7 variantes |
|
||||
| Config Program | `solana-config-interface 2.0.0` et processor `solana-config-program 2.2.20` | `ConfigKeys` en `solana_short_vec`, clés `(Pubkey, bool)`, puis payload typé opaque pour le décodeur générique | 1 écriture générique `store` |
|
||||
| Feature Gate | `solana-feature-gate-interface 4.0.0`, `FeatureGateInstruction` | tag `u8` exact `0`, sans suffixe | 1 variante |
|
||||
| Vote Program | `solana-vote-interface ^6.0`, enum `VoteInstruction` | schéma officiel `wincode`, discriminant historique `u32 LE`, lecture exacte sans octets suffixes | 20 variantes |
|
||||
| Stake Program | `solana-stake-interface ^4.3`, enum `StakeInstruction` | miroir wire privé `wincode` exact du layout historique ; types sémantiques officiels consommés | 18 variantes |
|
||||
| ZK ElGamal Proof | `solana-zk-elgamal-proof-interface ^0.1`, `ProofInstruction`, types `Pod`, runtime Agave | tag `u8`; preuve inline de taille officielle ou référence compte `u32 LE` sur instruction exacte de 5 octets | 13 variantes |
|
||||
| ZK Token Proof historique | miroir wire local audité contre `solana-zk-token-sdk 3.1.14`, Agave `v2.0.0`/`v4.1.1` | tag `u8`; layout historique inline ou offset compte sur 5 octets ; fallback runtime actuel sans effet | 17 variantes + fallback no-op |
|
||||
| Slashing stateless | Agave `v4.1.1` + `solana-program/slashing@fe8da3a` | tag `u8`; fermeture sur 1 octet ou preuve duplicate block sur 305 octets exacts | 2 variantes |
|
||||
|
||||
Les versions de crates sont utilisées comme références reproductibles. Un futur changement d’interface doit entraîner un changement de version du décodeur et un replay ledger/version/hash.
|
||||
|
||||
## System Program
|
||||
|
||||
Program ID : `11111111111111111111111111111111`.
|
||||
|
||||
| Tag historique compatible bincode/wincode `u32 LE` | Entry code | Paramètres structurés | Comptes principaux | État |
|
||||
|---------------------------------------------------:|--------------------------------|------------------------------------|----------------------------------------|--------|
|
||||
| 0 | `create_account` | lamports, space, owner | funding, new account | décodé |
|
||||
| 1 | `assign` | owner | assigned account | décodé |
|
||||
| 2 | `transfer` | lamports | funding, recipient | décodé |
|
||||
| 3 | `create_account_with_seed` | base, seed, lamports, space, owner | funding, new account, base optionnelle | décodé |
|
||||
| 4 | `advance_nonce_account` | aucun | nonce, recent blockhashes, authority | décodé |
|
||||
| 5 | `withdraw_nonce_account` | lamports | nonce, recipient, sysvars, authority | décodé |
|
||||
| 6 | `initialize_nonce_account` | authority | nonce, recent blockhashes, rent | décodé |
|
||||
| 7 | `authorize_nonce_account` | authority | nonce, authority | décodé |
|
||||
| 8 | `allocate` | space | allocated account | décodé |
|
||||
| 9 | `allocate_with_seed` | base, seed, space, owner | derived account, base | décodé |
|
||||
| 10 | `assign_with_seed` | base, seed, owner | derived account, base | décodé |
|
||||
| 11 | `transfer_with_seed` | lamports, from seed, from owner | funding, base, recipient | décodé |
|
||||
| 12 | `upgrade_nonce_account` | aucun | nonce account | décodé |
|
||||
| 13 | `create_account_allow_prefund` | lamports, space, owner | new account, funding conditionnel | décodé |
|
||||
|
||||
Le décodeur utilise le schéma `wincode` dérivé directement par `solana-system-interface 3.2.0`. `wincode::deserialize_exact` conserve la compatibilité octet pour octet avec le format historique de cette enum et refuse les octets suffixes. Le crate n’utilise aucune dépendance directe à `bincode`.
|
||||
|
||||
`pre.017` matérialise les cinq opérations durable nonce sous `durable_nonce_account:<operation>:0`. Initialize, advance, authorize et upgrade conservent leur transition explicite. Withdraw conserve le montant demandé et la règle officielle de fermeture conditionnelle : le compte n’est détruit que lorsque la totalité de son solde est retirée. La projection ne prétend pas reconstruire l’état final transactionnel du compte et ne duplique pas les deltas SOL du graphe core.
|
||||
|
||||
Les flags signer/writable sont conservés sous deux formes : privilèges attendus par le contrat officiel et privilèges réellement résolus depuis core. Ils ne sont pas confondus, car une CPI signée par PDA ne se reflète pas nécessairement comme signer statique de la transaction.
|
||||
|
||||
## Compute Budget
|
||||
|
||||
Program ID : `ComputeBudget111111111111111111111111111111`.
|
||||
|
||||
| Tag `u8` | Entry code | Taille décodée minimale | Paramètres | Statut |
|
||||
|---------:|---------------------------------------|------------------------:|--------------------------|-----------------------|
|
||||
| 0 | `unused_reserved` | 1 | aucun | `ignored` |
|
||||
| 0 | `request_units_deprecated` | 9 | units, additional fee | `decoded`, historique |
|
||||
| 1 | `request_heap_frame` | 5 | bytes `u32` | `decoded` |
|
||||
| 2 | `set_compute_unit_limit` | 5 | compute unit limit `u32` | `decoded` |
|
||||
| 3 | `set_compute_unit_price` | 9 | micro-lamports `u64` | `decoded` |
|
||||
| 4 | `set_loaded_accounts_data_size_limit` | 5 | bytes `u32` | `decoded` |
|
||||
|
||||
Le runtime Agave traite les variantes modernes avec `solana_borsh::v1::try_from_slice_unchecked`. Les octets suffixes sont donc acceptés après les 5 ou 9 octets utiles. Le décodeur reproduit cette règle et conserve `trailingDataByteLength`, `trailingDataSha256`, `trailingDataPrefixHex` et `trailingDataSemantics = ignored_by_runtime_borsh_unchecked`. Les payloads trop courts restent `failed`. Le tag `0` demeure dispatché par taille afin de distinguer le tag réservé actuel de la variante historique à neuf octets ; toute autre taille pour ce tag reste `failed`. Un tag inconnu est `unsupported` avec diagnostic borné et hash du payload disponible pour un replay futur.
|
||||
|
||||
|
||||
## Address Lookup Table
|
||||
|
||||
Program ID : `AddressLookupTab1e1111111111111111111111111`.
|
||||
|
||||
| Tag `u32 LE` | Entry code | Paramètres | Comptes | État |
|
||||
|-------------:|---------------------------|------------------------------------------------------------------|--------------------------------------------------|--------|
|
||||
| 0 | `create_lookup_table` | recent slot, bump seed, politique historique du signer authority | table, authority, payer, System Program | décodé |
|
||||
| 1 | `freeze_lookup_table` | aucun | table, authority | décodé |
|
||||
| 2 | `extend_lookup_table` | adresses ajoutées, count, présence du financement | table, authority, paire optionnelle payer/System | décodé |
|
||||
| 3 | `deactivate_lookup_table` | aucun | table, authority | décodé |
|
||||
| 4 | `close_lookup_table` | aucun | table, authority, recipient | décodé |
|
||||
|
||||
Le décodeur refuse une paire optionnelle d’extension incomplète et les octets suffixes. La création conserve `expectedSigner = null` pour l’autorité, car l’encodeur officiel actuel ne la marque plus signer tandis que les runtimes historiques antérieurs à v1.12 l’exigeaient.
|
||||
|
||||
La projection `solana_native_lifecycle` produit une sortie `address_lookup_table:<operation>:0` uniquement si l’observation est exacte, réussie et commitée. Les intentions issues de transactions échouées restent dans decode et sont refusées avant matérialisation.
|
||||
|
||||
## Programmes runtime natifs principaux
|
||||
|
||||
| Code canonique | Program ID | État au début de `pre.018` |
|
||||
|--------------------|-----------------------------------------------|--------------------------------------------------------------------------------|
|
||||
| `vote` | `Vote111111111111111111111111111111111111111` | 20 variantes décodées ; 500 `tower_sync` validés mainnet |
|
||||
| `stake` | `Stake11111111111111111111111111111111111111` | 18 variantes décodées ; 339 instructions mainnet sur neuf entry codes observés |
|
||||
| `config` | `Config1111111111111111111111111111111111111` | écriture générique `store` décodée ; corpus ciblé à inventorier |
|
||||
| `zk_elgamal_proof` | `ZkE1Gama1Proof11111111111111111111111111111` | 13 variantes ; 151 instructions mainnet et 139 contextes matérialisés |
|
||||
|
||||
## Loaders
|
||||
|
||||
| Surface | Program ID | Variantes | Statut `pre.006` |
|
||||
|------------------------|-----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|
|
||||
| Native Loader | `NativeLoader1111111111111111111111111111111` | invocation directe opaque | `ignored`, historique, aucun faux événement |
|
||||
| BPF Loader deprecated | `BPFLoader1111111111111111111111111111111111` | write, finalize | décodé |
|
||||
| BPF Loader v2 | `BPFLoader2111111111111111111111111111111111` | write, finalize | décodé |
|
||||
| BPF Loader upgradeable | `BPFLoaderUpgradeab1e11111111111111111111111` | initialize buffer, write, deploy with max data length, upgrade, set authority, close, extend program, set authority checked | décodé |
|
||||
| Loader v4 | `LoaderV411111111111111111111111111111111111` | write, copy, set program length, deploy, retract, transfer authority, finalize | décodé |
|
||||
|
||||
Les payloads `write` ne recopient pas le bytecode dans l’événement : ils conservent offset, longueur et SHA-256. Les tailles de vectors, booléens, octets suffixes et paires de comptes optionnelles sont validés avant toute lecture. Les anciennes formes upgradeable sans le booléen suffixe sont acceptées avec la valeur par défaut officielle et marquées comme telles.
|
||||
|
||||
La projection lifecycle produit `program_loader:<surface>:<operation>:0` pour les mutations réussies et commitées : finalize immuable, initialize/deploy/upgrade/close/extend upgradeable, puis set length/deploy/retract/finalize v4. Les écritures, copies et changements d’autorité restent des événements decode de familles Audit ou Admin.
|
||||
|
||||
## Slashing Program stateless
|
||||
|
||||
Program ID : `S1ashing11111111111111111111111111111111111`.
|
||||
|
||||
Agave `v4.1.1` le déclare comme stateless builtin derrière `enshrine_slashing_program`, avec un hash de build vérifié correspondant à la release officielle `solana-program/slashing@fe8da3a`.
|
||||
|
||||
| Tag `u8` | Entry code | Taille exacte | Effet runtime observé |
|
||||
|---------:|--------------------------|--------------:|------------------------------------------------------------------------|
|
||||
| 0 | `close_violation_report` | 1 | ferme le rapport après au moins trois epochs et transfère ses lamports |
|
||||
| 1 | `duplicate_block_proof` | 305 | vérifie une preuve externe puis stocke un rapport de violation |
|
||||
|
||||
La preuve duplicate block est lue depuis un compte externe à l’offset `u64` de l’instruction. Le replay transactionnel ne possède pas le contenu historique de ce compte. Le décodeur conserve l’offset, le slot, les clés, racines et signatures intégrées à l’instruction, résume l’instruction Ed25519 précédente et ne prétend ni relire le compte ni recalculer les signatures.
|
||||
|
||||
Une transaction réussie prouve seulement l’acceptation runtime, l’assignation/allocation du PDA préfinancé et l’écriture du rapport. Le programme ne brûle ni ne retire lui-même du stake : la projection `slashing_violation_report` conserve donc `penaltyAppliedByProgram = false` et décrit un signal destiné à une application de pénalité externe au programme.
|
||||
|
||||
## Précompiles de signature `pre.014`
|
||||
|
||||
| Code canonique | Program ID | Format | État `pre.014` |
|
||||
|----------------|-----------------------------------------------|---------------------------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| `ed25519` | `Ed25519SigVerify111111111111111111111111111` | header 2 octets, offsets 14 octets, clé 32, signature 64 | décodé, zéro signature accepté seulement sur 2 octets |
|
||||
| `secp256k1` | `KeccakSecp256k11111111111111111111111111111` | header 1 octet, offsets 11 octets, adresse 20, signature 64 + recovery ID | décodé, index `u8` explicites, zéro signature accepté seulement sur 1 octet |
|
||||
| `secp256r1` | `Secp256r1SigVerify1111111111111111111111111` | header 2 octets, offsets 14 octets, clé compressée 33, signature 64 | décodé, zéro signature refusé, maximum 8 |
|
||||
|
||||
Le contrat core `2` fournit les payloads de toutes les instructions outer. Ed25519 et secp256r1 interprètent `u16::MAX` comme la cible ; secp256k1 résout toujours un index explicite. Les références aux inner instructions ne sont pas autorisées. Les offsets et longueurs utilisent des additions vérifiées avant chaque slice.
|
||||
|
||||
Chaque observation conserve les index bruts et résolus, la provenance, les longueurs, SHA-256 et préfixes bornés des composants, ainsi que `runtimeVerification`. Aucun message complet n’est recopié, aucune signature n’est recalculée et aucune sortie métier n’est matérialisée. Une transaction échouée reste `decoded` si le layout est valide, mais porte `not_asserted_transaction_failed` et reste non commitée.
|
||||
|
||||
## Surfaces historiques ou complémentaires
|
||||
|
||||
| Code canonique | Program ID | État après `pre.008` |
|
||||
|------------------|-----------------------------------------------|-----------------------------------------------------------------------------------------------------|
|
||||
| `feature` | `Feature111111111111111111111111111111111111` | `revoke_pending_activation` décodé et lifecycle matérialisable |
|
||||
| `zk_token_proof` | `ZkTokenProof1111111111111111111111111111111` | 17 layouts historiques décodés + fallback `current_runtime_noop_invocation`; validation synthétique |
|
||||
|
||||
|
||||
|
||||
### Ancien ZK Token Proof Program
|
||||
|
||||
`pre.016` couvre les discriminants `0..16` de l’interface historique : fermeture du contexte, zero balance, withdraw, égalités de ciphertext/commitment, transfer, transfer with fee, validité de pubkey, range proofs simples et batchés, validité de grouped ciphertext à deux ou trois handles et fee sigma. Les tailles de preuve et de contexte sont maintenant conservées dans une table locale auditée contre les types `Pod` de `solana-zk-token-sdk 3.1.14`, sans dépendre de cette crate dépréciée.
|
||||
|
||||
Le runtime historique Agave `v2.0.0` distinguait une preuve inline d’une preuve stockée dans le premier compte lorsque l’instruction faisait exactement cinq octets. Il imposait des arités distinctes pour le contexte, refusait les vérifications comme inner instructions et plaçait certains chemins derrière `enable_zk_proof_from_account` ou `enable_zk_transfer_with_fee`. Le runtime Agave `v4.1.1` est au contraire un stub qui retourne succès sans mutation. Le décodeur conserve ces deux références sans prétendre connaître la version runtime d’une transaction donnée.
|
||||
|
||||
Aucune signature mainnet n’a été trouvée pour ce program ID lors du jalon. La surface est donc validée par fixtures synthétiques et sources officielles, sans critère de backfill. Les preuves inline sont seulement mesurées, hashées et préfixées de façon bornée ; les preuves externes conservent l’offset et la taille attendue. Aucune preuve n’est recalculée et aucune sortie n’est matérialisée.
|
||||
|
||||
### Stake Program
|
||||
|
||||
`pre.013` couvre les dix-huit variantes de `StakeInstruction`, tags `u32 LE` `0..17` : initialize, authorize, delegate, split, withdraw, deactivate, set lockup, merge, autorisations avec seed, variantes checked, minimum delegation, deactivate delinquent, `redelegate`, move stake et move lamports. `Redelegate` reste déclaré historique : le processor actuel renvoie `InvalidInstructionData` avant tout traitement de comptes ; une occurrence réelle doit donc provenir d’une transaction échouée et reste non commitée.
|
||||
|
||||
L’enum officielle de `solana-stake-interface ^4.3` conserve le contrat Serde/Bincode historique, mais n’implémente pas directement `SchemaRead`/`SchemaWrite`. Pour respecter l’interdiction workspace de dépendre de `bincode`, le décodeur utilise un miroir wire privé, ordonné exactement comme l’enum officielle, décodé par `wincode::deserialize_exact`. Les tests vérifient manuellement les tags, pubkeys, entiers, enums imbriquées et longueurs de chaînes du layout historique. Les types sémantiques et le program ID officiels restent consommés depuis l’interface.
|
||||
|
||||
Le processor BPF actuel supporte deux familles de comptes. Les layouts historiques sont détectés par la première sysvar héritée, généralement Clock ou Rent ; les layouts modernes retirent ces sysvars et placent directement les autorités aux positions attendues. `Split`, `SetLockup` et `SetLockupChecked` conservent volontairement les contraintes historiques plus souples du runtime. Le décodeur expose `accountLayout`, conserve les comptes additionnels et les privilèges attendus/observés, mais ne prétend pas rejouer les vérifications dépendant de l’état du stake account, du lockup, de la délégation ou des sysvars courantes. Aucune matérialisation Stake n’est ajoutée dans ce delta.
|
||||
|
||||
### Compte Stake Config historique
|
||||
|
||||
`StakeConfig11111111111111111111111111111111` n’est pas un programme exécutable. L’interface Stake officielle le déclare comme identifiant déprécié du compte de configuration historique du Stake Program. Il reste dans `kb_program_ids` comme `STAKE_CONFIG_ACCOUNT_ID` et dans le registre core avec `identifier_kind = well_known_account`, mais il est exclu des surfaces du décodeur et de l’exécuteur.
|
||||
|
||||
### Config Program
|
||||
|
||||
Le Config Program ne possède pas un enum d’instructions discriminé comparable à System ou Stake. Chaque invocation stocke un préfixe `ConfigKeys` sérialisé avec `solana_short_vec`, suivi d’un payload spécifique au type de configuration. Le décodeur valide la longueur compacte, les clés, les booléens signataires et l’ordre des comptes signataires, puis conserve le reste par taille, préfixe borné et SHA-256 avec la sémantique explicite `opaque_program_specific`.
|
||||
|
||||
### Vote Program
|
||||
|
||||
`pre.010` utilise `solana-vote-interface ^6.0` avec les features `serde` et `wincode`. Le discriminant wire historique reste un `u32` little-endian compris entre `0` et `19`, et `wincode::deserialize_exact` rejette les octets résiduels. `VoteInitV2` contient les autorités, la clé BLS, sa preuve de possession et les deux commissions en points de base ; les collecteurs Inflation Rewards et Block Revenue sont les comptes d’instruction 2 et 3.
|
||||
|
||||
La couverture comprend : initialisations V1/V2, autorisations simples/checked/seed, vote et vote switch, mises à jour d’état normales/compactes, tower sync, retrait, identité, commissions historiques et en points de base, collecteurs différenciés et dépôt de récompenses délégateurs. Les tableaux BLS V2 sont conservés en hexadécimal exact. Les sysvars Rent, Clock et Slot Hashes sont vérifiées aux positions imposées par l’interface. Les privilèges attendus et observés restent distincts afin de ne pas confondre les signataires statiques et les signatures PDA de CPI.
|
||||
|
||||
Aucune projection matérialisée spécifique à Vote n’est ajoutée dans ce delta : la mutation réelle dépend de l’état du vote account et du runtime. Une transaction échouée produit uniquement une observation non commitée.
|
||||
|
||||
## ZK ElGamal Proof `pre.015`
|
||||
|
||||
Program ID : `ZkE1Gama1Proof11111111111111111111111111111`.
|
||||
|
||||
Le décodeur consomme `solana-zk-elgamal-proof-interface ^0.1` pour l’enum `ProofInstruction` et les tailles exactes des types `Pod`. Il couvre `CloseContextState` et les douze preuves : zero ciphertext, égalités ciphertext/ciphertext et ciphertext/commitment, validité de clé publique, percentage with cap, range proofs 64/128/256 bits et validités grouped/batched à deux ou trois handles.
|
||||
|
||||
Deux modes de transport sont distingués conformément au runtime :
|
||||
|
||||
- instruction de cinq octets : tag puis offset `u32 LE` vers un compte contenant la preuve ;
|
||||
- autre taille exacte : preuve inline immédiatement après le tag, avec taille égale au type `ProofData` officiel.
|
||||
|
||||
Pour une preuve inline, les blocs contexte et preuve sont séparés par leurs tailles `Pod`, puis conservés uniquement sous forme de longueur, SHA-256 et préfixe hexadécimal borné. Le décodeur ne recalcule aucune preuve cryptographique. Pour une transaction réussie, il indique seulement que l’instruction a été acceptée par le runtime. Pour une transaction échouée, aucune validité n’est affirmée et l’observation reste non commitée.
|
||||
|
||||
Le mode référencé par compte a une limite historique structurelle : le graphe core de transaction ne contient pas le contenu du compte à l’instant d’exécution. L’événement conserve donc le compte source, l’offset, le type et la taille attendue, mais marque les octets de preuve comme indisponibles. Il ne tente ni lecture RPC actuelle, qui pourrait être divergente, ni reconstruction.
|
||||
|
||||
Les comptes optionnels de contexte sont résolus selon le mode : positions 0/1 pour une preuve inline et 1/2 après le compte de preuve pour une preuve externe. La fermeture du contexte conserve le contexte, la destination des lamports et l’autorité signataire, accepte les octets suffixes comme le runtime et refuse que contexte et destination désignent le même compte.
|
||||
|
||||
`pre.017` matérialise le lifecycle du compte de contexte sous `zk_proof_context:<operation>:0`. Une vérification n’est éligible que lorsque `contextStateRequested = true`, que la transaction a réussi et que l’observation est commitée. La sortie conserve le compte, l’autorité et le type de preuve, mais jamais la preuve elle-même. La fermeture réussie décrit la récupération des lamports, la remise à zéro des données et le retour de l’owner au System Program. Les preuves sans contexte restent en decode/audit.
|
||||
|
||||
## Statuts et politique d’échec
|
||||
|
||||
- `decoded` signifie que le format complet a été validé et qu’une observation structurée a été produite.
|
||||
- `ignored` est réservé à une entrée comprise mais sans événement utile, comme le tag Compute Budget `Unused`.
|
||||
- `unsupported` conserve une variante inconnue ou une surface non encore implémentée sans faux événement.
|
||||
- `failed` indique un payload absent, invalide, tronqué, non canonique ou des comptes incohérents.
|
||||
- `unmatched` reste un défaut de dispatch et ne doit pas apparaître pour une sélection bornée aux surfaces déclarées.
|
||||
|
||||
Une transaction on-chain échouée reste décodable comme intention. L’observation porte `transactionSucceeded = false` et `observation_committed = false`. Le matérialiseur `solana_native_lifecycle` est actif pour ALT, les mutations loader listées, Feature Gate, les durable nonce accounts System et les comptes de contexte ZK ElGamal. Il accepte les familles `Lifecycle`, `Admin` et `Audit` uniquement au niveau exact surface/entrée/paramètres, puis applique `SuccessfulCommittedOnly`. Une observation non commitée est refusée et une preuve ZK sans contexte est exclue avant ledger et politique.
|
||||
|
||||
## Validation mainnet `pre.005`
|
||||
|
||||
Les correctifs de `pre.005` ont été validés par les tests ciblés, `cargo clippy --all-targets` et deux campagnes Tauri. Le force replay ciblé a décodé 3/3 inputs sans échec. Le replay global a sélectionné, démarré et terminé 476 inputs : 476 décodés, zéro `failed`, `unsupported`, `unmatched` et zéro faux `materializationRefused`. Aucun output n’était attendu, car le corpus ne contenait pas d’opération ALT éligible.
|
||||
|
||||
## Validation `pre.006`, `pre.008`, Vote mainnet et anomalie Compute Budget
|
||||
|
||||
Les tests `pre.006`, Clippy et le replay Tauri global ont été validés : 476 inputs sélectionnés, 476 décodés, aucun échec, `unsupported`, `unmatched` ou refus de matérialisation. Les validations utilisateur de `pre.008` couvrent 256 tests ciblés, PostgreSQL réel, Clippy et un démarrage/replay Tauri propre. Le corpus initial ne contenait que System et Compute Budget : loaders, Config et Feature restent en attente d’un corpus réel.
|
||||
|
||||
Le backfill Vote de 500 signatures a inséré 500 transactions et l’extraction core a terminé 500/500 sans échec. Le replay global suivant contient 500 instructions Vote réelles, toutes reconnues et décodées comme `tower_sync`, sans échec Vote. L’unique `failed` de la campagne provient d’un `set_compute_unit_limit` de 12 octets : le runtime l’accepte grâce à la désérialisation Borsh unchecked, alors que le décodeur strict exigeait encore 5 octets exacts. `pre.011` corrige cet écart. Le processor reste `solana_native_classifier@0.4.1` : un force replay est requis pour remplacer le résultat `failed` déjà enregistré.
|
||||
106
migration/khadhroony-bot2-reference/docs/NOMENCLATURE.md
Normal file
106
migration/khadhroony-bot2-reference/docs/NOMENCLATURE.md
Normal file
@@ -0,0 +1,106 @@
|
||||
<!-- file: docs/NOMENCLATURE.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Nomenclature
|
||||
|
||||
Ce document fixe les noms internes utilisés dans le workspace.
|
||||
|
||||
## Programmes
|
||||
|
||||
- `program_id` : adresse Solana réelle.
|
||||
- `program_code` : nom interne stable du programme.
|
||||
- `protocol_code` : famille protocolaire.
|
||||
- `surface_code` : surface concrète.
|
||||
|
||||
## Événements
|
||||
|
||||
Le format canonique est :
|
||||
|
||||
```text
|
||||
<surface_code>.<event_name>
|
||||
```
|
||||
|
||||
Exemples :
|
||||
|
||||
```text
|
||||
pump_swap.buy
|
||||
raydium_amm_v4.swap_base_in
|
||||
meteora_dlmm.swap
|
||||
jupiter_v6.route
|
||||
```
|
||||
|
||||
## Tables Solana PostgreSQL
|
||||
|
||||
Les tables Solana PostgreSQL utilisent le schéma courant du profil PostgreSQL, généralement `public`. Le projet ne crée pas de schémas applicatifs `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`.
|
||||
|
||||
Le format canonique est :
|
||||
|
||||
```text
|
||||
kb_sol_<domain>_<name>
|
||||
```
|
||||
|
||||
Domaines autorisés au départ :
|
||||
|
||||
```text
|
||||
raw
|
||||
core
|
||||
obs
|
||||
decode
|
||||
mat
|
||||
catalog
|
||||
agg
|
||||
ops
|
||||
wallet
|
||||
```
|
||||
|
||||
Exemples valides :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions
|
||||
kb_sol_core_transactions
|
||||
kb_sol_obs_transaction_observations
|
||||
kb_sol_obs_program_observations
|
||||
kb_sol_decode_decoded_events
|
||||
kb_sol_mat_trade_events
|
||||
kb_sol_catalog_tokens
|
||||
kb_sol_ops_processing_ledger
|
||||
```
|
||||
|
||||
Exemples interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel :
|
||||
|
||||
```text
|
||||
raw DOT kb_sol_rpc_transactions
|
||||
core DOT kb_sol_transactions
|
||||
obs DOT kb_sol_program_observations
|
||||
decode DOT kb_sol_decoded_events
|
||||
mat DOT kb_sol_trade_events
|
||||
catalog DOT kb_sol_tokens
|
||||
ops DOT kb_sol_processing_ledger
|
||||
```
|
||||
|
||||
## Surfaces de programmes
|
||||
|
||||
Le nom canonique d'une surface de programme doit suivre le format :
|
||||
|
||||
```text
|
||||
<function_code>_<family_code>_<identifier_code>[_vN]
|
||||
```
|
||||
|
||||
Le nom de crate correspondant doit suivre le format :
|
||||
|
||||
```text
|
||||
kb_decoder_<function_code>_<family_code>_<identifier_code>[_vN]
|
||||
```
|
||||
|
||||
Les anciens noms issus des IDL, des explorateurs ou des anciennes matrices doivent être conservés dans le registre sous `source_code` ou `normalized_code`, mais ne doivent pas être utilisés comme nouvelle référence stable si un nom canonique existe.
|
||||
|
||||
Voir aussi :
|
||||
|
||||
- `docs/DATABASE.md` ;
|
||||
- `docs/PROGRAM_NAMING.md` ;
|
||||
- `docs/PROGRAM_REGISTRY_CONTROL.md` ;
|
||||
- `registry/program_registry_seed.toml`.
|
||||
|
||||
Le préfixe `program_` est interdit pour les surfaces canoniques. Les entrées dont la fonction est inconnue doivent rester en `unknown_*` avec un statut de classification, sans création de crate cible.
|
||||
|
||||
Les programmes core Solana/SPL sont documentés séparément des DEX et routers, même s'ils restent dans le registre machine.
|
||||
91
migration/khadhroony-bot2-reference/docs/POSTGRES_STORE.md
Normal file
91
migration/khadhroony-bot2-reference/docs/POSTGRES_STORE.md
Normal file
@@ -0,0 +1,91 @@
|
||||
<!-- file: docs/POSTGRES_STORE.md -->
|
||||
<!-- version: 10 -->
|
||||
|
||||
# Store PostgreSQL canonique
|
||||
|
||||
## Baseline active
|
||||
|
||||
PostgreSQL reste le backend principal. Le store utilise le schéma courant du profil, généralement `public`, et ne qualifie pas les tables par un schéma applicatif explicite.
|
||||
|
||||
La baseline active contient :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions
|
||||
kb_sol_obs_transaction_observations
|
||||
kb_sol_core_transactions
|
||||
kb_sol_core_account_keys
|
||||
kb_sol_core_instructions
|
||||
kb_sol_core_inner_instructions
|
||||
kb_sol_core_logs
|
||||
kb_sol_core_balance_changes
|
||||
kb_sol_ops_processing_ledger
|
||||
kb_sol_decode_events
|
||||
kb_sol_decode_coverage_declarations
|
||||
kb_sol_decode_coverage_observations
|
||||
kb_sol_mat_events
|
||||
```
|
||||
|
||||
## Initialisation
|
||||
|
||||
Le projet utilise encore des DDL idempotents gérés par `kb_store_pg`. `_sqlx_migrations` n’est pas requis pour démarrer l’application.
|
||||
|
||||
Les fichiers SQL actifs sont conservés comme représentation lisible de la baseline :
|
||||
|
||||
```text
|
||||
0001_canonical_transaction_store.sql
|
||||
0002_core_store.sql
|
||||
0003_processing_ledger.sql
|
||||
0004_decode_materialization_store.sql
|
||||
```
|
||||
|
||||
Au démarrage Tauri, `initialize_store_schema()` applique une seule fois, dans l’ordre, les DDL raw, core puis decode/mat. Les connexions ouvertes ensuite par les commandes de démo désactivent `auto_initialize_schema` et ne relancent plus les migrations. Les méthodes spécialisées restent disponibles pour les tests et outils qui demandent explicitement l’initialisation d’un sous-ensemble dépendant.
|
||||
|
||||
## Extraction canonique vers core
|
||||
|
||||
`CoreExtractionStore` sélectionne les lignes raw, vérifie le ledger puis persiste un `CoreExtractionBundle` dans une transaction PostgreSQL unique.
|
||||
|
||||
Le mode normal skippe la combinaison déjà réussie :
|
||||
|
||||
```text
|
||||
processor version + signature + canonical hash
|
||||
```
|
||||
|
||||
Le replay forcé remplace uniquement le graphe de la signature ciblée. Les autres signatures ne sont pas modifiées.
|
||||
|
||||
## Tests PostgreSQL
|
||||
|
||||
Les tests qui utilisent `KB_POSTGRES_TEST_URL` doivent être lancés sur une base dédiée ou suivis d’un nettoyage explicite.
|
||||
|
||||
Depuis `0.4.1-pre.021`, les tests optionnels qui partagent une base PostgreSQL réelle acquièrent un verrou process-local test-only. Ce verrou évite les interblocages intermittents entre campagnes de tests parallèles sans changer le code runtime ni le schéma SQL.
|
||||
|
||||
Les tests optionnels couvrent notamment le rollback atomique core/decode, la persistance réelle du ledger et la sémantique exacte des déclarations de couverture. `optional_postgres_core_extraction_rolls_back_partial_graph_from_env` :
|
||||
|
||||
1. insère une ligne raw isolée ;
|
||||
2. tente de persister un bundle contenant deux account keys avec le même index ;
|
||||
3. attend une violation de l’index unique après le début de la transaction ;
|
||||
4. vérifie qu’aucune transaction core, account key ou entrée ledger n’a été conservée et que le raw reste `received` ;
|
||||
5. rejoue un bundle corrigé ;
|
||||
6. vérifie le passage à `core_extracted` et le ledger `succeeded` ;
|
||||
7. nettoie ses lignes de test.
|
||||
|
||||
Commande de clôture :
|
||||
|
||||
```bash
|
||||
KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \
|
||||
cargo test -p kb_store_pg -- --nocapture
|
||||
```
|
||||
|
||||
Les contrôles réels déjà effectués sur la base principale ont confirmé :
|
||||
|
||||
- 70 transactions raw et core ;
|
||||
- 70 entrées ledger `succeeded` ;
|
||||
- 84 tentatives après un force replay de 14 signatures ;
|
||||
- aucune duplication ni ligne fille orpheline ;
|
||||
- cardinalité core inchangée après remplacement forcé.
|
||||
|
||||
|
||||
## Couverture et skip decode
|
||||
|
||||
`persist_decode_coverage_declarations()` synchronise le snapshot déclaré d’un processor/version et retourne des compteurs exacts : insertion réelle, modification/suppression du snapshot ou déclaration inchangée.
|
||||
|
||||
Le test PostgreSQL `optional_postgres_same_version_and_hash_is_current_from_env` persiste un résultat `unsupported` puis vérifie que le même stage, processor, version, input key et input hash est reconnu comme courant par le ledger.
|
||||
@@ -0,0 +1,42 @@
|
||||
<!-- file: docs/PROGRAM_CODE_INDEX.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Index court des programmes
|
||||
|
||||
L'idée d'un préfixe hexadécimal `00` à `ff` peut être utile comme identifiant court interne, mais elle ne doit pas remplacer le nom canonique lisible.
|
||||
|
||||
## Décision
|
||||
|
||||
Le nom de crate reste descriptif :
|
||||
|
||||
```text
|
||||
kb_decoder_amm_pump_swap
|
||||
kb_decoder_router_jupiter_aggregator_v6
|
||||
kb_decoder_clmm_orca_whirlpool
|
||||
```
|
||||
|
||||
Un code court optionnel peut être ajouté dans le registre :
|
||||
|
||||
```toml
|
||||
registry_code = "00"
|
||||
canonical_surface_code = "amm_pump_swap"
|
||||
target_crate = "kb_decoder_amm_pump_swap"
|
||||
```
|
||||
|
||||
## Pourquoi ne pas mettre `00_` dans les crates
|
||||
|
||||
- Le code `00` ne décrit pas la fonction du programme.
|
||||
- L'ordre peut changer quand de nouvelles surfaces sont ajoutées.
|
||||
- Deux cent cinquante-six valeurs peuvent devenir insuffisantes si toutes les surfaces, sysvars, routers, vaults, perps, bridges et variantes historiques sont incluses.
|
||||
- Les renommages de crates deviendraient fréquents et inutiles.
|
||||
|
||||
## Usage acceptable
|
||||
|
||||
Le code court est acceptable pour :
|
||||
|
||||
- affichage UI compact ;
|
||||
- clé stable optionnelle dans PostgreSQL ;
|
||||
- tri manuel dans les matrices ;
|
||||
- identifiant de configuration humain court.
|
||||
|
||||
Le code court ne doit pas servir de préfixe principal de nommage Rust.
|
||||
15
migration/khadhroony-bot2-reference/docs/PROGRAM_IDS.md
Normal file
15
migration/khadhroony-bot2-reference/docs/PROGRAM_IDS.md
Normal file
@@ -0,0 +1,15 @@
|
||||
<!-- file: docs/PROGRAM_IDS.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Identifiants de programmes
|
||||
|
||||
`kb_program_ids` est la source unique des `program_id` connus du workspace.
|
||||
|
||||
Les décodeurs et exécuteurs ne doivent pas redéfinir les mêmes chaînes dans leurs propres fichiers `constants.rs`. Ils doivent utiliser directement `kb_program_ids::XXX_PROGRAM_ID`.
|
||||
|
||||
Les fichiers `constants.rs` locaux restent utiles pour les constantes propres à une surface : discriminators, opcodes, seeds, index de comptes, tailles de payload ou constantes Borsh.
|
||||
|
||||
## Audit de génération
|
||||
|
||||
Le crate central a été alimenté à partir des constantes existantes des décodeurs/exécuteurs et des fichiers registry.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<!-- file: docs/PROGRAM_ID_CONSTANTS_AUDIT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Audit des constantes `PROGRAM_ID` des décodeurs
|
||||
|
||||
Ce document décrit la correction appliquée aux crates de décodeurs classifiés. Chaque décodeur associé à un `program_id` dans `registry/program_registry_seed.toml` doit exposer ce `program_id` via `src/constants.rs`, le réexporter depuis `src/lib.rs`, puis l’utiliser dans `program_ids()` via `crate::XXX_PROGRAM_ID`.
|
||||
|
||||
## Règle retenue
|
||||
|
||||
- `src/constants.rs` contient les identifiants stables du programme, puis plus tard les discriminants, sélecteurs, discriminators Anchor, codes d’instruction et constantes de décodage.
|
||||
- `src/lib.rs` réexporte les constantes publiques nécessaires.
|
||||
- `src/decoder.rs` ne doit pas contenir de literal de `program_id` dans `program_ids()`.
|
||||
- Les décodeurs génériques comme `kb_decoder_anchor` ne doivent pas inventer de `program_id`.
|
||||
|
||||
## Portée
|
||||
|
||||
La correction couvre les décodeurs classifiés présents dans le workspace et associés à un `program_id` non vide dans le registre. Les crates core Solana/SPL disposaient déjà de `constants.rs`.
|
||||
|
||||
152
migration/khadhroony-bot2-reference/docs/PROGRAM_NAMING.md
Normal file
152
migration/khadhroony-bot2-reference/docs/PROGRAM_NAMING.md
Normal file
@@ -0,0 +1,152 @@
|
||||
<!-- file: docs/PROGRAM_NAMING.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Nommage canonique des programmes et surfaces
|
||||
|
||||
Le nommage doit permettre de distinguer la fonction réelle du programme, sa famille protocolaire et sa version.
|
||||
|
||||
## Format recommandé
|
||||
|
||||
```text
|
||||
<function_code>_<family_code>_<identifier_code>[_vN]
|
||||
```
|
||||
|
||||
Le nom de crate correspondant est :
|
||||
|
||||
```text
|
||||
kb_decoder_<function_code>_<family_code>_<identifier_code>[_vN]
|
||||
```
|
||||
|
||||
|
||||
## Préfixes fonctionnels autorisés
|
||||
|
||||
Le préfixe doit décrire la fonction principale du programme, pas seulement le fait qu'il s'agit d'un programme Solana.
|
||||
|
||||
Exemples de préfixes acceptés :
|
||||
|
||||
```text
|
||||
core
|
||||
token
|
||||
amm
|
||||
cpmm
|
||||
clmm
|
||||
dlmm
|
||||
stable_swap
|
||||
weighted_swap
|
||||
orderbook
|
||||
router
|
||||
launchpad
|
||||
lending
|
||||
vault
|
||||
staking
|
||||
bridge
|
||||
perpetuals
|
||||
oracle
|
||||
nft
|
||||
metadata
|
||||
admin
|
||||
fees
|
||||
governance
|
||||
treasury
|
||||
lock
|
||||
vesting
|
||||
unknown
|
||||
```
|
||||
|
||||
Le préfixe `program` est interdit pour les nouveaux noms canoniques. Une entrée non classifiée doit utiliser `unknown_*` et ne doit pas déclencher la création d'une crate cible avant classification.
|
||||
|
||||
## Cas Meteora DAMM
|
||||
|
||||
`damm` est conservé comme identifiant de surface Meteora, mais pas comme préfixe fonctionnel. La forme attendue est donc `amm_meteora_damm_v1` ou `amm_meteora_damm_v2`. En revanche `cpmm`, `clmm`, `dlmm`, `stable_swap` et `weighted_swap` sont des mécanismes et peuvent être utilisés comme préfixes fonctionnels.
|
||||
|
||||
## Colonnes à conserver
|
||||
|
||||
Le registre ne doit pas perdre les anciens noms. Chaque programme doit conserver :
|
||||
|
||||
- `program_id` : adresse Solana réelle ;
|
||||
- `source_code` : nom brut issu de la liste, d’un IDL ou d’une ancienne documentation ;
|
||||
- `normalized_code` : nom nettoyé sans faute évidente ;
|
||||
- `canonical_surface_code` : nom cible stable ;
|
||||
- `current_crate` : crate existant si la surface est déjà réservée ;
|
||||
- `target_crate` : nom de crate à atteindre après migration contrôlée ;
|
||||
- `registry_status` : statut de contrôle.
|
||||
|
||||
## Exemples de normalisation
|
||||
|
||||
| Nom source | Fonction | Famille | Nom canonique |
|
||||
|---------------------------|-------------|------------|------------------------------------|
|
||||
| `raydium_lp_v4` | `amm` | `raydium` | `amm_raydium_lp_v4` |
|
||||
| `raydium_cpmm` | `cpmm` | `raydium` | `cpmm_raydium` |
|
||||
| `raydium_clmm` | `clmm` | `raydium` | `clmm_raydium` |
|
||||
| `meteora_dlmm` | `dlmm` | `meteora` | `dlmm_meteora` |
|
||||
| `meteora_damm_v2` | `amm` | `meteora` | `amm_meteora_damm_v2` |
|
||||
| `jupiter_agregator_v6` | `router` | `jupiter` | `router_jupiter_aggregator_v6` |
|
||||
| `okx_labs_v2` | `router` | `okx` | `router_okx_labs_v2` |
|
||||
| `openbook_v2` | `orderbook` | `openbook` | `orderbook_openbook_v2` |
|
||||
| `phoenix` | `orderbook` | `phoenix` | `orderbook_phoenix_v1` |
|
||||
| `pump_swap` | `amm` | `pump` | `amm_pump_swap` |
|
||||
| `pump_fun` | `launchpad` | `pump` | `launchpad_pump_fun` |
|
||||
| `pump_fees` | `admin` | `pump` | `admin_pump_fees` |
|
||||
| `orca_whirlpool` | `clmm` | `orca` | `clmm_orca_whirlpool` |
|
||||
| `metaplex_token_metadata` | `metadata` | `metaplex` | `metadata_metaplex_token_metadata` |
|
||||
| `bubblegum` | `nft` | `metaplex` | `nft_metaplex_bubblegum` |
|
||||
|
||||
|
||||
## Exceptions conservées
|
||||
|
||||
Les programmes Solana/SPL de base restent groupés dans des crates techniques déjà lisibles :
|
||||
|
||||
- `kb_decoder_solana_core` ;
|
||||
- `kb_decoder_spl_token` ;
|
||||
- `kb_decoder_spl_token_2022` ;
|
||||
- `kb_decoder_spl_associated_token_account`.
|
||||
|
||||
Ces crates ne sont pas renommées en `kb_decoder_core_*` ou `kb_decoder_token_*`, car elles servent de couche primitive avant les surfaces DEX/router.
|
||||
|
||||
## Politique de migration
|
||||
|
||||
La migration physique des dossiers ne doit pas être faite en masse. Pour chaque rename :
|
||||
|
||||
1. prouver que le `program_id` correspond à la surface ;
|
||||
2. créer ou renommer une seule crate ;
|
||||
3. mettre à jour `Cargo.toml` ;
|
||||
4. mettre à jour le registre ;
|
||||
5. exécuter `cargo build` ;
|
||||
6. supprimer l’ancien alias seulement après validation.
|
||||
|
||||
|
||||
|
||||
## Préfixe hexadécimal
|
||||
|
||||
Les préfixes `00` à `ff` sont interdits comme préfixes principaux de crate. Ils peuvent exister uniquement comme champ de registre optionnel `registry_code`. Le nom canonique doit rester lisible et fonctionnel.
|
||||
|
||||
Exemple accepté :
|
||||
|
||||
```toml
|
||||
registry_code = "01"
|
||||
canonical_surface_code = "amm_pump_swap"
|
||||
target_crate = "kb_decoder_amm_pump_swap"
|
||||
```
|
||||
|
||||
Exemple refusé :
|
||||
|
||||
```text
|
||||
kb_decoder_01_pump_swap
|
||||
```
|
||||
|
||||
## Fichiers `constants.rs`
|
||||
|
||||
Les crates peuvent avoir un fichier `src/constants.rs` pour regrouper les `program_id`, discriminants, sélecteurs, longueurs Borsh et autres constantes techniques. Les `program_id` publics sont réexportés dans `lib.rs`. Les constantes internes futures, par exemple des discriminants ou tailles de payload, doivent rester `pub(crate)` sauf besoin explicite d'API publique.
|
||||
|
||||
Depuis un module interne de la même crate, une constante réexportée par `lib.rs` doit être appelée via `crate::CONSTANT_NAME`. Le chemin `crate::constants::CONSTANT_NAME` reste réservé aux constantes non réexportées.
|
||||
|
||||
## Préfixes ajoutés après inspection IDL
|
||||
|
||||
- `adapter` : wrapper technique ou adaptation de format, par exemple `adapter_saber_decimal_wrapper`.
|
||||
- `rwa` : programme d'actifs réels tokenisés ou marchés globaux, par exemple `rwa_ondo_global_markets`.
|
||||
- `vesting` : programme de vesting, streaming ou timelock.
|
||||
- `wallet` : smart wallet applicatif ou surface d'approbation/exécution.
|
||||
|
||||
## Préfixe `fees`
|
||||
|
||||
Le préfixe `fees` est autorisé pour les programmes dont le rôle principal est la configuration, distribution ou réclamation de frais. Il doit être préféré à `admin` lorsque la surface est explicitement centrée sur le partage de frais.
|
||||
@@ -0,0 +1,328 @@
|
||||
<!-- file: docs/PROGRAM_REGISTRY_CONTROL.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Contrôle du registre des programmes
|
||||
|
||||
Ce document transforme la liste de programmes fournie en registre contrôlable. Il ne valide pas encore chaque `program_id` sur chaîne ; il sert de base interne pour détecter les doublons, les aliases, les surfaces mal nommées et les crates manquantes.
|
||||
|
||||
## Résumé
|
||||
|
||||
- Entrées du registre : 150.
|
||||
- `program_id` distincts : 149.
|
||||
- Doublons exacts de `program_id` : 1.
|
||||
- Registre machine : `registry/program_registry_seed.toml`.
|
||||
- La colonne `Canonique` est la référence de tri et de migration.
|
||||
|
||||
## Correction de principe
|
||||
|
||||
La table de contrôle est maintenant triée par `Canonique`, avec `Canonique` en première colonne. C'est le nom qui doit piloter les futures crates, les noms de surfaces et les tables de couverture.
|
||||
|
||||
Les programmes Solana/SPL de base ne sont plus mélangés avec les DEX, routers et protocoles applicatifs. Ils sont listés dans un tableau séparé parce qu'ils relèvent de la couche core : `kb_decoder_solana_core`, `kb_decoder_spl_token`, `kb_decoder_spl_token_2022` et futurs décodeurs primitifs.
|
||||
|
||||
## Pourquoi `amm` et pas `damm` en préfixe ?
|
||||
|
||||
Le préfixe doit représenter la fonction générique du programme. `amm` signifie Automated Market Maker. `damm` est un nom de surface Meteora, généralement interprété comme Dynamic AMM. Donc `damm` ne doit pas être utilisé comme préfixe fonctionnel général.
|
||||
|
||||
La forme recommandée est :
|
||||
|
||||
```text
|
||||
amm_meteora_damm_v1
|
||||
amm_meteora_damm_v2
|
||||
```
|
||||
|
||||
et non :
|
||||
|
||||
```text
|
||||
damm_meteora_v1
|
||||
damm_meteora_v2
|
||||
```
|
||||
|
||||
À l'inverse, `dlmm`, `clmm`, `cpmm`, `stable_swap` et `weighted_swap` sont des types de mécanismes et peuvent être utilisés comme préfixes fonctionnels quand ils décrivent mieux la surface.
|
||||
|
||||
## Pourquoi supprimer `program_` ?
|
||||
|
||||
Le préfixe `program_` était un fallback technique. Il n'indique aucune fonction : ni AMM, ni router, ni lending, ni bridge, ni NFT. Il est donc insuffisant pour une nomenclature stable.
|
||||
|
||||
Nouvelle règle :
|
||||
|
||||
- aucune nouvelle surface canonique ne doit commencer par `program_` ;
|
||||
- une surface non classifiée utilise temporairement `unknown_*` ;
|
||||
- une surface `unknown_*` ne doit pas devenir une crate cible tant que sa fonction réelle n'est pas décidée ;
|
||||
- le statut attendu est `needs_classification` ou `current_alias_needs_classification`.
|
||||
|
||||
## Programmes core Solana/SPL
|
||||
|
||||
Ces identifiants sont détaillés dans `docs/CORE_PROGRAM_IDS.md`. Ils sont couverts par les décodeurs core/primitifs ou par des crates spécialisées SPL futures, pas par une crate DEX dédiée.
|
||||
|
||||
| Canonique | Program ID | Source | Type | Crate cible | Statut |
|
||||
|--------------------------------------------|------------------------------------------------|-----------------------------------|----------------------|-------------------------------------------|-------------------------------------|
|
||||
| `core_solana_address_lookup_table_v1` | `AddressLookupTab1e1111111111111111111111111` | `address_lookup_table` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_bpf_loader_deprecated_v1` | `BPFLoader1111111111111111111111111111111111` | `bpf_loader_deprecated` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_bpf_loader_v2` | `BPFLoader2111111111111111111111111111111111` | `bpf_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_bpf_loader_upgradeable_v1` | `BPFLoaderUpgradeab1e11111111111111111111111` | `bpf_loader_upgradeable` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_compute_budget_v1` | `ComputeBudget111111111111111111111111111111` | `compute_budget` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_config_v1` | `Config1111111111111111111111111111111111111` | `config` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_ed25519_v1` | `Ed25519SigVerify111111111111111111111111111` | `ed25519` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_feature_v1` | `Feature111111111111111111111111111111111111` | `feature` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_incinerator_v1` | `1nc1nerator11111111111111111111111111111111` | `incinerator` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_loader_v4` | `LoaderV411111111111111111111111111111111111` | `loader_v4` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_native_loader_v1` | `NativeLoader1111111111111111111111111111111` | `native_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_secp256k1_v1` | `KeccakSecp256k11111111111111111111111111111` | `secp256k1` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_secp256r1_v1` | `Secp256r1SigVerify1111111111111111111111111` | `secp256r1` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_stake_config_v1` | `StakeConfig11111111111111111111111111111111` | `stake_config` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_stake_v1` | `Stake11111111111111111111111111111111111111` | `stake` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_system_v1` | `11111111111111111111111111111111` | `system` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_sysvar_clock_v1` | `SysvarC1ock11111111111111111111111111111111` | `sysvar_clock` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_epoch_rewards_v1` | `SysvarEpochRewards1111111111111111111111111` | `sysvar_epoch_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_epoch_schedule_v1` | `SysvarEpochSchedu1e111111111111111111111111` | `sysvar_epoch_schedule` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_fees_v1` | `SysvarFees111111111111111111111111111111111` | `sysvar_fees` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_instructions_v1` | `Sysvar1nstructions1111111111111111111111111` | `sysvar_instructions` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_last_restart_slot_v1` | `SysvarLastRestartS1ot1111111111111111111111` | `sysvar_last_restart_slot` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_recent_blockhashes_v1` | `SysvarRecentB1ockHashes11111111111111111111` | `sysvar_recent_blockhashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_rent_v1` | `SysvarRent111111111111111111111111111111111` | `sysvar_rent` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_rewards_v1` | `SysvarRewards111111111111111111111111111111` | `sysvar_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_slot_hashes_v1` | `SysvarS1otHashes111111111111111111111111111` | `sysvar_slot_hashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_slot_history_v1` | `SysvarS1otHistory11111111111111111111111111` | `sysvar_slot_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_stake_history_v1` | `SysvarStakeHistory1111111111111111111111111` | `sysvar_stake_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_sysvar_v1` | `Sysvar1111111111111111111111111111111111111` | `sysvar` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` |
|
||||
| `core_solana_vote_v1` | `Vote111111111111111111111111111111111111111` | `vote` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_zk_elgamal_proof_v1` | `ZkE1Gama1Proof11111111111111111111111111111` | `zk_elgamal_proof` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_solana_zk_token_proof_v1` | `ZkTokenProof1111111111111111111111111111111` | `zk_token_proof` | `program` | `kb_decoder_solana_core` | `core_handled` |
|
||||
| `core_spl_associated_token_account_v1` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | `associated_token_account` | `program` | `kb_decoder_spl_associated_token_account` | `core_handled` |
|
||||
| `core_spl_account_compression_v1` | `cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK` | `spl_account_compression` | `program` | `kb_decoder_spl_account_compression` | `core_specialized_reserved_current` |
|
||||
| `core_spl_memo_v1` | `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` | `spl_memo_v1` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` |
|
||||
| `core_spl_memo_v3` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | `spl_memo_v3` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` |
|
||||
| `core_spl_memo_v4` | `Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH` | `spl_memo_v4` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` |
|
||||
| `core_spl_noop_v1` | `noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV` | `spl_noop` | `program` | `kb_decoder_spl_noop` | `core_specialized_reserved_current` |
|
||||
| `core_spl_single_pool_v1` | `SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE` | `spl_single_pool` | `program` | `kb_decoder_spl_single_pool` | `core_specialized_reserved_current` |
|
||||
| `core_spl_name_service_v1` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `spl_name_service` | `program` | `kb_decoder_metadata_spl_name_service` | `core_specialized_reserved_current` |
|
||||
| `core_spl_stake_pool_v1` | `SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy` | `stake_pool` | `program` | `kb_decoder_spl_stake_pool` | `core_specialized_reserved_current` |
|
||||
| `core_spl_token_2022_v2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | `token_2022` | `program` | `kb_decoder_spl_token_2022` | `core_handled` |
|
||||
| `core_spl_token_2022_elgamal_registry_v1` | `regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` | `spl_token_2022_elgamal_registry` | `program` | `kb_decoder_spl_token_2022` | `core_specialized_reserved_current` |
|
||||
| `core_spl_token_v1` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | `token` | `program` | `kb_decoder_spl_token` | `core_handled` |
|
||||
|
||||
|
||||
## DEX, AMM, CPMM, CLMM, DLMM, stable swap, weighted swap et orderbooks
|
||||
|
||||
| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut |
|
||||
|------------------------------------|------------------------------------------------|--------------------------|--------------------------|-------------------------------------|-----------------------------------------------|------------------------|
|
||||
| `amm_aldrin_v1` | `AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6` | `aldrin_amm_v1` | `aldrin_amm_v1` | `-` | `kb_decoder_amm_aldrin_v1` | `reserved_missing` |
|
||||
| `amm_aldrin_v2` | `CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4` | `aldrin_amm_v2` | `aldrin_amm_v2` | `-` | `kb_decoder_amm_aldrin_v2` | `reserved_missing` |
|
||||
| `amm_alphaq` | `ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA` | `alphaq` | `alphaq` | `kb_decoder_alphaq` | `kb_decoder_amm_alphaq` | `current_alias` |
|
||||
| `amm_believe` | `5qWya6UjwWnGVhdSBL3hyZ7B45jbk6Byt1hwd7ohEGXE` | `believe` | `believe` | `kb_decoder_believe` | `kb_decoder_amm_believe` | `current_alias` |
|
||||
| `amm_bonk_swap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | `bonk_swap` | `bonk_swap` | `kb_decoder_bonkswap` | `kb_decoder_amm_bonk_swap` | `current_alias` |
|
||||
| `amm_cropper_finance` | `CTMAxxk34HjKWxQ3QLZK1HpaLXmBveao3ESePXbiyfzh` | `cropper_finance` | `cropper_finance` | `-` | `kb_decoder_amm_cropper_finance` | `reserved_missing` |
|
||||
| `amm_dexlab_swap` | `DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N` | `dexlab_swap` | `dexlab_swap` | `-` | `kb_decoder_amm_dexlab_swap` | `reserved_missing` |
|
||||
| `amm_fluxbeam` | `FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X` | `fluxbeam` | `fluxbeam` | `kb_decoder_fluxbeam` | `kb_decoder_amm_fluxbeam` | `current_alias` |
|
||||
| `amm_fusion` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | `fusion_amm` | `fusion_amm` | `kb_decoder_fusion_amm` | `kb_decoder_amm_fusion` | `current_alias` |
|
||||
| `amm_goon_fi` | `goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j` | `goon_fi` | `goon_fi` | `kb_decoder_goonfi` | `kb_decoder_amm_goon_fi` | `current_alias` |
|
||||
| `amm_goon_fi_v2` | `goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE` | `goon_fi_v2` | `goon_fi_v2` | `-` | `kb_decoder_amm_goon_fi_v2` | `reserved_missing` |
|
||||
| `amm_goosefx_gamma` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | `goose_fx_gamma` | `goosefx_gamma` | `kb_decoder_goosefx_gamma` | `kb_decoder_amm_goosefx_gamma` | `current_alias` |
|
||||
| `amm_goosefx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | `goose_fx_v2` | `goosefx_v2` | `kb_decoder_goosefx_v2` | `kb_decoder_amm_goosefx_v2` | `current_alias` |
|
||||
| `amm_guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | `guac_swap` | `guac_swap` | `kb_decoder_guacswap` | `kb_decoder_amm_guac_swap` | `current_alias` |
|
||||
| `amm_heaven_dex` | `HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o` | `heaven_dex` | `heaven_dex` | `-` | `kb_decoder_amm_heaven_dex` | `reserved_missing` |
|
||||
| `amm_hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | `hylo_exchange` | `hylo_exchange` | `kb_decoder_hylo_exchange` | `kb_decoder_amm_hylo_exchange` | `current_alias` |
|
||||
| `amm_invariant_swap` | `HyaB3W9q6XdA5xwpU4XnSZV94htfmbmqJXZcEbRaJutt` | `invariant_swap` | `invariant_swap` | `-` | `kb_decoder_amm_invariant_swap` | `reserved_missing` |
|
||||
| `amm_lifinity_swap` | `EewxydAPCCVuNEyrVN68PuSYdQ7wKn27V9Gjeoi8dy3S` | `lifinity_swap` | `lifinity_swap` | `-` | `kb_decoder_amm_lifinity_swap` | `reserved_missing` |
|
||||
| `amm_lifinity_swap_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | `lifinity_swap_v2` | `lifinity_swap_v2` | `-` | `kb_decoder_amm_lifinity_swap_v2` | `reserved_missing` |
|
||||
| `amm_marcopolo_swap` | `9tKE7Mbmj4mxDjWatikzGAtkoWosiiZX9y6J4Hfm2R8H` | `marcopolo_swap` | `marcopolo_swap` | `-` | `kb_decoder_amm_marcopolo_swap` | `reserved_missing` |
|
||||
| `amm_metadao_futarchy_amm` | `FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq` | `metadao_futarchy_amm` | `metadao_futarchy_amm` | `-` | `kb_decoder_amm_metadao_futarchy_amm` | `reserved_missing` |
|
||||
| `amm_metadao_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | `metadao_amm_v0_5` | `metadao_amm_v0_5` | `kb_decoder_metadao_amm_v0_5` | `kb_decoder_amm_metadao_v0_5` | `current_alias` |
|
||||
| `amm_meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | `meteora_damm_v1` | `meteora_damm_v1` | `kb_decoder_meteora_damm_v1` | `kb_decoder_amm_meteora_damm_v1` | `current_alias` |
|
||||
| `amm_meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | `meteora_damm_v2` | `meteora_damm_v2` | `kb_decoder_meteora_damm_v2` | `kb_decoder_amm_meteora_damm_v2` | `current_alias` |
|
||||
| `amm_obric_v2` | `obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y` | `obric_v2` | `obric_v2` | `kb_decoder_obric_v2` | `kb_decoder_amm_obric_v2` | `current_alias` |
|
||||
| `amm_one_dex` | `DEXYosS6oEGvk8uCDayvwEZz4qEyDJRf9nFgYCaqPMTm` | `one_dex` | `one_dex` | `kb_decoder_one_dex` | `kb_decoder_amm_one_dex` | `current_alias` |
|
||||
| `amm_orca_token_swap` | `DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1` | `orca_token_swap` | `orca_token_swap` | `-` | `kb_decoder_amm_orca_token_swap` | `reserved_missing` |
|
||||
| `amm_orca_token_swap_v2` | `9W959DqEETiGZocYWCQPaJ6sBmUzgfxXfqGeTEdp3aQP` | `orca_token_swap_v2` | `orca_token_swap_v2` | `-` | `kb_decoder_amm_orca_token_swap_v2` | `reserved_missing` |
|
||||
| `amm_pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | `pancake_swap` | `pancake_swap` | `kb_decoder_pancake_swap` | `kb_decoder_amm_pancake_swap` | `current_alias` |
|
||||
| `amm_pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | `pump_swap` | `pump_swap` | `kb_decoder_pump_swap` | `kb_decoder_amm_pump_swap` | `current_alias` |
|
||||
| `amm_raydium_lp_amm` | `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` | `raydium_lp_amm` | `raydium_lp_amm` | `-` | `kb_decoder_amm_raydium_lp_amm` | `reserved_missing` |
|
||||
| `amm_raydium_lp_v2` | `RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr` | `raydium_lp_v2` | `raydium_lp_v2` | `-` | `kb_decoder_amm_raydium_lp_v2` | `reserved_missing` |
|
||||
| `amm_raydium_lp_v3` | `27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv` | `raydium_lp_v3` | `raydium_lp_v3` | `-` | `kb_decoder_amm_raydium_lp_v3` | `reserved_missing` |
|
||||
| `amm_raydium_lp_v4` | `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` | `raydium_lp_v4` | `raydium_lp_v4` | `-` | `kb_decoder_amm_raydium_lp_v4` | `reserved_missing` |
|
||||
| `amm_saros` | `SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr` | `saros_amm` | `saros_amm` | `-` | `kb_decoder_amm_saros` | `reserved_missing` |
|
||||
| `amm_solfi` | `SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe` | `solfi` | `solfi` | `kb_decoder_solfi` | `kb_decoder_amm_solfi` | `current_alias` |
|
||||
| `amm_solfi_v2` | `SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF` | `solfi_v2` | `solfi_v2` | `kb_decoder_solfi_v2` | `kb_decoder_amm_solfi_v2` | `current_alias` |
|
||||
| `amm_step_finance_swap` | `SSwpMgqNDsyV7mAgN9ady4bDVu5ySjmmXejXvy2vLt1` | `step_finance_swap` | `step_finance_swap` | `-` | `kb_decoder_amm_step_finance_swap` | `reserved_missing` |
|
||||
| `amm_stepn_dooar_swap` | `Dooar9JkhdZ7J3LHN3A7YCuoGRUggXhQaG4kijfLGU2j` | `stepn_dooar_swap` | `stepn_dooar_swap` | `-` | `kb_decoder_amm_stepn_dooar_swap` | `reserved_missing` |
|
||||
| `amm_swap_v1` | `SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8` | `swap` | `swap` | `-` | `kb_decoder_amm_swap_v1` | `reserved_missing` |
|
||||
| `amm_vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | `vertigo` | `vertigo` | `kb_decoder_vertigo` | `kb_decoder_amm_vertigo` | `current_alias` |
|
||||
| `amm_virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | `virtuals` | `virtuals` | `kb_decoder_virtuals` | `kb_decoder_amm_virtuals` | `current_alias` |
|
||||
| `amm_woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | `woofi` | `woofi` | `kb_decoder_woofi` | `kb_decoder_amm_woofi` | `current_alias` |
|
||||
| `amm_zero_fi` | `ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY` | `zero_fi` | `zero_fi` | `kb_decoder_zerofi` | `kb_decoder_amm_zero_fi` | `current_alias` |
|
||||
| `amm_zora` | `zoRabwLGd5zXaV7Gxacppw8tcceXEiTrSKyNLSaSTUc` | `zora` | `zora` | `kb_decoder_zora` | `kb_decoder_amm_zora` | `current_alias` |
|
||||
| `clmm_byreal` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | `byreal_clmm` | `byreal_clmm` | `kb_decoder_byreal_clmm` | `kb_decoder_clmm_byreal` | `current_alias` |
|
||||
| `clmm_crema_finance` | `CLMM9tUoggJu2wagPkkqs9eFG4BWhVBZWkP1qv3Sp7tR` | `crema_finance` | `crema_finance` | `-` | `kb_decoder_clmm_crema_finance` | `reserved_missing` |
|
||||
| `clmm_cropper_whirlpool` | `H8W3ctz92svYg6mkn1UtGfu2aQr2fnUFHM1RhScEtQDt` | `cropper_whirlpool` | `cropper_whirlpool` | `-` | `kb_decoder_clmm_cropper_whirlpool` | `reserved_missing` |
|
||||
| `clmm_orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool` | `orca_whirlpool` | `kb_decoder_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `duplicate_program_id` |
|
||||
| `clmm_orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool` | `orca_whirlpool` | `kb_decoder_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `duplicate_program_id` |
|
||||
| `clmm_raydium` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | `raydium_clmm` | `raydium_clmm` | `kb_decoder_raydium_clmm` | `kb_decoder_clmm_raydium` | `current_alias` |
|
||||
| `clmm_stabble` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | `stabble_clmm` | `stabble_clmm` | `kb_decoder_stabble_clmm` | `kb_decoder_clmm_stabble` | `current_alias` |
|
||||
| `cpmm_raydium` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | `raydium_cpmm` | `raydium_cpmm` | `kb_decoder_raydium_cpmm` | `kb_decoder_cpmm_raydium` | `current_alias` |
|
||||
| `dlmm_meteora` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | `meteora_dlmm` | `meteora_dlmm` | `kb_decoder_meteora_dlmm` | `kb_decoder_dlmm_meteora` | `current_alias` |
|
||||
| `orderbook_jupiter_limit_order` | `jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu` | `jupiter_limit_order` | `jupiter_limit_order` | `kb_decoder_jupiter_limit_order` | `kb_decoder_orderbook_jupiter_limit_order` | `current_alias` |
|
||||
| `orderbook_jupiter_limit_order_v2` | `j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X` | `jupiter_limit_order_v2` | `jupiter_limit_order_v2` | `kb_decoder_jupiter_limit_order_v2` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `current_alias` |
|
||||
| `orderbook_manifest_v1` | `MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms` | `manifest` | `manifest` | `-` | `kb_decoder_orderbook_manifest_v1` | `reserved_missing` |
|
||||
| `orderbook_openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | `openbook_v2` | `openbook_v2` | `kb_decoder_openbook_v2` | `kb_decoder_orderbook_openbook_v2` | `current_alias` |
|
||||
| `orderbook_phoenix_v1` | `PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY` | `phoenix` | `phoenix` | `-` | `kb_decoder_orderbook_phoenix_v1` | `reserved_missing` |
|
||||
| `stable_swap_jupiter_stable` | `JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr` | `jupiter_stable` | `jupiter_stable` | `kb_decoder_jupiter_stable` | `kb_decoder_stable_swap_jupiter_stable` | `current_alias` |
|
||||
| `stable_swap_mercurial` | `MERLuDFBMmsHnsBPZw2sDQZHvXFMwp8EdjudcU2HKky` | `mercurial_stable_swap` | `mercurial_stable_swap` | `-` | `kb_decoder_stable_swap_mercurial` | `reserved_missing` |
|
||||
| `stable_swap_saber` | `SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ` | `sabre_statble_swap` | `sabre_stable_swap` | `-` | `kb_decoder_stable_swap_saber` | `reserved_missing` |
|
||||
| `stable_swap_stabble` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | `stabble_stable_swap` | `stabble_stable_swap` | `kb_decoder_stabble_stable_swap` | `kb_decoder_stable_swap_stabble` | `current_alias` |
|
||||
| `weighted_swap_stabble` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | `stabble_weighted_swap` | `stabble_weighted_swap` | `kb_decoder_stabble_weighted_swap` | `kb_decoder_weighted_swap_stabble` | `current_alias` |
|
||||
|
||||
|
||||
## Routers et surfaces de routing
|
||||
|
||||
| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut |
|
||||
|--------------------------------|------------------------------------------------|-------------------------|-------------------------|----------------------------------|-------------------------------------------|--------------------|
|
||||
| `router_dflow_aggregator_v4` | `DF1ow4tspfHX9WJsAb9epbkA8hmpSEAtxXy1V27QBH` | `dflow_agregator_v4` | `dflow_aggregator_v4` | `kb_decoder_dflow_aggregator_v4` | `kb_decoder_router_dflow_aggregator_v4` | `current_alias` |
|
||||
| `router_jupiter_aggregator_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | `jupiter_agregator_v4` | `jupiter_aggregator_v4` | `-` | `kb_decoder_router_jupiter_aggregator_v4` | `reserved_missing` |
|
||||
| `router_jupiter_aggregator_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | `jupiter_agregator_v6` | `jupiter_aggregator_v6` | `-` | `kb_decoder_router_jupiter_aggregator_v6` | `reserved_missing` |
|
||||
| `router_jupiter_dca` | `DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M` | `jupiter_dca` | `jupiter_dca` | `kb_decoder_jupiter_dca` | `kb_decoder_router_jupiter_dca` | `current_alias` |
|
||||
| `router_okx_labs_v1` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | `okx_labs_v1` | `okx_labs_v1` | `kb_decoder_okx_lab_v1` | `kb_decoder_router_okx_labs_v1` | `current_alias` |
|
||||
| `router_okx_labs_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | `okx_labs_v2` | `okx_labs_v2` | `-` | `kb_decoder_router_okx_labs_v2` | `reserved_missing` |
|
||||
| `router_raydium_amm_routing` | `routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS` | `raydium_amm_routing` | `raydium_amm_routing` | `-` | `kb_decoder_router_raydium_amm_routing` | `reserved_missing` |
|
||||
| `router_sanctum` | `stkitrT1Uoy18Dk1fTrgPw8W6MVzoCfYoAFT4MLsmhq` | `sanctum_router` | `sanctum_router` | `-` | `kb_decoder_router_sanctum` | `reserved_missing` |
|
||||
| `router_tessera_v` | `TessVdML9pBGgG9yGks7o4HewRaXVAMuoVj4x83GLQH` | `tessera_v` | `tessera_v` | `-` | `kb_decoder_router_tessera_v` | `reserved_missing` |
|
||||
| `router_titan_exchange_router` | `T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT` | `titan_exchange_router` | `titan_exchange_router` | `-` | `kb_decoder_router_titan_exchange_router` | `reserved_missing` |
|
||||
|
||||
|
||||
## Launchpads et bonding curves
|
||||
|
||||
| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut |
|
||||
|--------------------------------------|------------------------------------------------|----------------------------|----------------------------|-----------------------------|-------------------------------------------------|--------------------|
|
||||
| `launchpad_boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | `boop_fun` | `boop_fun` | `kb_decoder_boop_fun` | `kb_decoder_launchpad_boop_fun` | `current_alias` |
|
||||
| `launchpad_letsbonk_fun` | `FfYek5vEz23cMkWsdJwG2oa6EphsvXSHrGpdALN4g6W1` | `letsbonk_fun` | `letsbonk_fun` | `-` | `kb_decoder_launchpad_letsbonk_fun` | `reserved_missing` |
|
||||
| `launchpad_metadao_ico` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | `metadao_ico` | `metadao_ico` | `-` | `kb_decoder_launchpad_metadao_ico` | `reserved_missing` |
|
||||
| `launchpad_meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | `meteora_dbc` | `meteora_dbc` | `kb_decoder_meteora_dbc` | `kb_decoder_launchpad_meteora_dbc` | `current_alias` |
|
||||
| `launchpad_moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | `moonit` | `moonit` | `kb_decoder_moonit` | `kb_decoder_launchpad_moonit` | `current_alias` |
|
||||
| `launchpad_moonshot_token_authority` | `7rtiKSUDLBm59b1SBmD9oajcP8xE64vAGSMbAN5CXy1q` | `moonshot_token_authority` | `moonshot_token_authority` | `-` | `kb_decoder_launchpad_moonshot_token_authority` | `reserved_missing` |
|
||||
| `launchpad_orca_wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | `orca_wavebreak` | `orca_wavebreak` | `kb_decoder_orca_wavebreak` | `kb_decoder_launchpad_orca_wavebreak` | `current_alias` |
|
||||
| `launchpad_printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | `printr` | `printr` | `kb_decoder_printr` | `kb_decoder_launchpad_printr` | `current_alias` |
|
||||
| `launchpad_pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | `pump_fun` | `pump_fun` | `kb_decoder_pump_fun` | `kb_decoder_launchpad_pump_fun` | `current_alias` |
|
||||
| `launchpad_pump_pumpup_ai` | `PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd` | `pumpup_ai` | `pumpup_ai` | `-` | `kb_decoder_launchpad_pump_pumpup_ai` | `reserved_missing` |
|
||||
| `launchpad_raydium_launchlab` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | `raydium_launchlab` | `raydium_launchlab` | `-` | `kb_decoder_launchpad_raydium_launchlab` | `reserved_missing` |
|
||||
|
||||
|
||||
## Lending, vault, staking, bridge, perps, oracle et treasury
|
||||
|
||||
| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut |
|
||||
|---------------------------------------------------|------------------------------------------------|-------------------------------------------|-------------------------------------------|---------------------------------|--------------------------------------------------------------|--------------------|
|
||||
| `bridge_circle_cctp_token_messenger_minter` | `CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3` | `cctp_token_messenger_minter` | `cctp_token_messenger_minter` | `-` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `reserved_missing` |
|
||||
| `bridge_circle_cctp_token_messenger_minter_v2` | `CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe` | `cctp_token_messenger_minter_v2` | `cctp_token_messenger_minter_v2` | `-` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `reserved_missing` |
|
||||
| `bridge_de_bridge_destination` | `dst5MGcFPoBeREFAA5E3tU5ij8m5uVYwkzkSAbsLbNo` | `de_bridge_destination` | `de_bridge_destination` | `-` | `kb_decoder_bridge_de_bridge_destination` | `reserved_missing` |
|
||||
| `bridge_de_bridge_source` | `src5qyZHqTqecJV4aY6Cb6zDZLMDzrDKKezs22MPHr4` | `de_bridge_source` | `de_bridge_source` | `-` | `kb_decoder_bridge_de_bridge_source` | `reserved_missing` |
|
||||
| `bridge_layer_zero_endpoint` | `76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6` | `layer_zero_endpoint` | `layer_zero_endpoint` | `-` | `kb_decoder_bridge_layer_zero_endpoint` | `reserved_missing` |
|
||||
| `bridge_layer_zero_executor` | `6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn` | `layer_zero_executor` | `layer_zero_executor` | `-` | `kb_decoder_bridge_layer_zero_executor` | `reserved_missing` |
|
||||
| `bridge_wormhole` | `wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb` | `whormhole_bridge` | `wormhole_bridge` | `-` | `kb_decoder_bridge_wormhole` | `reserved_missing` |
|
||||
| `lending_jupiter_lend_borrow` | `jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi` | `jupiter_lend_borrow` | `jupiter_lend_borrow` | `-` | `kb_decoder_lending_jupiter_lend_borrow` | `reserved_missing` |
|
||||
| `lending_jupiter_lend_earn` | `jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9` | `jupiter_lend_earn` | `jupiter_lend_earn` | `-` | `kb_decoder_lending_jupiter_lend_earn` | `reserved_missing` |
|
||||
| `lending_jupiter_lend_flash_loan` | `jupgfSgfuAXv4B6R2Uxu85Z1qdzgju79s6MfZekN6XS` | `jupiter_lend_flash_loan` | `jupiter_lend_flash_loan` | `-` | `kb_decoder_lending_jupiter_lend_flash_loan` | `reserved_missing` |
|
||||
| `lending_jupiter_lend_liquidity` | `jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC` | `jupiter_lend_liquidity` | `jupiter_lend_liquidity` | `-` | `kb_decoder_lending_jupiter_lend_liquidity` | `reserved_missing` |
|
||||
| `lending_kamino` | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` | `kamino_lending` | `kamino_lending` | `-` | `kb_decoder_lending_kamino` | `reserved_missing` |
|
||||
| `lending_marginfi` | `MFLQPPPPjNinkdKoy2odNFBhvpY43XtCDZjBwG2fwn5` | `marginfi` | `marginfi` | `-` | `kb_decoder_lending_marginfi` | `reserved_missing` |
|
||||
| `lending_marginfi_v2` | `MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA` | `marginfi_v2` | `marginfi_v2` | `-` | `kb_decoder_lending_marginfi_v2` | `reserved_missing` |
|
||||
| `lending_sharky_fi` | `SHARKobtfF1bHhxD2eqftjHBdVSCbKo9JtgK71FhELP` | `sharky_fi` | `sharky_fi` | `-` | `kb_decoder_lending_sharky_fi` | `reserved_missing` |
|
||||
| `lending_solend_protocol` | `So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo` | `solend_protocol` | `solend_protocol` | `-` | `kb_decoder_lending_solend_protocol` | `reserved_missing` |
|
||||
| `oracle_jupiter_prediction_market` | `3ZZuTbwC6aJbvteyVxXUS7gtFYdf7AuXeitx6VyvjvUp` | `jupiter_prediction_market` | `jupiter_prediction_market` | `-` | `kb_decoder_oracle_jupiter_prediction_market` | `reserved_missing` |
|
||||
| `perpetuals_drift_v2` | `dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH` | `drift_v2` | `drift_v2` | `kb_decoder_drift_v2` | `kb_decoder_perpetuals_drift_v2` | `current_alias` |
|
||||
| `perpetuals_jupiter` | `PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu` | `jupiter_perpetuals` | `jupiter_perpetuals` | `kb_decoder_jupiter_perpetuals` | `kb_decoder_perpetuals_jupiter` | `current_alias` |
|
||||
| `perpetuals_zeta` | `ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD` | `zeta` | `zeta` | `kb_decoder_zeta` | `kb_decoder_perpetuals_zeta` | `current_alias` |
|
||||
| `perpetuals_zeta_matching_engine` | `zDEXqXEG7gAyxb1Kg9mK5fPnUdENCGKzWrM21RMdWRq` | `zeta_matching_engine` | `zeta_matching_engine` | `-` | `kb_decoder_perpetuals_zeta_matching_engine` | `reserved_missing` |
|
||||
| `staking_jito_tip_distribution` | `4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7` | `jito_tip_distribution` | `jito_tip_distribution` | `-` | `kb_decoder_staking_jito_tip_distribution` | `reserved_missing` |
|
||||
| `staking_kamino_farm` | `FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr` | `kamino_farm` | `kamino_farm` | `-` | `kb_decoder_staking_kamino_farm` | `reserved_missing` |
|
||||
| `staking_marinade_finance` | `MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD` | `marinade_finance` | `marinade_finance` | `-` | `kb_decoder_staking_marinade_finance` | `reserved_missing` |
|
||||
| `staking_sanctum_multi_validator_spl_stake_pool` | `SPMBzsVUuoHA4Jm6KunbsotaahvVikZs1JyTW6iJvbn` | `sanctum_multi_validator_spl_stake_pool` | `sanctum_multi_validator_spl_stake_pool` | `-` | `kb_decoder_staking_sanctum_multi_validator_spl_stake_pool` | `reserved_missing` |
|
||||
| `staking_sanctum_single_validator_spl_stake_pool` | `SP12tWFxD9oJsVWNavTTBZvMbA6gkAmxtVgxdqvyvhY` | `sanctum_single_validator_spl_stake_pool` | `sanctum_single_validator_spl_stake_pool` | `-` | `kb_decoder_staking_sanctum_single_validator_spl_stake_pool` | `reserved_missing` |
|
||||
| `staking_solayer` | `sSo1iU21jBrU9VaJ8PJib1MtorefUV4fzC9GURa2KNn` | `solayer` | `solayer` | `kb_decoder_solayer` | `kb_decoder_staking_solayer` | `current_alias` |
|
||||
| `treasury_helium_treasury_management` | `treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5` | `helium_treasury_management` | `helium_treasury_management` | `-` | `kb_decoder_treasury_helium_treasury_management` | `reserved_missing` |
|
||||
| `vault_kamino` | `kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr` | `kamino_vault` | `kamino_vault` | `-` | `kb_decoder_vault_kamino` | `reserved_missing` |
|
||||
| `vault_kamino_v2` | `KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd` | `kamino_vault_v2` | `kamino_vault_v2` | `-` | `kb_decoder_vault_kamino_v2` | `reserved_missing` |
|
||||
| `vault_meteora` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | `meteora_vault` | `meteora_vault` | `kb_decoder_meteora_vault` | `kb_decoder_vault_meteora` | `current_alias` |
|
||||
| `vesting_streamflow` | `strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m` | `streamflow` | `streamflow` | `-` | `kb_decoder_vesting_streamflow` | `reserved_missing` |
|
||||
|
||||
|
||||
## NFT, metadata, admin, governance et locks
|
||||
|
||||
| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut |
|
||||
|------------------------------------|------------------------------------------------|---------------------------|---------------------------|------------------------|-----------------------------------------------|--------------------|
|
||||
| `lock_raydium_lp` | `LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE` | `raydium_lock_lp` | `raydium_lock_lp` | `-` | `kb_decoder_lock_raydium_lp` | `reserved_missing` |
|
||||
| `admin_jupiter_lock` | `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn` | `jupiter_lock` | `jupiter_lock` | `-` | `kb_decoder_admin_jupiter_lock` | `reserved_missing` |
|
||||
| `admin_pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | `pump_fees` | `pump_fees` | `kb_decoder_pump_fees` | `kb_decoder_admin_pump_fees` | `current_alias` |
|
||||
| `metadata_metaplex_token_metadata` | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | `metaplex_token_metadata` | `metaplex_token_metadata` | `-` | `kb_decoder_metadata_metaplex_token_metadata` | `reserved_missing` |
|
||||
| `metadata_name_service` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `name_service` | `name_service` | `-` | `kb_decoder_metadata_spl_name_service` | `reserved_missing` |
|
||||
| `nft_metaplex_bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | `bubblegum` | `bubblegum` | `-` | `kb_decoder_nft_metaplex_bubblegum` | `reserved_missing` |
|
||||
| `nft_metaplex_mpl_core` | `CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d` | `mpl_core` | `mpl_core` | `-` | `kb_decoder_nft_metaplex_mpl_core` | `reserved_missing` |
|
||||
|
||||
|
||||
## Surfaces à classifier
|
||||
|
||||
Ces entrées ont un `program_id`, mais leur fonction réelle n'est pas encore assez claire pour créer une crate canonique. Elles doivent être étudiées avant renommage.
|
||||
|
||||
| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut |
|
||||
|---------------------------------------|------------------------------------------------|-------------------------------|-------------------------------|----------------------------------|-------------|--------------------------------------|
|
||||
| `unknown_aquifer` | `AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45` | `aquifer` | `aquifer` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_axiom_trade` | `FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9` | `axiom_trade` | `axiom_trade` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_bison_fi` | `BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi` | `bison_fi` | `bison_fi` | `kb_decoder_bisonfi` | `-` | `current_alias_needs_classification` |
|
||||
| `unknown_carrot_defi` | `CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s` | `carrot_defi` | `carrot_defi` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_clone` | `C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr` | `clone` | `clone` | `kb_decoder_clone` | `-` | `current_alias_needs_classification` |
|
||||
| `unknown_gavel` | `srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi` | `gavel` | `gavel` | `kb_decoder_gavel` | `-` | `current_alias_needs_classification` |
|
||||
| `unknown_hawk_fi` | `FqGg2Y1FNxMiGd51Q6UETixQWkF5fB92MysbYogRJb3P` | `hawk_fi` | `hawk_fi` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_humidi_fi` | `9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp` | `humidi_fi` | `humidi_fi` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_hylo_stability_pool` | `HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ` | `hylo_stability_pool` | `hylo_stability_pool` | `kb_decoder_hylo_stability_pool` | `-` | `current_alias_needs_classification` |
|
||||
| `unknown_jup_studio_authority` | `8rE9CtCjwhSmbwL5fbJBtRFsS3ohfMcDFeTTC7t4ciUA` | `jup_studio_authority` | `jup_studio_authority` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_jupiter_apepro_smart_wallet` | `JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw` | `jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_kamino` | `6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc` | `kamino` | `kamino` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | `metadao_bid_wall` | `metadao_bid_wall` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_numeraire` | `NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P` | `numeraire` | `numeraire` | `kb_decoder_numeraire` | `-` | `current_alias_needs_classification` |
|
||||
| `unknown_ondo_global_markets` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | `ondo_global_markets` | `ondo_global_markets` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_ore_v3` | `oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv` | `ore_v3` | `ore_v3` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_penguin_finance` | `PSwapMdSai8tjrEXcxFeQth87xC4rRsa4VA5mhGhXkP` | `pinguin_finance` | `penguin_finance` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_sabre_decimal_wrapper` | `DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB` | `sabre_decimal_wrapper` | `sabre_decimal_wrapper` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_sanctum_s_controller` | `5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx` | `sanctum_s_controller` | `sanctum_s_controller` | `-` | `-` | `needs_classification` |
|
||||
| `unknown_scorch` | `SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn` | `scorch` | `scorch` | `kb_decoder_scorch` | `-` | `current_alias_needs_classification` |
|
||||
| `unknown_swig` | `swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB` | `swig` | `swig` | `-` | `-` | `needs_classification` |
|
||||
|
||||
|
||||
## Autres surfaces
|
||||
|
||||
| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut |
|
||||
|-----------|------------|--------|-----------|----------------|-------------|--------|
|
||||
|
||||
|
||||
## Doublons et aliases à surveiller
|
||||
|
||||
- Les noms `okx_v1`, `okx_v2`, `onchain_labs_dex_v1`, `onchain_labs_dex_v2` ne doivent pas être retenus comme noms canoniques tant que les `program_id` prouvés sont `okx_labs_v1` et `okx_labs_v2`.
|
||||
- `goosefx_gamma` et `goosefx_v2` ne doivent pas être fusionnés tant que les `program_id` restent distincts.
|
||||
- `raydium_pool_v4` doit être traité comme alias de documentation de `amm_raydium_lp_v4` si le `program_id` est `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`.
|
||||
- `meteora_pools_amm` est un alias documentaire probable de `amm_meteora_damm_v1` si le `program_id` est `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB`.
|
||||
|
||||
## Règle de migration
|
||||
|
||||
Aucun renommage physique massif ne doit être fait dans un delta général. Pour chaque surface :
|
||||
|
||||
1. confirmer le `program_id` ;
|
||||
2. confirmer la fonction réelle ;
|
||||
3. choisir le `canonical_surface_code` ;
|
||||
4. créer ou renommer une seule crate ;
|
||||
5. mettre à jour `Cargo.toml` ;
|
||||
6. exécuter `cargo build` ;
|
||||
7. mettre à jour le registre ;
|
||||
8. supprimer l'ancien alias seulement après validation.
|
||||
|
||||
|
||||
## Comptes qui ne sont pas des programmes
|
||||
|
||||
Les comptes qui ne sont pas prouvés comme programmes exécutables doivent rester hors du registre de surfaces. Le cas `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` est documenté dans `docs/ACCOUNT_ONLY_CANDIDATES.md`.
|
||||
|
||||
## Index hexadécimal optionnel
|
||||
|
||||
Un préfixe `00` à `ff` ne doit pas remplacer le nom canonique. Il peut être ajouté comme champ `registry_code` dans le registre, mais les crates doivent rester descriptives. Voir `docs/PROGRAM_CODE_INDEX.md`.
|
||||
|
||||
## Bags Fee Share
|
||||
|
||||
| Canonique | Program id | Source | Fonction | Crate cible | Statut |
|
||||
|--------------------------|------------------------------------------------|-------------------|----------|-------------------------------------|---------------------|
|
||||
| `fees_bags_fee_share_v1` | `FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi` | Bags Fee Share V1 | `fees` | `kb_decoder_fees_bags_fee_share_v1` | `canonical_current` |
|
||||
| `fees_bags_fee_share_v2` | `FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK` | Bags Fee Share V2 | `fees` | `kb_decoder_fees_bags_fee_share_v2` | `canonical_current` |
|
||||
@@ -0,0 +1,20 @@
|
||||
<!-- file: docs/PROJECT_OBJECTIVES.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Objectifs du projet
|
||||
|
||||
## Objectif court terme
|
||||
|
||||
L'objectif court terme est de construire un système de trading assisté sur Solana. Le programme doit aider l'opérateur à observer les créations de tokens, créations de pools, migrations, changements de liquidité, swaps, claims de frais et opérations administratives qui peuvent influencer une décision d'achat ou de revente.
|
||||
|
||||
Le système ne doit pas commencer par une stratégie autonome. Il doit d'abord fournir une observation fiable, des démos live, des garde-fous d'exécution et une validation post-transaction.
|
||||
|
||||
## Objectif long terme
|
||||
|
||||
L'objectif long terme est de construire une bibliothèque d'analyse Solana capable de rivaliser progressivement avec des outils d'exploration et d'analyse comme les indexeurs publics : parser la blockchain, classifier les programmes, décoder les instructions, matérialiser les événements métier, agréger les états et rendre les surfaces inconnues auditables.
|
||||
|
||||
Ce second objectif impose de conserver une architecture large : raw immuable, registres de programmes, décodeurs par surface, matérialisateurs transversaux, exécuteurs séparés, tests anti-régression et documentation de classification.
|
||||
|
||||
## Conséquence sur le squelette
|
||||
|
||||
Le squelette doit rester plus large que le besoin immédiat de trading. En revanche, le développement actif doit rester priorisé : logging, configuration, stockage, RPC, wallet, démos live, puis surfaces prioritaires pour les listeners et l'exécution contrôlée.
|
||||
@@ -0,0 +1,115 @@
|
||||
<!-- file: docs/RAW_STORAGE_LIFECYCLE.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Cycle de vie du stockage transactionnel Solana
|
||||
|
||||
## Principe général
|
||||
|
||||
Le stockage rejouable repose désormais sur une transaction Solana canonique unique par signature. Les formats d’origine JSON-RPC, WebSocket Helius ou Yellowstone Protobuf sont des formats de transport, pas des modèles de stockage métier.
|
||||
|
||||
Le cycle est :
|
||||
|
||||
```text
|
||||
acquisition
|
||||
-> normalisation canonique
|
||||
-> persistance raw canonique
|
||||
-> extraction core
|
||||
-> décodage
|
||||
-> matérialisation
|
||||
-> compaction / archivage / purge éventuelle
|
||||
```
|
||||
|
||||
## Transaction canonique
|
||||
|
||||
`kb_sol_raw_transactions` conserve suffisamment d’information pour :
|
||||
|
||||
- auditer une transaction ;
|
||||
- reconstruire les tables `core` ;
|
||||
- rejouer de nouvelles versions de décodeurs ;
|
||||
- rejouer de nouvelles versions de matérialiseurs ;
|
||||
- comparer plusieurs sources sans dépendre de leur format.
|
||||
|
||||
La ligne est unique par signature. `canonical_format_version` permet de faire évoluer le contrat sans ambiguïté.
|
||||
|
||||
## Observations d’acquisition
|
||||
|
||||
`kb_sol_obs_transaction_observations` conserve les preuves techniques d’acquisition :
|
||||
|
||||
- fournisseur et endpoint ;
|
||||
- protocole et méthode ;
|
||||
- origine live/backfill/replay/réparation ;
|
||||
- commitment ;
|
||||
- session et filtre ;
|
||||
- timestamps de détection, réception, normalisation et persistance ;
|
||||
- taille du message ;
|
||||
- statut et erreur éventuelle.
|
||||
|
||||
Cette table ne conserve pas le payload complet. Elle peut contenir plusieurs lignes pour une même signature.
|
||||
|
||||
## Ancienne table WebSocket
|
||||
|
||||
`kb_sol_raw_ws_notifications` est une table historique de `0.2.3`, supprimée pendant la transition validée `0.3.1`. Elle n’existe plus dans la baseline SQL active.
|
||||
|
||||
Les nouvelles notifications WebSocket ne doivent pas être stockées intégralement par défaut. Une notification de logs peut rester temporairement dans une queue mémoire jusqu’à l’hydratation de la transaction.
|
||||
|
||||
## États de rétention
|
||||
|
||||
Les états de rétention de `kb_sol_raw_transactions` sont :
|
||||
|
||||
```text
|
||||
full
|
||||
compacted
|
||||
archived
|
||||
purged
|
||||
```
|
||||
|
||||
### `full`
|
||||
|
||||
Le document canonique complet est présent dans PostgreSQL.
|
||||
|
||||
### `compacted`
|
||||
|
||||
Une représentation réduite ou un stockage externe permet encore l’audit minimal, mais le payload canonique complet n’est plus dans la ligne chaude.
|
||||
|
||||
### `archived`
|
||||
|
||||
Le payload a été déplacé vers un stockage d’archive durable.
|
||||
|
||||
### `purged`
|
||||
|
||||
Le payload n’est plus disponible. Le hash canonique, la signature, le slot et les données dérivées doivent rester suffisants pour les diagnostics autorisés.
|
||||
|
||||
## États de traitement
|
||||
|
||||
```text
|
||||
received
|
||||
core_extracted
|
||||
decoded
|
||||
materialized
|
||||
failed
|
||||
```
|
||||
|
||||
L’état décrit la progression de la transaction canonique, pas celle de chaque observation de source.
|
||||
|
||||
## Conditions avant compaction ou purge
|
||||
|
||||
Aucune purge destructive ne doit être activée avant validation de :
|
||||
|
||||
- l’extraction core idempotente ;
|
||||
- la couverture des décodeurs prioritaires ;
|
||||
- la reconstruction depuis archive ;
|
||||
- la stabilité du hash canonique ;
|
||||
- la conservation du ledger de traitement ;
|
||||
- la stratégie de sauvegarde PostgreSQL.
|
||||
|
||||
## Payloads fournisseur de diagnostic
|
||||
|
||||
Les payloads complets spécifiques à un fournisseur peuvent être exportés temporairement pour une session de test. Ils ne doivent pas devenir une dépendance du replay métier.
|
||||
|
||||
Un export de diagnostic doit être :
|
||||
|
||||
- opt-in ;
|
||||
- borné en durée et en taille ;
|
||||
- associé à une session ;
|
||||
- supprimable indépendamment de PostgreSQL.
|
||||
|
||||
147
migration/khadhroony-bot2-reference/docs/RAW_STORE.md
Normal file
147
migration/khadhroony-bot2-reference/docs/RAW_STORE.md
Normal file
@@ -0,0 +1,147 @@
|
||||
<!-- file: docs/RAW_STORE.md -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Store transactionnel Solana canonique
|
||||
|
||||
## Historique
|
||||
|
||||
`0.2.3` a créé deux tables minimales :
|
||||
|
||||
```text
|
||||
kb_sol_raw_rpc_transactions
|
||||
kb_sol_raw_ws_notifications
|
||||
```
|
||||
|
||||
Ces tables restent un jalon historique validé dans `CHANGELOG.md`. La transition a été appliquée et contrôlée sur PostgreSQL réel pendant `0.3.1`.
|
||||
|
||||
Le modèle actif est :
|
||||
|
||||
```text
|
||||
kb_sol_raw_transactions
|
||||
kb_sol_obs_transaction_observations
|
||||
```
|
||||
|
||||
## Objectif cible
|
||||
|
||||
Le store principal conserve une transaction Solana source-indépendante par signature. Les sources HTTP, WebSocket Helius et Yellowstone gRPC ne doivent pas produire plusieurs copies complètes du même contenu.
|
||||
|
||||
```text
|
||||
source provider
|
||||
-> normalisation canonique
|
||||
-> transaction unique
|
||||
-> observations multiples
|
||||
```
|
||||
|
||||
## Table `kb_sol_raw_transactions`
|
||||
|
||||
Rôle : stocker la représentation canonique rejouable de la transaction et de ses metadata.
|
||||
|
||||
Colonnes principales visées :
|
||||
|
||||
| Colonne | Rôle |
|
||||
|----------------------------|-----------------------------------------|
|
||||
| `id` | clé primaire technique |
|
||||
| `signature` | signature Solana unique |
|
||||
| `slot` | slot de la transaction |
|
||||
| `canonical_format_version` | version du contrat canonique |
|
||||
| `canonical_json` | transaction et metadata normalisées |
|
||||
| `canonical_json_hash` | hash déterministe du document canonique |
|
||||
| `retention_state` | état de rétention |
|
||||
| `processing_state` | état d’extraction et de traitement |
|
||||
| `lifecycle_reason` | raison technique optionnelle |
|
||||
| `created_at` | première insertion |
|
||||
| `updated_at` | dernière évolution autorisée |
|
||||
|
||||
Le contenu canonique ne contient pas de nom de fournisseur, endpoint, subscription id ou format de transport.
|
||||
|
||||
`kb_model::CanonicalTransaction` fournit la sérialisation déterministe et le hash SHA-256. `kb_store_core::RawTransactionInsert::from_canonical` construit le DTO de persistance avec la signature primaire, le slot, `canonical_format_version`, `canonical_json` et `canonical_json_hash`. Le transport RPC ne dépend donc pas du backend PostgreSQL.
|
||||
|
||||
## Table `kb_sol_obs_transaction_observations`
|
||||
|
||||
Rôle : enregistrer chaque détection ou acquisition d’une signature sans recopier le payload complet.
|
||||
|
||||
Colonnes principales visées :
|
||||
|
||||
| Colonne | Rôle |
|
||||
|-----------------------|-------------------------------------------------------|
|
||||
| `id` | clé primaire technique |
|
||||
| `observation_key` | clé idempotente de l’observation |
|
||||
| `raw_transaction_id` | lien optionnel vers `kb_sol_raw_transactions` |
|
||||
| `signature` | signature détectée ou reçue |
|
||||
| `slot` | slot quand connu |
|
||||
| `provider` | fournisseur |
|
||||
| `endpoint_code` | endpoint de configuration |
|
||||
| `protocol` | protocole de transport |
|
||||
| `acquisition_method` | méthode ou type de stream |
|
||||
| `origin` | `live`, `backfill`, `replay`, `repair` ou `migration` |
|
||||
| `commitment` | commitment demandé ou reçu |
|
||||
| `capture_session_id` | session de mesure ou d’ingestion |
|
||||
| `filter_code` | filtre logique utilisé |
|
||||
| `detected_at` | premier signal reçu |
|
||||
| `received_at` | transaction complète reçue |
|
||||
| `normalized_at` | fin de normalisation |
|
||||
| `persisted_at` | fin de persistance |
|
||||
| `payload_size_bytes` | taille du message source |
|
||||
| `source_payload_hash` | hash optionnel du message source |
|
||||
| `status` | résultat technique |
|
||||
| `error_code` | erreur normalisée optionnelle |
|
||||
| `error_message` | message de diagnostic optionnel |
|
||||
|
||||
Cette table ne possède pas de `raw_json`, de `canonical_json` ni de payload Protobuf complet.
|
||||
|
||||
## Notifications WebSocket
|
||||
|
||||
`kb_sol_raw_ws_notifications` n’est plus conservée comme cible durable.
|
||||
|
||||
Pour `logsSubscribe` :
|
||||
|
||||
1. recevoir la signature et le slot ;
|
||||
2. mémoriser temporairement le timestamp de détection ;
|
||||
3. hydrater par `getTransaction` si nécessaire ;
|
||||
4. écrire la transaction canonique ;
|
||||
5. écrire une observation avec `detected_at`, `received_at` et le statut d’hydratation.
|
||||
|
||||
Les payloads complets de notifications peuvent être exportés temporairement pour un benchmark borné, mais ils ne doivent pas être dupliqués en PostgreSQL.
|
||||
|
||||
## Fusion par signature
|
||||
|
||||
La signature est la clé d’unicité de la transaction canonique.
|
||||
|
||||
- hash identique : conserver la ligne et ajouter une observation ;
|
||||
- metadata plus complètes : appliquer un enrichissement déterministe ;
|
||||
- différence incompatible : enregistrer un conflit et ne pas écraser silencieusement ;
|
||||
- transaction absente ou erreur provider : écrire uniquement l’observation technique.
|
||||
|
||||
## Cycle de vie
|
||||
|
||||
Les états de rétention de la transaction canonique restent :
|
||||
|
||||
```text
|
||||
full
|
||||
compacted
|
||||
archived
|
||||
purged
|
||||
```
|
||||
|
||||
Les états de traitement restent :
|
||||
|
||||
```text
|
||||
received
|
||||
core_extracted
|
||||
decoded
|
||||
materialized
|
||||
failed
|
||||
```
|
||||
|
||||
Les observations sont append-only et suivent une rétention technique distincte.
|
||||
|
||||
## Baseline SQL active
|
||||
|
||||
Après validation de la transition historique, le dossier `kb_store_pg/migrations/` a été consolidé :
|
||||
|
||||
```text
|
||||
0001_canonical_transaction_store.sql
|
||||
0002_core_store.sql
|
||||
```
|
||||
|
||||
La baseline ne contient que les tables et colonnes actives. Les anciens noms RPC/WS ne sont plus recréés. Le DDL runtime conserve temporairement un chemin de compatibilité idempotent pour les workspaces encore issus de `pre.001`.
|
||||
80
migration/khadhroony-bot2-reference/docs/REPLAY_PIPELINE.md
Normal file
80
migration/khadhroony-bot2-reference/docs/REPLAY_PIPELINE.md
Normal file
@@ -0,0 +1,80 @@
|
||||
<!-- file: docs/REPLAY_PIPELINE.md -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Pipeline de replay
|
||||
|
||||
Le replay ne doit pas être global par défaut. Chaque campagne reçoit un `campaign_id` et cible un ensemble explicite de processors, une version et un périmètre borné d’inputs.
|
||||
|
||||
## Étapes
|
||||
|
||||
```text
|
||||
ingest -> extract -> observe -> decode -> materialize -> aggregate -> validate
|
||||
```
|
||||
|
||||
Chaque étape doit être rejouable séparément avec son propre ledger versionné.
|
||||
|
||||
## Sélection de décodage
|
||||
|
||||
La sélection opérateur peut utiliser :
|
||||
|
||||
- des signatures ;
|
||||
- une plage de slots ;
|
||||
- des `program_id` ;
|
||||
- des instruction paths ;
|
||||
- des états lifecycle ;
|
||||
- une limite stricte.
|
||||
|
||||
Le pipeline calcule ensuite `effective_program_ids` comme l’union des programmes déclarés par les décodeurs activés. Sans filtre program ID explicite, cette union est appliquée automatiquement au store. Avec un filtre explicite, chaque valeur doit être supportée par au moins un décodeur activé, sinon la campagne est refusée avant la requête SQL.
|
||||
|
||||
Cette règle évite qu’un classifieur natif sélectionne des instructions SPL, Jupiter, Pump ou d’autres programmes non compatibles. Elle empêche également la resélection infinie d’un lot entièrement `unmatched` uniquement parce que les processors choisis ne couvrent pas ses programmes.
|
||||
|
||||
## Force replay
|
||||
|
||||
Le force replay contourne le skip processor/version/input/hash, mais reste borné par une liste de signatures explicites ou par une autorisation opérateur « toutes les signatures » combinée aux autres filtres et à la limite. Une requête sans l’un de ces deux scopes est refusée.
|
||||
|
||||
Lorsqu’une campagne contient des signatures explicites, `force_replay=true` retire le filtre lifecycle pour ces signatures. Les inputs déjà terminaux peuvent alors être réellement rejoués. Le mode explicite « toutes les signatures » retire également ce filtre, mais conserve obligatoirement le scope des programmes compatibles, les slots et instruction paths éventuels ainsi que la limite. Sans signatures ni autorisation « toutes les signatures », la requête est refusée.
|
||||
|
||||
Le remplacement ne touche que les sorties du processor, de la version et de l’input ciblés. Les autres processors et les autres versions restent intacts.
|
||||
|
||||
## Skip
|
||||
|
||||
Sans force replay, un input est skippé uniquement lorsque le ledger contient un succès ayant exactement :
|
||||
|
||||
```text
|
||||
stage
|
||||
processor_name
|
||||
processor_version
|
||||
input_key
|
||||
input_hash
|
||||
```
|
||||
|
||||
Une campagne de validation doit pouvoir montrer successivement :
|
||||
|
||||
```text
|
||||
premier passage -> decoded/ignored/unsupported
|
||||
second passage -> skipped
|
||||
force replay -> exécution sans skip
|
||||
passage suivant -> skipped
|
||||
```
|
||||
|
||||
## Unmatched
|
||||
|
||||
`unmatched` signifie qu’aucun décodeur compatible n’a accepté l’input après le filtre exact de programme. L’instruction n’est pas marquée globalement comme définitivement ignorée, car un futur processor peut la supporter. Les décodeurs doivent classer comme `unsupported` les instructions inconnues de leurs propres programmes afin de produire une couverture exploitable.
|
||||
|
||||
## Traçabilité
|
||||
|
||||
Les logs de campagne conservent :
|
||||
|
||||
```text
|
||||
campaign_id
|
||||
signature_count
|
||||
signature_sample
|
||||
decoder_names
|
||||
requested_program_ids
|
||||
effective_program_ids
|
||||
requested_processing_states
|
||||
effective_processing_states
|
||||
force_replay
|
||||
```
|
||||
|
||||
Les logs par input conservent ensuite la signature exacte, le path, le programme, le processor, le hash et la décision terminale.
|
||||
@@ -0,0 +1,77 @@
|
||||
<!-- file: docs/RPC_ENDPOINT_ROLES.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Rôles des endpoints RPC et streams
|
||||
|
||||
Les rôles servent à sélectionner un endpoint selon l’opération, le provider, le protocole et les limites locales.
|
||||
|
||||
## Rôles HTTP actifs
|
||||
|
||||
| Rôle | Usage |
|
||||
|---------------------|-----------------------------------------------------------------|
|
||||
| `http_queries` | lectures courantes |
|
||||
| `history_backfill` | `getSignaturesForAddress`, `getTransaction` et backfills ciblés |
|
||||
| `http_transactions` | simulation, envoi et confirmation |
|
||||
| `http_fallback` | endpoint de secours |
|
||||
| `http_heavy` | appels lourds |
|
||||
|
||||
`0.3.x` utilise principalement `history_backfill` avec les comptes gratuits.
|
||||
|
||||
## Rôles WebSocket standard existants
|
||||
|
||||
| Rôle | Usage |
|
||||
|-------------------------|-------------------------------|
|
||||
| `slot_notifications` | progression et santé du flux |
|
||||
| `logs_subscribe` | logs ciblés |
|
||||
| `program_subscribe` | comptes détenus par programme |
|
||||
| `account_subscribe` | comptes précis |
|
||||
| `program_logs` | compatibilité historique |
|
||||
| `account_notifications` | compatibilité historique |
|
||||
|
||||
## Rôles futurs `0.10.x`
|
||||
|
||||
| Rôle | Usage |
|
||||
|----------------------------------|-----------------------------------------------|
|
||||
| `helius_transaction_stream` | extension Helius `transactionSubscribe` |
|
||||
| `yellowstone_transaction_stream` | filtre transactions Yellowstone gRPC |
|
||||
| `logs_all_probe` | comparaison temporaire `logsSubscribe("all")` |
|
||||
| `realtime_transaction_hydration` | hydratation HTTP et réparation |
|
||||
|
||||
Les transports Helius et Yellowstone restent implémentés dans `kb_rpc`. Les rôles ne créent pas de dépendance provider dans les décodeurs.
|
||||
|
||||
## Request kinds actifs pour le backfill
|
||||
|
||||
```text
|
||||
get_signatures_for_address
|
||||
get_transaction
|
||||
get_signature_statuses
|
||||
get_slot
|
||||
get_version
|
||||
```
|
||||
|
||||
## Request kinds futurs
|
||||
|
||||
```text
|
||||
transaction_subscribe
|
||||
transaction_unsubscribe
|
||||
yellowstone_transaction_subscribe
|
||||
logs_subscribe_all
|
||||
logs_unsubscribe
|
||||
```
|
||||
|
||||
## Limites par rôle
|
||||
|
||||
```text
|
||||
requests_per_second
|
||||
burst_capacity
|
||||
max_concurrent_requests
|
||||
max_subscriptions
|
||||
pause_after_rate_limit_ms
|
||||
```
|
||||
|
||||
Pour les streams gRPC, la configuration future ajoutera les limites de streams, filtres, adresses et taille de queue sans casser les rôles HTTP/WS existants.
|
||||
|
||||
## Preuve runtime
|
||||
|
||||
Un rôle ou un plan configuré ne garantit pas la capacité distante. La réponse du provider reste la preuve runtime. Les refus de plan, filtres invalides et limitations doivent être normalisés comme erreurs de `kb_rpc`.
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
<!-- file: docs/RUST_WORKSPACE_RULE_AUDIT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Audit des règles Rust du workspace
|
||||
|
||||
Violations détectées : **616**.
|
||||
|
||||
## Synthèse
|
||||
|
||||
| Code | Nombre |
|
||||
|------------|-------:|
|
||||
| `RUST010` | 156 |
|
||||
| `RUST011` | 16 |
|
||||
| `RUST013` | 19 |
|
||||
| `RUST021` | 1 |
|
||||
| `TRACE002` | 32 |
|
||||
| `TRACE003` | 202 |
|
||||
| `TRACE004` | 190 |
|
||||
|
||||
## Premiers écarts
|
||||
|
||||
- `RUST010` `kb_app_demo/src/demo_backfill.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_backfill.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_backfill.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_config.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_config.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_execution_spl.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_http.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_http.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_spl_ata.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_spl_token.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_spl_token_2022.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_sql_common.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_sql_common.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_sql_diag.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_sql_pg_core.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_sql_pg_raw.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_sql_replay_candidates.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_ws.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_ws.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_ws.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/demo_ws.rs:993` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/frontend_log.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/lib.rs:13` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/lib.rs:14` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/main.rs:12` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/splash.rs:9` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_app_demo/src/splash.rs:10` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_config/src/settings.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_config/src/settings.rs:1223` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_api/src/contracts.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/address_lookup_table.rs:326` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/compute_budget.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/compute_budget.rs:673` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/config.rs:322` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/feature.rs:167` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/loaders.rs:992` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/payload.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/payload.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/slashing.rs:461` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/stake.rs:805` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/system.rs:438` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/vote.rs:707` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/zk_elgamal.rs:522` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_solana_core/src/zk_token_proof.rs:693` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_associated_token_account/src/decoder.rs:335` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_associated_token_account/src/instruction.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_associated_token_account/src/instruction.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_elgamal_registry/src/decoder.rs:95` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_elgamal_registry/src/decoder.rs:162` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_elgamal_registry/src/registry.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_elgamal_registry/src/registry.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_elgamal_registry/src/state.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_elgamal_registry/src/state.rs:49` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_memo/src/decoder.rs:160` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_memo/src/memo.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_memo/src/memo.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token/src/token.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token/src/token.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token_2022/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token_2022/src/state.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token_2022/src/state.rs:916` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token_2022/src/token.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_decoder_spl_token_2022/src/token.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_api/src/execution.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_api/src/executor.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_safety/src/safety.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_solana/src/nonce.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_solana/src/transaction.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_solana/src/transaction.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_solana/src/transaction.rs:654` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_execution_solana/src/transaction.rs:655` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/address_lookup_table.rs:321` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/builder.rs:1374` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/native_admin.rs:381` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/precompiles.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/slashing.rs:346` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/stake.rs:1497` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/validation.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/validation.rs:108` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/vote.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_solana_core/src/vote.rs:1846` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_associated_token_account/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_elgamal_registry/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_elgamal_registry/src/builder.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_elgamal_registry/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_memo/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_memo/src/builder.rs:194` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_memo/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_token/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_token/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_token_2022/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_token_2022/src/confidential.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_token_2022/src/confidential.rs:425` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_executor_spl_token_2022/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_logging/src/tracing_runtime.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_logging/src/tracing_runtime.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_logging/src/tracing_runtime.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_logging/src/tracing_runtime.rs:676` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_logging/src/tracing_runtime.rs:677` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_model/src/canonical_transaction.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_model/src/canonical_transaction.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_model/src/decoded.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_model/src/materialized.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_model/src/nomenclature.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_model/src/observation.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_model/src/solana.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/backfill.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/backfill.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/backfill.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/core_extraction.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/core_extraction.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/core_extraction.rs:8` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/decode_replay.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/decode_replay.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/solana_elgamal_registry_stateful.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/solana_elgamal_registry_stateful.rs:227` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/solana_stateful.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/solana_token_stateful.rs:392` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_pipeline/src/solana_token_stateful.rs:470` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/execution_rpc.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/execution_rpc.rs:2251` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/get_transaction.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/standard_http.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/ws_client.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/ws_client.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/ws_session.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/ws_session.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/ws_session.rs:1219` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_rpc/src/ws_session.rs:1220` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_store_pg/src/queries/core_extraction_queries.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_store_pg/src/queries/core_queries.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_store_pg/src/queries/decode_pipeline_queries.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_store_pg/src/queries/table_diagnostics_queries.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_wallet/src/wallet.rs:6` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_wallet/src/wallet.rs:7` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_wallet/src/wallet.rs:261` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_wallet/src/wallet.rs:294` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_wallet/src/wallet.rs:299` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_wallet/src/wallet.rs:411` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST010` `kb_wallet/src/wallet.rs:524` — ordinary `use` requires a trait-import justification marker
|
||||
- `RUST011` `kb_executor_solana_core/src/lib.rs:129` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_executor_solana_core/src/lib.rs:137` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_executor_solana_core/src/lib.rs:145` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_executor_solana_core/src/lib.rs:159` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_executor_spl_token/src/lib.rs:29` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_executor_spl_token_2022/src/lib.rs:16` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_executor_spl_token_2022/src/lib.rs:47` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:167` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:175` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:180` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:188` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:198` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:204` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:209` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:232` — grouped re-export is forbidden
|
||||
- `RUST011` `kb_rpc/src/lib.rs:257` — grouped re-export is forbidden
|
||||
- `RUST013` `kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs:6` — grouped imports are forbidden
|
||||
- `RUST013` `kb_decoder_spl_token_2022/src/state.rs:916` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_solana_core/src/lib.rs:129` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_solana_core/src/lib.rs:137` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_solana_core/src/lib.rs:145` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_solana_core/src/lib.rs:159` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_spl_token/src/lib.rs:29` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_spl_token_2022/src/confidential.rs:425` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_spl_token_2022/src/lib.rs:16` — grouped imports are forbidden
|
||||
- `RUST013` `kb_executor_spl_token_2022/src/lib.rs:47` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:167` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:175` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:180` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:188` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:198` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:204` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:209` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:232` — grouped imports are forbidden
|
||||
- `RUST013` `kb_rpc/src/lib.rs:257` — grouped imports are forbidden
|
||||
- `RUST021` `kb_app_demo/src/lib.rs:42` — crate-root re-export requires adjacent rustdoc
|
||||
- `TRACE002` `kb_app_demo/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
- `TRACE002` `kb_decoder_metadata_metaplex_token_metadata/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
- `TRACE002` `kb_decoder_solana_core/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
- `TRACE002` `kb_decoder_spl_associated_token_account/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
- `TRACE002` `kb_decoder_spl_elgamal_registry/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
- `TRACE002` `kb_decoder_spl_memo/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
- `TRACE002` `kb_decoder_spl_token/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
- `TRACE002` `kb_decoder_spl_token_2022/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use`
|
||||
|
||||
Le rapport est tronqué aux 200 premiers écarts sur 616.
|
||||
|
||||
## Stratégie de correction
|
||||
|
||||
La mise en conformité est découpée avant toute nouvelle prérelease :
|
||||
|
||||
1. `pre.035-delta-fix-002` : séparation des règles et audit automatisé reproductible ;
|
||||
2. correctifs suivants : points d'entrée de crates, réexports `pub`/`pub(crate)`, rustdocs et chemins `crate::...` ;
|
||||
3. correctifs suivants : imports de traits et suppression des imports ordinaires/groupés ;
|
||||
4. correctifs suivants : `TRACING_TARGET`, dépendances `tracing`, macros et matrice de routes ;
|
||||
5. correctifs suivants : helpers dupliqués et mutualisation inter-crates ;
|
||||
6. fermeture : `cargo fmt --all`, audit strict, tests workspace et Clippy global.
|
||||
|
||||
Aucune nouvelle prérelease ne doit être créée tant que cette séquence n'est pas terminée et validée localement.
|
||||
@@ -0,0 +1,272 @@
|
||||
<!-- file: docs/SOLANA_INTERFACE_DEPENDENCIES.md -->
|
||||
<!-- version: 28 -->
|
||||
|
||||
# Dépendances d’interfaces Solana et SPL
|
||||
|
||||
## Objectif
|
||||
|
||||
Le workspace privilégie les interfaces officielles étroites afin d’éviter de recopier des enums, layouts, IDs ou builders déjà publiés par Solana ou SPL. Les dépendances de `[workspace.dependencies]` constituent un catalogue de versions cohérentes ; elles ne sont pas automatiquement ajoutées à toutes les crates.
|
||||
|
||||
## Règle de consommation
|
||||
|
||||
Une crate ajoute une interface uniquement lorsqu’elle utilise réellement au moins un de ses éléments :
|
||||
|
||||
- enum d’instruction ou d’état ;
|
||||
- encodeur/décodeur officiel ;
|
||||
- type de compte ou paramètre ;
|
||||
- program ID ou sysvar ID ;
|
||||
- modèle RPC appartenant à sa responsabilité.
|
||||
|
||||
Une déclaration à la racine ne justifie pas une dépendance inutilisée dans une crate feuille. Les features restent minimales et explicites.
|
||||
|
||||
## Ordre des formats
|
||||
|
||||
1. `wincode` lorsqu’il est officiellement exposé par l’interface ;
|
||||
2. Borsh lorsqu’il correspond au contrat officiel ou au comportement du runtime ;
|
||||
3. parseur local borné lorsque l’interface officielle ne fournit son helper qu’avec `bincode`.
|
||||
|
||||
### Compatibilité de version `wincode`
|
||||
|
||||
Le workspace reste épinglé sur `wincode ^0.5`. Les crates Solana modulaires actuellement utilisées avec la feature `wincode` publient leurs implémentations `SchemaRead`/`SchemaWrite` contre `wincode 0.5.x`. Ces traits sont nominalement distincts de ceux de `wincode 0.6.x` : une dépendance directe migrée seule vers `0.6` ne peut donc ni désérialiser `solana_nonce::versions::Versions`, ni sérialiser `solana_transaction::Transaction`. Cargo peut charger les deux versions simultanément, mais les implémentations de traits ne sont pas interchangeables et l’orphan rule interdit de les réimplémenter localement pour ces types externes.
|
||||
|
||||
La migration vers `wincode 0.6` doit attendre que l’ensemble des crates Solana consommées migre de façon cohérente. Une double dépendance aliasée n’est acceptable que pour des types propres au workspace ; tous les appels portant sur des types Solana doivent utiliser la même version `0.5.x` que celle employée par leurs crates d’origine. Avant toute migration, vérifier avec `cargo tree -d` et `cargo tree -i wincode@<version>`.
|
||||
|
||||
|
||||
Le workspace n’ajoute pas de dépendance directe à `bincode`. Une feature officielle `bincode` seule n’est pas activée pour contourner cette règle.
|
||||
|
||||
## Interfaces utilisées dans `kb_decoder_solana_core`
|
||||
|
||||
| Surface | Interface | Features utiles | Stratégie actuelle |
|
||||
|---------------------------|---------------------------------------------------|----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||
| Address Lookup Table | `solana-address-lookup-table-interface ^3.1` | `serde`, `wincode` | enum officielle et `wincode` exact |
|
||||
| Compute Budget | `solana-compute-budget-interface ^3.0` | `borsh`, `serde` | enum officielle pour les fixtures ; parser borné aligné sur le Borsh unchecked du runtime |
|
||||
| System Program | `solana-system-interface ^3.2` | `alloc`, `serde`, `std`, `solana-instruction`, `wincode` | enum officielle et `wincode` |
|
||||
| Vote Program | `solana-vote-interface ^6.0` | `serde`, `wincode` | enum officielle et `wincode::deserialize_exact` |
|
||||
| Stake Program | `solana-stake-interface ^4.3` | `borsh`, `serde`, `wincode` | types/ID officiels + miroir wire privé `wincode` exact, compatible avec le layout historique |
|
||||
| Précompiles signature | sources runtime Agave et crates programme `3.0.0` | aucune nouvelle dépendance consommée | parseurs locaux bornés des tables d’offsets, résolution outer via contrat core `2`, sans cryptographie ni `bincode` |
|
||||
| ZK ElGamal Proof | `solana-zk-elgamal-proof-interface ^0.1` | aucune feature additionnelle | enum `ProofInstruction` et tailles `Pod` officielles ; comportement des comptes vérifié contre le runtime Agave |
|
||||
| ZK Token Proof historique | aucune dépendance runtime dédiée | miroir wire local borné | tags et tailles historiques audités contre `solana-zk-token-sdk 3.1.14`; comparaison Agave `v2.0.0`/`v4.1.1` |
|
||||
| Slashing Program | Agave `v4.1.1`, `solana-program/slashing@fe8da3a` | aucune dépendance ajoutée | parseur local borné du contrat release vérifié ; compte de preuve externe non relu et aucune cryptographie locale |
|
||||
|
||||
## Décision pour les précompiles de signature
|
||||
|
||||
Les crates officielles Ed25519, secp256k1 et secp256r1 exposent les IDs, constantes et structures de layout, mais `kb_program_ids` centralise déjà les IDs et le delta n’exécute aucune vérification cryptographique. Ajouter ces dépendances uniquement pour recopier des tailles ou des structures `Pod` constituerait une dépendance sans consommation fonctionnelle.
|
||||
|
||||
`kb_decoder_solana_core` reproduit les layouts exacts depuis les sources runtime Agave : table Ed25519/secp256r1 de 14 octets, table secp256k1 de 11 octets, sentinelle `u16::MAX` uniquement pour Ed25519/secp256r1, index `u8` toujours explicite pour secp256k1, règles zéro signature et limite secp256r1. Les fixtures manuelles rendent chaque offset visible et les tests couvrent les références inter-instructions et les bornes. Aucune dépendance `bincode` n’est ajoutée.
|
||||
|
||||
## Transaction client dans `kb_execution_solana`
|
||||
|
||||
`kb_execution_solana` n’importe plus l’agrégat `solana-sdk`. Sa frontière transactionnelle utilise les crates modulaires réellement consommées : `solana-instruction`, `solana-message`, `solana-transaction`, `solana-hash`, `solana-pubkey`, `solana-keypair`, `solana-signer`, `solana-nonce` et `solana-system-interface`. Les exécuteurs spécifiques conservent la même politique et ajoutent uniquement l’interface de programme nécessaire.
|
||||
|
||||
Le message à signer provient de `solana_transaction::Transaction::message_data()`. La transaction complète utilise `wincode::serialize`, supporté officiellement par les types modulaires `Message` et `Transaction`. Cette stratégie remplace tout appel direct à `bincode` et reproduit le wire attendu par `getFeeForMessage`, `simulateTransaction` et `sendTransaction`. La taille sérialisée est refusée au-delà de 1 232 octets.
|
||||
|
||||
## Identifiants natifs, Vote et loaders dans l’exécuteur
|
||||
|
||||
`solana-sdk-ids` est conservé intentionnellement comme registre officiel modulaire des adresses natives et sysvars. Les chemins `solana_sdk_ids::sysvar::{clock,rent,slot_hashes,instructions}` ne constituent pas un retour au SDK monolithique : ils fournissent uniquement des `Pubkey` canoniques. `solana-instructions-sysvar` ne doit être ajouté que lorsqu’une crate appelle réellement ses fonctions d’introspection d’instructions, pas pour remplacer un simple ID officiel.
|
||||
|
||||
`kb_executor_solana_core` consomme `solana-vote-interface ^6.0` avec `serde` et `wincode` pour construire les vingt variantes wire Vote actuelles. Il utilise `solana-instruction` pour `Instruction`/`AccountMeta`, `solana-pubkey::Pubkey` pour les adresses publiques, `solana-hash::Hash` pour les hashes et `solana-sdk-ids` pour Rent, Clock et SlotHashes. Les quatre créations composées utilisent `solana-system-interface`; aucune dépendance `solana-sdk` ni feature `bincode` n’est ajoutée à la crate.
|
||||
|
||||
`kb_decoder_solana_core` et `kb_executor_solana_core` consomment désormais `solana-loader-v3-interface ^8.0` avec `wincode`. Le décodeur désérialise l’enum officielle de façon exacte tout en conservant la compatibilité du booléen optionnel historique des dernières variantes. L’exécuteur appelle les helpers officiels pour onze plans : création de buffer, écriture, déploiement, upgrade, changements d’autorité, fermetures et extension.
|
||||
|
||||
`solana-loader-v4-interface ^3.1` publie l’enum et les comptes officiels, mais ses helpers de construction sont conditionnés par la feature `bincode` et l’enum ne fournit pas de schéma `wincode`. Pour respecter l’interdiction de `bincode`, `kb_executor_solana_core` reproduit localement le wire borné des sept discriminants (`u32 LE`, champs numériques et vecteur borné) et les métadonnées de comptes exactes pour neuf plans. La crate feuille n’ajoute donc pas une dépendance inutilisée à l’interface v4; le catalogue workspace la conserve comme source normative/versionnée.
|
||||
|
||||
## Interfaces officielles non encore consommées
|
||||
|
||||
| Interface | Propriétaire futur | Décision |
|
||||
|------------------------------------------|------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `solana-feature-gate-interface ^4.0` | `kb_decoder_solana_core` | peut fournir les types officiels ; l’instruction actuelle reste un tag d’un octet vérifié depuis le builder/processor |
|
||||
| `solana-feature-set-interface ^4.0` | classification/diagnostics natifs | ajouter seulement lorsqu’un type est consommé |
|
||||
| `solana-loader-v4-interface ^3.1` | source normative Loader v4 | helpers officiels conditionnés par `bincode`; conserver le miroir wire local borné tant qu’aucun schéma `wincode` n’est publié |
|
||||
| interfaces SPL | crates `kb_decoder_spl_*` et `kb_executor_spl_*` correspondantes | ne pas les rattacher au décodeur natif ni à l’application |
|
||||
| types RPC/account/transaction status | `kb_rpc` | ne pas les ajouter à `kb_app_demo` ; l’application consomme les DTO du workspace |
|
||||
|
||||
## Exceptions locales actuelles
|
||||
|
||||
### SPL Memo v1, v3 et v4
|
||||
|
||||
`kb_decoder_spl_memo` et `kb_executor_spl_memo` consomment `spl-memo-interface ^2.1` sans feature additionnelle. La version effectivement résolue pendant les validations est 2.1.0. Elle publie les trois IDs exacts et un builder générique recevant explicitement le Program ID. Ce builder place les octets exacts du message dans `Instruction::data` et transforme chaque pubkey fournie en compte readonly signer, sans sérialisation intermédiaire.
|
||||
|
||||
L’exécuteur appelle ce builder pour les trois générations et compare chaque plan au résultat officiel. Les différences runtime restent explicites : v1 ignore les comptes, v3/v4 les vérifient. La bibliothèque ne lie toutefois aucune génération à un cluster ou au dry-run : la simulation obligatoire établit la disponibilité effective du Program ID ciblé, puis les politiques communes gouvernent l’autorisation d’envoi et Mainnet. La démo du jalon retient séparément v4 sur Devnet. La taille packet finale reste contrôlée par `kb_execution_solana` après compilation du message.
|
||||
|
||||
Le décodeur ne dépend pas du builder pour lire le wire : il analyse directement les octets base64 retenus par le core, avec une borne de 4 096 octets, puis valide l’UTF‑8. La source historique v1 prouve que les comptes sont ignorés ; les processors v3/v4 exigent au contraire que chaque compte fourni soit signer avant de valider l’UTF‑8. Ces règles restent séparées dans `docs/SPL_MEMO_MATRIX.json` et les tests comparent les trois IDs locaux aux constantes de l’interface officielle.
|
||||
|
||||
L’archive source `0.4.2` ne contenait pas de `Cargo.lock`. Les compilations et validations `0.4.3` ont résolu `spl-memo-interface 2.1.0`, sans mise à jour automatique de la contrainte workspace. Cette version exacte a construit les trois générations et le parcours v4 Devnet réel.
|
||||
|
||||
### SPL Token classique
|
||||
|
||||
Le catalogue workspace déclare `spl-token-interface ^3.0` sans feature. Le tag officiel
|
||||
`interface@v3.0.0` publie la version `3.0.0`, sans feature optionnelle ni dépendance à `bincode`.
|
||||
`kb_decoder_spl_token` consomme l'ID officiel et utilise `TokenInstruction::unpack` dans les tests
|
||||
différentiels. Le wire de production reste un parseur local borné afin de conserver les tailles
|
||||
consommées, les suffixes acceptés par les processors, les diagnostics UTF-8 et la structure interne
|
||||
de `Batch`, que l'enum officielle représente seulement par son tag parent.
|
||||
|
||||
La surface compte 28 tags : `0..24`, `38` (`WithdrawExcessLamports`), `45`
|
||||
(`UnwrapLamports`) et `255` (`Batch`). L'interface `3.0.0` fournit un builder pour chacun. La source
|
||||
historique `program/src/processor.rs` ne suffit toutefois pas à prouver les deux nouveaux tags : le
|
||||
processor p-token `1.0.0` sous le même Program ID implémente explicitement `UnwrapLamports` et
|
||||
`Batch`. La publication de l'interface, la constructibilité, le binaire déployé par cluster et le
|
||||
résultat de simulation restent quatre preuves séparées dans `docs/SPL_TOKEN_MATRIX.json`.
|
||||
|
||||
Le processor p-token lit les champs numériques avec des contrôles de longueur minimale et peut
|
||||
donc ignorer des octets suffixes selon l'opération. Le décodeur les conserve et les signale au lieu
|
||||
de les accepter silencieusement. `Batch` est découpé selon les paires `account_count/data_len`,
|
||||
interdit les données enfant vides et appelle uniquement le processor interne, ce qui rend un batch
|
||||
imbriqué invalide. Le décodeur ajoute des bornes défensives de 64 enfants, 512 comptes cumulés et
|
||||
16 384 octets par instruction.
|
||||
|
||||
La résolution `3.0.0` et les tests différentiels du décodeur ont été confirmés dans le workspace le
|
||||
15 juillet 2026. `kb_executor_spl_token` consomme les builders officiels de cette même interface.
|
||||
Les variantes `InitializeMint`, `InitializeAccount`, `InitializeMultisig` et `InitializeAccount2`
|
||||
restent decode-only dans la politique d'exécution ; les opérations anciennes toujours publiées et
|
||||
utilisables, comme `Transfer` ou `SetAuthority`, ne sont pas confondues avec des opérations
|
||||
obsolètes.
|
||||
|
||||
Les signatures officielles de `unwrap_lamports` et `batch` ont été auditées au commit `46eaacd`.
|
||||
Leur constructibilité est donc prouvée. Leur déploiement sur le programme classique Devnet a été
|
||||
observé le 16 juillet 2026 par deux simulations sans signature ni envoi : `Batch` avec un enfant
|
||||
`TransferChecked` de montant nul a réussi en 270 compute units, puis `UnwrapLamports` d'un lamport
|
||||
depuis un compte wrapped SOL auxiliaire classique a réussi en 140 compute units. Localnet, Testnet
|
||||
et Mainnet restent des preuves séparées. L'exécuteur ne bloque aucun cluster et laisse
|
||||
l'autorisation d'envoi aux politiques communes.
|
||||
|
||||
Sur Devnet, le programme classique a également confirmé un `TransferChecked` complet et un
|
||||
lifecycle sans ATA couvrant `InitializeAccount3`, `MintToChecked`, `TransferChecked`,
|
||||
`ApproveChecked`, `Revoke`, `BurnChecked` et `CloseAccount`. Sur Mainnet, un corpus canonique de 370
|
||||
instructions a été décodé et matérialisé sans entrée unsupported, failed ou unmatched. Localnet n'a
|
||||
pas été exercé. La matrice conserve donc indépendamment la version d'interface, le processor de
|
||||
référence, le builder, les observations réelles et le résultat de simulation/soumission par
|
||||
instruction.
|
||||
|
||||
### SPL Associated Token Account
|
||||
|
||||
Le catalogue workspace déclare `spl-associated-token-account-interface ^2.0` avec la feature
|
||||
`borsh`. Cargo a résolu la version `2.0.0` pendant le jalon `0.4.5`. Les crates
|
||||
`kb_decoder_spl_associated_token_account` et `kb_executor_spl_associated_token_account` consomment
|
||||
réellement l'enum, les builders, le Program ID et le helper officiel de dérivation ; le pipeline et
|
||||
la démo consomment ensuite leurs contrats typés sans recopier l'interface.
|
||||
|
||||
L'interface publie exactement trois variantes Borsh actuelles : `Create` (`0`),
|
||||
`CreateIdempotent` (`1`) et `RecoverNested` (`2`). Le processor accepte aussi historiquement une
|
||||
donnée vide comme `Create`; cette forme reste décodable mais n'est pas produite par le builder
|
||||
actuel. La dérivation canonique utilise les seeds `[wallet, Token Program ID, mint]` sous le Program
|
||||
ID ATA. Le Token Program fait donc partie de l'adresse et distingue nécessairement les ATA
|
||||
classiques des ATA Token-2022 d'un même wallet et mint.
|
||||
|
||||
Le décodeur conserve les comptes, flags, doublons, chemins outer/inner, transactions échouées,
|
||||
adresses observées et dérivées, ainsi que les rôles distincts de `RecoverNested`. Les tests
|
||||
différentiels comparent les trois builders et les vecteurs PDA au helper officiel. L'exécuteur
|
||||
utilise les mêmes builders pour les trois variantes, impose simulation et dry-run par défaut, puis
|
||||
laisse au pipeline stateful les contrôles de mint, owner, compte existant, rent, signataires et
|
||||
postconditions.
|
||||
|
||||
Les preuves Devnet du 16 juillet 2026 couvrent `CreateIdempotent` pour SPL Token classique et
|
||||
Token-2022, y compris création, réutilisation, replay post-exécution idempotent et ATA Token-2022
|
||||
avec extension `ImmutableOwner`. Elles couvrent aussi un `RecoverNested` classique contrôlé : le
|
||||
solde brut a été transféré vers l'ATA wallet, le compte nested a été fermé et les deux faits
|
||||
lifecycle/risk ont été matérialisés sans dupliquer les CPI SPL Token. Le décodage général des
|
||||
instructions et extensions Token-2022 reste réservé à `0.4.6`.
|
||||
|
||||
### Token-2022 — audit initial `0.4.6-pre.001`
|
||||
|
||||
Le catalogue workspace déclare `spl-token-2022-interface ^3.1` avec `serde` et
|
||||
`spl-elgamal-registry-interface ^0.2` sans feature additionnelle. La résolution opérateur confirme
|
||||
respectivement `3.1.1` avec `default + serde` et `0.2.1` avec `default`, consommées par
|
||||
`kb_decoder_spl_token_2022` pour poursuivre l'audit. La matrice inventorie 48 tags de premier
|
||||
niveau et 29 types TLV de production, dont le récent `PermissionedBurnExtension` `46` et son TLV
|
||||
`PermissionedBurn` `28`.
|
||||
|
||||
Les sous-discriminants, comptes, layouts, tailles, builders et processors restent explicitement
|
||||
`pending`. L'interface ElGamal `0.2.1` confirme le Program ID
|
||||
`regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` et la seed PDA `elgamal-registry`. Le registre
|
||||
garde toutefois une matrice, un dispatch et une politique d'exécution séparés du programme natif
|
||||
ZK ElGamal Proof. Les TLV Token Metadata/Token Group et les pointeurs Transfer Hook ne donnent
|
||||
aucun Program ID universel aux programmes externes qui implémentent ces interfaces.
|
||||
|
||||
L'interface Token-2022 `3.1.1` publie le commit exact
|
||||
`e18f9c6f9bf6044b934f48e3090e8e59e4820f02` (`interface@v3.1.1`). Au même commit, le processor
|
||||
porte la version `11.0.0` et son lock source résout Token Group `0.7.2`, Token Metadata `1.0.0` et
|
||||
le registre ElGamal `0.2.1`. Son dispatch tente d'abord les 48 tags Token-2022, puis les cinq
|
||||
discriminants Token Metadata et les quatre discriminants Token Group. Cette incorporation ne donne
|
||||
pas de Program ID universel aux interfaces : elle décrit seulement leur exécution directe par
|
||||
Token-2022. L'égalité avec les binaires cluster reste à prouver séparément.
|
||||
|
||||
Le `Cargo.lock` opérateur confirme le graphe réellement consommé par l'interface `3.1.1` : ZK
|
||||
ElGamal Proof `0.1.3`, extraction de preuve confidentielle `0.6.1`, Token Group `0.7.2`, Token
|
||||
Metadata `1.0.1` et TLV `0.9.1`. Token Metadata apparaît directement sous
|
||||
`spl-token-2022-interface` dans `cargo tree -p ... -e features`. Le processor source `11.0.0`
|
||||
audité figeait encore Token Metadata `1.0.0`. La publication officielle `1.0.1` ne change ni wire,
|
||||
ni discriminant, ni builder : elle ajoute seulement, avant Borsh, une validation bornée des
|
||||
longueurs de chaînes de `Initialize`, `UpdateField` et `RemoveKey`. Cette garde plus sûre devient
|
||||
normative pour le décodeur ; `UpdateAuthority` et `Emit` restent sans chaîne.
|
||||
|
||||
### ZK ElGamal Proof
|
||||
|
||||
`kb_decoder_solana_core` consomme directement `solana-zk-elgamal-proof-interface ^0.1`. L’enum `ProofInstruction` fournit les treize discriminants et les types de `proof_data` fournissent les tailles `Pod` exactes pour séparer le contexte du corps de preuve inline. Le runtime Agave reste la source normative pour la sélection entre preuve inline et preuve stockée dans un compte, les positions de comptes optionnels et l’acceptation des octets suffixes de `CloseContextState`.
|
||||
|
||||
Aucune dépendance à `bytemuck` n’est ajoutée directement : le décodeur ne convertit pas les octets en preuve cryptographique et n’appelle aucun vérificateur. Il utilise uniquement `size_of` sur les types officiels, puis conserve longueur, SHA-256 et préfixe borné. Le contenu d’un compte de preuve externe n’est pas récupéré par RPC pendant un replay historique.
|
||||
|
||||
### ZK Token Proof historique
|
||||
|
||||
`kb_decoder_solana_core` ne dépend plus de `solana-zk-token-sdk`. Cette crate historique est dépréciée et entraînait `bincode` transitivement. Le workspace conserve une table wire locale limitée aux tags `0..16`, aux tailles exactes de contexte/preuve et à l’offset `u32 LE`, auditée contre la version historique `3.1.14`; aucun code de preuve ni sérialiseur historique n’est recopié.
|
||||
|
||||
Le comportement est documenté depuis deux références runtime distinctes : Agave `v2.0.0` pour le vérificateur historique et Agave `v4.1.1` pour le stub actuel sans effet. Aucun corpus mainnet n’ayant été observé, les tests sont synthétiques et n’exigent pas de backfill.
|
||||
|
||||
### Stake Program
|
||||
|
||||
`solana-stake-interface 4.3.1` publie `StakeInstruction` avec Serde et conserve ses builders derrière la feature `bincode`. La feature `wincode` fournit les schémas nécessaires aux types d’état, mais pas directement à l’enum d’instruction. Le décodeur et l’exécuteur consomment donc l’ID et les types sémantiques officiels, avec un miroir wire privé commun dans son ordre de variantes et de champs. Aucun helper `bincode` n’est activé.
|
||||
|
||||
L’exécuteur reproduit les vingt-quatre helpers clients actuels : initialize/create, checked/seed, split/merge, create-and-delegate, quatre authorize, delegate, withdraw, deactivate, lockup, minimum delegation, delinquent deactivation et move. Les helpers `redelegate` et `redelegate_with_seed` sont explicitement exclus parce que la source officielle les marque dépréciés et « will not be enabled » ; ils restent uniquement dans le miroir historique du décodeur.
|
||||
|
||||
Le contrat des comptes est aligné sur les builders officiels. Les contrôles stateful du processor BPF — état de compte, rent, epochs, compatibilité et autorités courantes — restent à l’orchestration localnet/devnet.
|
||||
|
||||
### Config Program
|
||||
|
||||
`solana-config-interface 2.x` expose son module d’instruction derrière sa feature `bincode`. Le décodeur et l’exécuteur conservent donc un wire local borné pour `ConfigKeys` : compact-u16 canonique, booléens stricts, ordre exact des signataires, rejet des doublons et payload métier opaque. Le builder de création reproduit la taille `max_config_space + serialized_size(ConfigKeys)` et l’initialisation `ConfigKeys vide + T::default()` à partir d’octets déjà sérialisés ; il n’active aucun helper `bincode`.
|
||||
|
||||
### BPF Loader v2
|
||||
|
||||
`solana-loader-v2-interface 3.x` publie ses helpers d’instruction derrière `bincode`. Le décodeur conserve le layout local vérifié tant qu’aucun schéma officiel `wincode` n’est disponible.
|
||||
|
||||
### Feature Gate
|
||||
|
||||
`solana-feature-gate-interface 4.x` publie la séquence d’activation `transfer -> allocate -> assign` et `RevokePendingActivation`, mais les helpers restent derrière sa feature `bincode`. L’exécuteur utilise `Feature::size_of()` et reproduit ces instructions à partir des interfaces System étroites ; la révocation reste exactement `vec![0]` avec feature, incinerator et System Program dans l’ordre officiel.
|
||||
|
||||
### Slashing Program
|
||||
|
||||
La source normative est SIMD-0204 et le contrat du programme Slashing publié : deux instructions, un payload DuplicateBlockProof de 305 octets, un report PDA dérivé de `node_pubkey + slot LE + type`, et une instruction Ed25519 précédente dont les offsets pointent vers le payload Slashing. Aucune crate de sérialisation générale n’est nécessaire. L’exécuteur construit le plan atomique et laisse au pipeline stateful le compte de preuve, le rent et les fenêtres epoch.
|
||||
|
||||
### ZK ElGamal Proof dans l’exécuteur
|
||||
|
||||
`kb_executor_solana_core` dépend directement de `solana-zk-elgamal-proof-interface ^0.1` pour les types POD et leurs tailles. Les layouts d’instruction sont simples et officiels : discriminant + preuve inline, ou discriminant + offset `u32 LE`; les comptes de contexte suivent `ContextStateInfo`. La fermeture reproduit le builder officiel. Aucune vérification cryptographique n’est recalculée côté client.
|
||||
|
||||
### Loaders v3 et v4
|
||||
|
||||
La migration Loader v3 est terminée dans `pre.020` : le décodeur utilise `wincode::deserialize_exact<UpgradeableLoaderInstruction>` et l’exécuteur utilise les helpers officiels de `solana-loader-v3-interface 8.x`. Les payloads tronqués, suffixés, les longueurs et les contrats de comptes restent bornés par les tests.
|
||||
|
||||
Loader v4 conserve un parser et un constructeur locaux limités aux sept discriminants publiés. Le layout est suffisamment petit pour être audité explicitement : tag `u32 LE`, `Write { offset, Vec<u8> }`, `Copy { destination_offset, source_offset, length }`, `SetProgramLength { new_size }` et quatre variantes unitaires. Cette exception évite d’activer `bincode` uniquement pour appeler les helpers de l’interface.
|
||||
|
||||
## `solana-sdk`
|
||||
|
||||
`solana-sdk` avec la feature `full` peut rester disponible au niveau workspace pour les applications ou outils qui ont réellement besoin de l’agrégat complet. Les crates feuille doivent préférer les interfaces étroites afin de réduire les features transitives, les temps de compilation et les risques de dépendances inutilisées.
|
||||
|
||||
## Vérification avant ajout
|
||||
|
||||
Pour chaque nouvelle interface :
|
||||
|
||||
1. lire ses features et ses modules réellement compilés ;
|
||||
2. identifier le format officiel de sérialisation ;
|
||||
3. confirmer la version compatible avec Agave ciblée ;
|
||||
4. ajouter la dépendance uniquement à la crate propriétaire ;
|
||||
5. créer une fixture depuis l’encodeur officiel lorsque disponible ;
|
||||
6. tester les payloads tronqués, inconnus, suffixés et les comptes invalides ;
|
||||
7. documenter toute divergence entre l’interface et le runtime.
|
||||
|
||||
### Metaplex Token Metadata — audit et premier décodage `0.4.7-pre.002`
|
||||
|
||||
Le catalogue workspace déclare `mpl-token-metadata ^5.1` avec la seule feature `serde`. La publication officielle résolue reste `5.1.1`; elle fournit les modules générés `accounts`, `instructions`, `types` et `errors` issus de l’IDL Metaplex, sans introduire Anchor. La crate officielle utilise Borsh `< 1.0`; le décodeur référence donc explicitement `borsh 0.10` sous l’alias workspace `borsh_0_10`, distinct de Borsh 1.x utilisé par d’autres interfaces du workspace.
|
||||
|
||||
Le dépôt officiel `metaplex-foundation/mpl-token-metadata` confirme le Program ID `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s`. L’IDL officiel audité porte la version `1.14.0` et le blob `5df4a24f62c2743125be096cc174680790c92c18`; l’inventaire Rust généré des instructions porte le blob `3c42eec629f82e44ba690a7c4bd2177cc78f1d49`.
|
||||
|
||||
`kb_decoder_metadata_metaplex_token_metadata` implémente désormais `InstructionDecoder` pour `CreateMetadataAccountV3` et `UpdateMetadataAccountV2`. Les arguments sont lus avec les types Borsh officiels et projetés avec la feature `serde`; les payloads, chaînes, créateurs, frais, comptes et suffixes sont bornés. L’enregistrement dans le registre runtime reste différé jusqu’à validation de ce premier groupe. Les metadata Token-2022 incorporées, Metaplex Core, Bubblegum et le JSON externe restent des provenances ou composants distincts.
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<!-- file: docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Audit de clôture native, exécution et RPC — `0.4.2`
|
||||
|
||||
## Décision
|
||||
|
||||
Le jalon `0.4.2` est clôturé. L’audit distingue quatre contrats indépendants :
|
||||
|
||||
1. les opérations futures constructibles par `kb_executor_solana_core` ;
|
||||
2. les instructions passées décodables par `kb_decoder_solana_core` ;
|
||||
3. les méthodes standard HTTP/WebSocket exposées par `kb_rpc` ;
|
||||
4. l’orchestration de sécurité, de simulation, d’envoi et de replay post-exécution.
|
||||
|
||||
Une méthode transportable via JSON brut n’est pas considérée comme un adaptateur typé. Un builder stateless n’autorise pas à lui seul une opération dépendant d’un compte, d’une autorité, du rent, d’un slot ou d’un epoch. Une opération dangereuse peut être complète dans la bibliothèque tout en restant absente de l’UI et désactivée sur Mainnet.
|
||||
|
||||
## Résultat synthétique
|
||||
|
||||
| Domaine | Inventaire canonique | Contrat final `0.4.2` |
|
||||
|-------------------------|-------------------------------------------------------------------------:|------------------------------------------------------------------------------------------------|
|
||||
| Exécuteur natif | 18 surfaces, 14 appelables, 4 non invocables/historiques, 109 opérations | égalité imposée par test Rust ; builders comparés aux interfaces ou layouts officiels |
|
||||
| Décodeur natif | 18 surfaces, 121 déclarations | égalité imposée avec `kb_program_ids::native_program_ids()` et `SolanaCoreDecoder::coverage()` |
|
||||
| HTTP JSON-RPC | 52 méthodes | 52 contrats typés configurables, 0 méthode standard limitée au JSON brut |
|
||||
| WebSocket JSON-RPC | 9 paires / 18 méthodes | 9 requêtes, 9 notifications et 9 runtimes persistants typés |
|
||||
| Préflight stateful | ALT, Config, Feature, Slashing, ZK ElGamal | rapports `NotRequired` / `Ready` / `Blocked`, Localnet/Devnet uniquement |
|
||||
| Parcours mutable validé | System transfer Devnet | simulation, signature, envoi, confirmation, canonical, core et decode replay réussis |
|
||||
|
||||
## Exécuteur Solana Core
|
||||
|
||||
`docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` et `SOLANA_CORE_OPERATION_CODES` imposent :
|
||||
|
||||
- exactement 18 surfaces ;
|
||||
- exactement 14 surfaces appelables et 4 surfaces sans instruction client ;
|
||||
- exactement 109 codes d’opération uniques ;
|
||||
- Program IDs présents dans `kb_program_ids` ;
|
||||
- contrat de construction, politique, tests, validation cluster et décision UI pour chaque opération ;
|
||||
- distribution des préflights `27 none / 14 required / 68 future`.
|
||||
|
||||
Les surfaces sans builder client sont BPF Loader v1, BPF Loader v2, Native Loader et l’ancien ZK Token Proof Program. Stake `Redelegate` reste historique `decode-only`. Cette classification évite d’inventer des instructions qui ne sont plus ou n’ont jamais été appelables par un client moderne.
|
||||
|
||||
Les entrées `future` ne signifient pas « non implémenté ». Elles signifient que le wire et le plan sont couverts offline, mais que l’activation mutable exige encore une preuve stateful par scénario sur Localnet/Devnet. Aucune de ces opérations n’est activée sur Mainnet par `0.4.2`.
|
||||
|
||||
## Décodeur Solana Core
|
||||
|
||||
`docs/NATIVE_SOLANA_DECODER_MATRIX.json` couvre exactement les 18 surfaces du registre natif et 121 déclarations. Les tests de complétude parcourent les enums officielles ou les tables wire auditées pour System, Compute Budget, ALT, Stake, Vote, loaders, précompiles, Slashing et preuves ZK.
|
||||
|
||||
Stake Config reste un compte bien connu non exécutable. Il ne possède ni dispatch d’instruction ni plan d’exécution.
|
||||
|
||||
## HTTP JSON-RPC standard
|
||||
|
||||
`docs/SOLANA_STANDARD_RPC_MATRIX.json` et `STANDARD_HTTP_METHODS` enregistrent 52 méthodes. Chacune possède un contrat de requête/résultat propre à `kb_rpc`. Les options facultatives restent indépendantes : commitment, `minContextSlot`, encodage, `dataSlice`, filtres, contexte, tri, niveau de détail, rewards et version maximale de transaction.
|
||||
|
||||
Les DTO Agave ne font pas partie de l’API publique. La voie JSON brute reste une primitive interne ou d’extension fournisseur, pas un substitut à une méthode standard absente.
|
||||
|
||||
## WebSocket JSON-RPC standard
|
||||
|
||||
Les neuf paires standard possèdent paramètres, notifications et runtime persistant dans `kb_rpc`. `WsSession` fournit :
|
||||
|
||||
- une socket multiplexée ;
|
||||
- des identifiants locaux stables et distants remappables ;
|
||||
- unsubscribe explicite ;
|
||||
- timeouts bornés ;
|
||||
- ping/pong et fermeture ;
|
||||
- reconnexion exponentielle bornée ;
|
||||
- réabonnement ;
|
||||
- suppression terminale de `signatureSubscribe`.
|
||||
|
||||
`blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont désactivées par défaut, mais activables lorsqu’un endpoint les annonce explicitement. La première souscription réelle sert de probe paresseux. Une réponse `Method not found`, `not enabled`, `unavailable` ou `unsupported` désactive uniquement la capacité concernée pour la session ; les erreurs de paramètres, d’authentification ou de rate limit ne la désactivent pas.
|
||||
|
||||
## Frontière Tauri et TS-rs
|
||||
|
||||
Les payloads actifs de `kb_app_demo` et `kb_config` transmis par JSON utilisent des `number`, pas des `bigint`. Les valeurs d’identifiants sont contrôlées avec `Number.isSafeInteger` avant invocation. Les types génériques hors frontière Tauri conservent leurs contrats exacts lorsqu’ils ne sont pas sérialisés par `JSON.stringify`.
|
||||
|
||||
Le navigateur SQL applique maintenant la limite maximale reçue du backend avant l’appel Tauri. Une valeur supérieure à 5 000 produit un diagnostic frontend et n’atteint plus PostgreSQL.
|
||||
|
||||
## Preuves de validation
|
||||
|
||||
Validations fournies le 13 juillet 2026 :
|
||||
|
||||
| Crate / contrôle | Résultat |
|
||||
|------------------------------------|----------:|
|
||||
| `kb_program_ids` | 5 tests |
|
||||
| `kb_decoder_solana_core` | 116 tests |
|
||||
| `kb_executor_solana_core` | 88 tests |
|
||||
| `kb_execution_api` | 22 tests |
|
||||
| `kb_execution_safety` | 15 tests |
|
||||
| `kb_execution_solana` | 12 tests |
|
||||
| `kb_rpc` | 113 tests |
|
||||
| `kb_pipeline` | 56 tests |
|
||||
| `kb_config` | 41 tests |
|
||||
| `kb_app_demo` | 88 tests |
|
||||
| `kb_store_pg` avec PostgreSQL réel | 45 tests |
|
||||
| `cargo clippy --all-targets` | propre |
|
||||
|
||||
Le test Devnet opt-in a exécuté un transfert de 1 000 000 lamports avec airdrop nul depuis un wallet préfinancé. Il a confirmé simulation, signature, envoi, confirmation, hydratation canonique, extraction core et decode replay. `cargo tauri dev` a démarré Vite, initialisé les treize tables attendues et exercé les sessions WebSocket.
|
||||
|
||||
L’archive de logs Mainnet/Tauri confirme des unsubscribe réels pour `slot`, `root` et `program`. Le refus de sélectionner un autre endpoint tant qu’une session est active est un garde-fou volontaire. L’ancien échec `JSON.stringify cannot serialize BigInt` n’est plus présent.
|
||||
|
||||
## Limites conservées après clôture
|
||||
|
||||
- Mainnet reste désactivé par défaut et exige une politique explicite.
|
||||
- Les opérations stateful administratives non exercées sur cluster restent hors UI et bloquées avant toute future activation mutable.
|
||||
- Le parcours validé utilise une transaction legacy avec recent blockhash ; durable nonce et transactions v0 sont couverts par les bibliothèques mais nécessitent leurs propres parcours opérateur avant exposition.
|
||||
- Les méthodes WebSocket instables dépendent du nœud et peuvent être désactivées automatiquement à l’exécution.
|
||||
|
||||
La clôture de `0.4.2` n’ouvre aucune version suivante et ne fixe aucun plan de sources historiques externes.
|
||||
@@ -0,0 +1,430 @@
|
||||
{
|
||||
"file": "docs/SOLANA_STANDARD_RPC_MATRIX.json",
|
||||
"version": 3,
|
||||
"release": "0.4.2-pre.025",
|
||||
"canonical_reference": "https://solana.com/docs/rpc",
|
||||
"http_method_count": 52,
|
||||
"http_typed_adapter_count": 52,
|
||||
"http_raw_json_count": 0,
|
||||
"ws_subscription_pair_count": 9,
|
||||
"ws_method_count": 18,
|
||||
"ws_unstable_subscription_count": 3,
|
||||
"http_methods": [
|
||||
{
|
||||
"method": "getAccountInfo",
|
||||
"category": "accounts",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getBalance",
|
||||
"category": "accounts",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getLargestAccounts",
|
||||
"category": "accounts",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getMinimumBalanceForRentExemption",
|
||||
"category": "accounts",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getMultipleAccounts",
|
||||
"category": "accounts",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getProgramAccounts",
|
||||
"category": "accounts",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getTokenAccountBalance",
|
||||
"category": "tokens",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getTokenAccountsByDelegate",
|
||||
"category": "tokens",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getTokenAccountsByOwner",
|
||||
"category": "tokens",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getTokenLargestAccounts",
|
||||
"category": "tokens",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getTokenSupply",
|
||||
"category": "tokens",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getFeeForMessage",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getLatestBlockhash",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getRecentPrioritizationFees",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getSignaturesForAddress",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getSignatureStatuses",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getTransaction",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getTransactionCount",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "isBlockhashValid",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "requestAirdrop",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "sendTransaction",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "simulateTransaction",
|
||||
"category": "transactions",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getBlock",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getBlockCommitment",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getBlockHeight",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getBlockProduction",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getBlocks",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getBlocksWithLimit",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getBlockTime",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getFirstAvailableBlock",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getRecentPerformanceSamples",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "minimumLedgerSlot",
|
||||
"category": "blocks",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getClusterNodes",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getEpochInfo",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getEpochSchedule",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getGenesisHash",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool"
|
||||
},
|
||||
{
|
||||
"method": "getHealth",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getHighestSnapshotSlot",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getIdentity",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getLeaderSchedule",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getMaxRetransmitSlot",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getMaxShredInsertSlot",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getSlot",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getSlotLeader",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getSlotLeaders",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getVersion",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getVoteAccounts",
|
||||
"category": "cluster",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getInflationGovernor",
|
||||
"category": "economics",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getInflationRate",
|
||||
"category": "economics",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getInflationReward",
|
||||
"category": "economics",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getStakeMinimumDelegation",
|
||||
"category": "economics",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
},
|
||||
{
|
||||
"method": "getSupply",
|
||||
"category": "economics",
|
||||
"contract": "typed_adapter",
|
||||
"implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport"
|
||||
}
|
||||
],
|
||||
"ws_subscriptions": [
|
||||
{
|
||||
"subscribe_method": "accountSubscribe",
|
||||
"unsubscribe_method": "accountUnsubscribe",
|
||||
"notification_method": "accountNotification",
|
||||
"stability": "stable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": false
|
||||
},
|
||||
{
|
||||
"subscribe_method": "blockSubscribe",
|
||||
"unsubscribe_method": "blockUnsubscribe",
|
||||
"notification_method": "blockNotification",
|
||||
"stability": "unstable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": true
|
||||
},
|
||||
{
|
||||
"subscribe_method": "logsSubscribe",
|
||||
"unsubscribe_method": "logsUnsubscribe",
|
||||
"notification_method": "logsNotification",
|
||||
"stability": "stable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": false
|
||||
},
|
||||
{
|
||||
"subscribe_method": "programSubscribe",
|
||||
"unsubscribe_method": "programUnsubscribe",
|
||||
"notification_method": "programNotification",
|
||||
"stability": "stable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": false
|
||||
},
|
||||
{
|
||||
"subscribe_method": "rootSubscribe",
|
||||
"unsubscribe_method": "rootUnsubscribe",
|
||||
"notification_method": "rootNotification",
|
||||
"stability": "stable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": false
|
||||
},
|
||||
{
|
||||
"subscribe_method": "signatureSubscribe",
|
||||
"unsubscribe_method": "signatureUnsubscribe",
|
||||
"notification_method": "signatureNotification",
|
||||
"stability": "stable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": false
|
||||
},
|
||||
{
|
||||
"subscribe_method": "slotSubscribe",
|
||||
"unsubscribe_method": "slotUnsubscribe",
|
||||
"notification_method": "slotNotification",
|
||||
"stability": "stable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": false
|
||||
},
|
||||
{
|
||||
"subscribe_method": "slotsUpdatesSubscribe",
|
||||
"unsubscribe_method": "slotsUpdatesUnsubscribe",
|
||||
"notification_method": "slotsUpdatesNotification",
|
||||
"stability": "unstable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": true
|
||||
},
|
||||
{
|
||||
"subscribe_method": "voteSubscribe",
|
||||
"unsubscribe_method": "voteUnsubscribe",
|
||||
"notification_method": "voteNotification",
|
||||
"stability": "unstable",
|
||||
"request_contract": "typed_adapter",
|
||||
"persistent_runtime": true,
|
||||
"library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription",
|
||||
"notification_contract": "typed_adapter",
|
||||
"explicit_capability_required": true
|
||||
}
|
||||
],
|
||||
"ws_typed_request_count": 9,
|
||||
"ws_typed_notification_count": 9,
|
||||
"ws_persistent_runtime_count": 9
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
{
|
||||
"file": "docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json",
|
||||
"version": 9,
|
||||
"schemaVersion": 1,
|
||||
"programId": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
|
||||
"systemProgramId": "11111111111111111111111111111111",
|
||||
"supportedTokenPrograms": [
|
||||
{"code": "spl_token", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "scope": "classic_token_account_lifecycle_only"},
|
||||
{"code": "spl_token_2022", "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "scope": "ata_lifecycle_only_without_general_token_2022_instruction_or_extension_decode"}
|
||||
],
|
||||
"interface": {
|
||||
"workspaceConstraint": "^2.0",
|
||||
"resolvedVersion": "2.0.0",
|
||||
"resolutionEvidence": "docs_rs_latest_published_version_and_official_interface_tag",
|
||||
"workspaceCargoResolutionStatus": "confirmed_by_operator_cargo_tree_2026-07-16",
|
||||
"features": ["borsh"],
|
||||
"bincodeEnabled": false,
|
||||
"officialTag": "interface@v2.0.0",
|
||||
"releaseCommit": "250da0e21a9853a7afddba85658ed8a9119c71c9",
|
||||
"releaseDate": "2025-08-08",
|
||||
"instructionSource": "https://github.com/solana-program/associated-token-account/blob/interface@v2.0.0/interface/src/instruction.rs",
|
||||
"addressSource": "https://github.com/solana-program/associated-token-account/blob/interface@v2.0.0/interface/src/address.rs",
|
||||
"publishedDocumentation": "https://docs.rs/spl-associated-token-account-interface/2.0.0/spl_associated_token_account_interface/"
|
||||
},
|
||||
"processor": {
|
||||
"auditedRelease": "program@v8.0.0",
|
||||
"releaseCommit": "0b867b5",
|
||||
"releaseDate": "2025-10-29",
|
||||
"source": "https://github.com/solana-program/associated-token-account/blob/program@v8.0.0/program/src/processor.rs",
|
||||
"deploymentBinaryEquality": {"localnet": "not_tested", "devnet": "not_proven", "mainnet": "not_proven"},
|
||||
"note": "Interface publication, processor source, cluster deployment and successful execution are tracked independently."
|
||||
},
|
||||
"wire": {
|
||||
"format": "Borsh enum with unit variants encoded as one u8 ordinal",
|
||||
"strictSuffixPolicy": "reject",
|
||||
"legacyEmptyCreate": {"decodeSupport": true, "builderEmits": false, "encodingHex": "", "status": "historical_runtime_and_transaction_parser_compatibility_to_be_reconfirmed_by_cluster_corpus"},
|
||||
"unknownOrTruncatedPolicy": "bounded diagnostic without lifecycle projection"
|
||||
},
|
||||
"pdaDerivation": {
|
||||
"programId": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL",
|
||||
"seedOrder": ["wallet_address_bytes[32]", "token_program_id_bytes[32]", "mint_address_bytes[32]"],
|
||||
"algorithm": "Pubkey::find_program_address",
|
||||
"validationPolicy": "preserve observed address and report exact match or mismatch; never replace an observed account silently",
|
||||
"differentialVectors": [
|
||||
{"wallet": "11111111111111111111111111111111", "mint": "So11111111111111111111111111111111111111112", "tokenProgramId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "expectedAta": "aqxoAhCwpy3oB1BpNw9hL1HdLYLgPpbPjzxDrrQj3Fs", "expectedBump": 254, "evidence": "PublicKey.findProgramAddressSync with the canonical official seed order"},
|
||||
{"wallet": "11111111111111111111111111111111", "mint": "So11111111111111111111111111111111111111112", "tokenProgramId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "expectedAta": "2sZUUBGq1i6aE47ZoxCaCW89jmYm2EXLPPmNMgMDXHMS", "expectedBump": 250, "evidence": "PublicKey.findProgramAddressSync with the canonical official seed order"}
|
||||
]
|
||||
},
|
||||
"limits": {"exactVariantCount": 3, "createRequiredAccountCount": 6, "recoverNestedRequiredAccountCount": 7, "maximumDiagnosticBytes": 256, "extraAccounts": "preserved_and_reported; never assigned an invented role"},
|
||||
"executionPreflight": {
|
||||
"clusters": ["localnet", "devnet"],
|
||||
"programIds": ["associated_token_program", "selected_token_program", "system_program_for_creation"],
|
||||
"stateChecks": ["mint_exists_initialized_and_owned_by_selected_token_program", "canonical_pda_derivation", "strict_create_absence", "idempotent_absence_or_existing_account_compatibility", "recover_nested_three_account_relationships", "required_signers_available", "payer_balance_covers_rent_plus_fee_ceiling"],
|
||||
"token2022Boundary": "base Mint and Token Account prefixes are validated while suffix extensions remain opaque; simulation determines extension-dependent runtime acceptance and exact account size",
|
||||
"simulation": "required_before_signing_or_sending",
|
||||
"defaultMode": "dry_run"
|
||||
},
|
||||
"surfaceEquality": {"matrixVariantCount": 3, "officialInterfaceVariantCount": 3, "compiledDecoderCoverageStatus": "three_exact_compiled_instruction_declarations_operator_validated", "compiledExecutorCapabilityStatus": "three_exact_current_capabilities_operator_validated", "requiredFinalInvariant": "matrix == official interface enum == decoder coverage == executor capability declarations"},
|
||||
"instructions": [
|
||||
{
|
||||
"name": "create",
|
||||
"discriminant": 0,
|
||||
"canonicalEncodingHex": "00",
|
||||
"acceptedDecodeEncodingsHex": ["", "00"],
|
||||
"status": "current_with_legacy_empty_wire_compatibility",
|
||||
"accounts": [
|
||||
{"position": 0, "role": "funding_account", "signer": true, "writable": true, "expectedProgram": "system_account"},
|
||||
{"position": 1, "role": "associated_token_account", "signer": false, "writable": true, "expectedProgram": "derived_pda_before_creation_or_supported_token_program_after_creation"},
|
||||
{"position": 2, "role": "wallet_owner", "signer": false, "writable": false, "expectedProgram": "unconstrained_wallet_address"},
|
||||
{"position": 3, "role": "mint", "signer": false, "writable": false, "expectedProgram": "account_owned_by_token_program_at_runtime"},
|
||||
{"position": 4, "role": "system_program", "signer": false, "writable": false, "expectedAddress": "11111111111111111111111111111111"},
|
||||
{"position": 5, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]}
|
||||
],
|
||||
"addressRules": ["associated_token_account = PDA(wallet_owner, token_program, mint)", "observed associated_token_account is retained on mismatch"],
|
||||
"tokenProgramDifferences": {"classic": "create and initialize a classic token account", "token2022": "size is obtained from the Token-2022 mint and immutable-owner initialization is performed before account initialization; extensions themselves remain out of decoder scope"},
|
||||
"runtimeRules": ["fails if the associated account already exists", "funding account pays rent and creation cost", "mint and created account must be coherent with the selected token program"],
|
||||
"event": "spl_associated_token_account.create",
|
||||
"authorizedProjections": ["token_accounts:ata_created"],
|
||||
"forbiddenProjections": ["token_balance_snapshot", "duplicate_spl_token_initialize_account_cpi", "token_2022_extension_fact"],
|
||||
"officialBuilder": {"available": true, "function": "create_associated_token_account", "canonicalWireOnly": true},
|
||||
"executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.create", "builder": "create_associated_token_account", "simulation": "required", "defaultMode": "dry_run"},
|
||||
"proofs": {"synthetic": ["canonical_00", "legacy_empty", "classic_token_program", "token_2022_program", "wrong_ata", "missing_account", "extra_account", "duplicate_account", "wrong_flags", "unknown_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": "not_tested", "mainnetSignatures": []}
|
||||
},
|
||||
{
|
||||
"name": "create_idempotent",
|
||||
"discriminant": 1,
|
||||
"canonicalEncodingHex": "01",
|
||||
"acceptedDecodeEncodingsHex": ["01"],
|
||||
"status": "current",
|
||||
"accounts": [
|
||||
{"position": 0, "role": "funding_account", "signer": true, "writable": true, "expectedProgram": "system_account"},
|
||||
{"position": 1, "role": "associated_token_account", "signer": false, "writable": true, "expectedProgram": "derived_pda_or_supported_token_program"},
|
||||
{"position": 2, "role": "wallet_owner", "signer": false, "writable": false},
|
||||
{"position": 3, "role": "mint", "signer": false, "writable": false},
|
||||
{"position": 4, "role": "system_program", "signer": false, "writable": false, "expectedAddress": "11111111111111111111111111111111"},
|
||||
{"position": 5, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]}
|
||||
],
|
||||
"addressRules": ["associated_token_account = PDA(wallet_owner, token_program, mint)", "existing account must retain the selected token program, wallet owner and mint"],
|
||||
"tokenProgramDifferences": {"classic": "existing classic account is accepted only when owner and mint match", "token2022": "existing Token-2022 account is accepted only when owner and mint match; no extension semantics are inferred"},
|
||||
"runtimeRules": ["creates when absent", "succeeds without recreating when an initialized compatible ATA already exists", "fails when an existing account has a different wallet owner, mint or account owner program"],
|
||||
"event": "spl_associated_token_account.create_idempotent",
|
||||
"authorizedProjections": ["token_accounts:ata_created_or_reused_idempotently"],
|
||||
"forbiddenProjections": ["token_balance_snapshot", "duplicate_spl_token_initialize_account_cpi", "token_2022_extension_fact"],
|
||||
"officialBuilder": {"available": true, "function": "create_associated_token_account_idempotent", "canonicalWireOnly": true},
|
||||
"executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.create_idempotent", "builder": "create_associated_token_account_idempotent", "simulation": "required", "defaultMode": "dry_run"},
|
||||
"proofs": {"synthetic": ["canonical_01", "absent_account", "compatible_existing_account", "conflicting_existing_owner", "conflicting_existing_mint", "conflicting_existing_program", "classic_token_program", "token_2022_program", "wrong_ata", "wrong_flags", "unexpected_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": ["5Vw4QFpMa8oJ8MsLrvDg4RFzxqUh27mTFRFzzwJFoA4uAry8rZQc6GP92aidzWugn3CeZ5NYNxDp7tZ6bh8XyN1P", "2H9VfhDySdXMteazm2P3s2fTttJBQzMJinQtKqsLtkjDg7zbFFgiWG81UxRQF4va3bbR2pUV4QGo2qErisR8CPbP", "3wWTkx45LoDR3UaU8VUfsWxwHuLFyJyhrnDrmijCa7pXPdmJKz3rQ1LdyDvb78dvNRiKqw5ee7HuZ6Gjj1Gpfv6q", "bMMjoHcwbaTvLXerdQ4kbGXGb7wydiTyvSEwwmoKKLQcRWWBzqGUMYHZztonNnER1iWZ7yKp9p3neeMhypX4QAJ", "4RZKA3KwEbSqgU8UnemTgvd3fBgJmoSK2nmaaTNP8nJGTVh6DrFwrHVZYCBvLFvhvzUdi9p1KRFe4hHqYt7JbVSG"], "mainnetSignatures": ["2F9RvuCkevfJN7ju252hAWoCE58wagCXCWHQF9Wsdtsxvp3HcuNJQFCwU7zGqe3z3b8pv3vPp4xY5EH7KLcaHQCY"], "realCorpusStatus": "controlled classic and Token-2022 CreateIdempotent creation then compatible-existing reuse confirmed, materialized once per signature and replayed idempotently; search-discovered Token-2022 outer transaction remains additional corpus"}
|
||||
},
|
||||
{
|
||||
"name": "recover_nested",
|
||||
"discriminant": 2,
|
||||
"canonicalEncodingHex": "02",
|
||||
"acceptedDecodeEncodingsHex": ["02"],
|
||||
"status": "current_recovery_operation",
|
||||
"accounts": [
|
||||
{"position": 0, "role": "nested_associated_token_account", "signer": false, "writable": true},
|
||||
{"position": 1, "role": "nested_mint", "signer": false, "writable": false},
|
||||
{"position": 2, "role": "wallet_nested_mint_associated_token_account", "signer": false, "writable": true},
|
||||
{"position": 3, "role": "owner_associated_token_account", "signer": false, "writable": false},
|
||||
{"position": 4, "role": "owner_mint", "signer": false, "writable": false},
|
||||
{"position": 5, "role": "wallet_owner", "signer": true, "writable": true},
|
||||
{"position": 6, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]}
|
||||
],
|
||||
"addressRules": ["owner_associated_token_account = PDA(wallet_owner, token_program, owner_mint)", "nested_associated_token_account = PDA(owner_associated_token_account, token_program, nested_mint)", "wallet_nested_mint_associated_token_account = PDA(wallet_owner, token_program, nested_mint)"],
|
||||
"tokenProgramDifferences": {"classic": "transfer the complete nested balance and close the nested classic token account", "token2022": "same ATA address relationships through Token-2022 CPIs; extension-specific transfer or close restrictions can prevent recovery and are not decoded as ATA facts"},
|
||||
"runtimeRules": ["all three derived addresses must match their observed accounts", "owner and nested token accounts must be initialized and coherent with their respective mints", "wallet owner must sign", "complete nested token balance is moved to the wallet destination ATA", "nested account is closed and its lamports are returned to the wallet"],
|
||||
"event": "spl_associated_token_account.recover_nested",
|
||||
"authorizedProjections": ["token_accounts:nested_ata_recovered", "risk:nested_ata_anti_pattern_recovered"],
|
||||
"forbiddenProjections": ["duplicate_spl_token_transfer_cpi", "duplicate_spl_token_close_account_cpi", "token_balance_snapshot", "token_2022_extension_fact"],
|
||||
"officialBuilder": {"available": true, "function": "recover_nested", "canonicalWireOnly": true},
|
||||
"executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.recover_nested", "builder": "recover_nested", "simulation": "required", "defaultMode": "dry_run"},
|
||||
"proofs": {"synthetic": ["canonical_02", "classic_token_program", "token_2022_program", "valid_three_pda_relationships", "wrong_nested_ata", "wrong_destination_ata", "wrong_owner_ata", "owner_and_nested_mints_not_inverted", "missing_account", "extra_account", "duplicate_account", "wrong_flags", "unexpected_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": ["37LG6GdMRp5ECG8qf1RSYxmriJk75UtCwqwzXZWpArebkKo6BQvGhYEnbDh2A6KWiUaHK28Zy1gGGZ5zihvrHskM"], "mainnetSignatures": [], "realCorpusStatus": "controlled classic RecoverNested confirmed with positive raw balance transfer, nested ATA closure, owner and destination post-validation, two distinct ATA lifecycle and risk projections, and idempotent second replay", "controlledDevnetFixture": {"walletOwner": "DwuAapPLg6pEJ4QhmxvPg8rzkavmYdg5FW4dfEMcg8B2", "ownerMint": "So11111111111111111111111111111111111111112", "ownerAssociatedTokenAccount": "3tdXq1W7vEbbkxQmT1r2fc349uYti98zpwLKhp7FFbF5", "nestedMint": "3fd5CqP1w4Y1xeMoAe3apA7Jf5SbL3vboxv7ihyXhC9A", "nestedAssociatedTokenAccount": "AAyvGeKpq6i3kxMcScJLcN2u5iyAsspr731LhgXygti5", "walletNestedMintAssociatedTokenAccount": "69TP2QoSx2inueFAEb6npjumH5o3wGJQDSwwAJLLfyWp", "rawAmountTransferred": "1000000000", "nestedAccountClosed": true, "destinationUiBalanceAfter": "1", "materializedOutputCount": 2}}
|
||||
}
|
||||
],
|
||||
"offlineCorpus": {
|
||||
"status": "implemented_and_operator_validated_in_decoder_unit_tests",
|
||||
"wire": ["create_00", "legacy_empty_create", "create_idempotent_01", "recover_nested_02", "unknown_tag", "rejected_suffix"],
|
||||
"tokenPrograms": ["spl_token_classic", "token_2022"],
|
||||
"locations": ["outer", "inner_cpi"],
|
||||
"transactionOutcomes": ["committed", "failed_uncommitted_intent"],
|
||||
"accountShapes": ["canonical", "missing", "extra", "duplicate", "wrong_flags", "wrong_derived_address", "unsupported_token_program", "wrong_system_program"],
|
||||
"derivation": ["create_single_pda", "recover_nested_three_independent_pda_relationships", "official_helper_differential_vectors"],
|
||||
"limits": "diagnostic count and payload prefix are bounded; no arbitrary payload or account repair"
|
||||
},
|
||||
"materializationContract": {
|
||||
"committedOnly": true,
|
||||
"idempotenceIdentity": "signature + instruction_path + program_id + normalized_operation + ata",
|
||||
"projectionOwners": [
|
||||
{
|
||||
"crate": "kb_materializer_token_accounts",
|
||||
"facts": [
|
||||
"ata_created",
|
||||
"ata_created_or_reused_idempotently",
|
||||
"nested_ata_recovered"
|
||||
],
|
||||
"justification": "ATA creation and recovery are stable token-account lifecycle facts; this crate already owns instruction-level token-account mutations."
|
||||
},
|
||||
{
|
||||
"crate": "kb_materializer_risk",
|
||||
"facts": [
|
||||
"nested_ata_anti_pattern_recovered"
|
||||
],
|
||||
"justification": "A committed RecoverNested proves the distinct stable risk fact that a nested ATA anti-pattern existed and was recovered; no score or speculative severity is added."
|
||||
}
|
||||
],
|
||||
"explicitNoProjection": [
|
||||
{
|
||||
"crate": "kb_materializer_lifecycle",
|
||||
"reason": "Native program lifecycle remains owned here; duplicating ATA lifecycle would overlap kb_materializer_token_accounts."
|
||||
},
|
||||
{
|
||||
"crate": "kb_materializer_admin",
|
||||
"reason": "ATA instructions do not change a mint authority, freeze authority, close authority or multisig configuration."
|
||||
},
|
||||
{
|
||||
"crate": "kb_materializer_fees",
|
||||
"reason": "ATA wire does not prove an exact rent or network-fee amount; core balance changes and execution validation retain those facts."
|
||||
}
|
||||
],
|
||||
"implementationStatus": "pre_004 executor and stateful preflight operator validated; pre_005 classic and Token-2022 CreateIdempotent Devnet orchestration plus Tauri validated; pre_006 classic RecoverNested Devnet orchestration validated",
|
||||
"idempotentOutcomeLimit": "CreateIdempotent parent proves successful ensure semantics but cannot distinguish created from reused without sibling CPI correlation; the projection preserves created_or_reused and invents neither boolean.",
|
||||
"parentChildOwnership": "ATA parent owns only associated-address lifecycle and the distinct nested-ATA risk fact; SPL Token or Token-2022 CPI observations own token initialize, transfer and close mutations.",
|
||||
"noSnapshotClaim": true,
|
||||
"databaseMigrationStatus": "not_justified"
|
||||
},
|
||||
"executionContract": {"simulationRequired": true, "dryRunDefault": true, "clusterPolicyOwnedByCommonLayers": true, "rentAndFeeCeilingRequired": true, "signerDeduplicationDoesNotChangeMetaOrder": true, "confirmedStateReadRetryBound": 20, "canonicalHydrationRpcRetryBoundPerAttempt": 2, "postValidation": {"create": "ATA exists, is owned by selected token program, and contains expected wallet owner and mint", "createIdempotent": "same invariant whether the ATA was created or reused", "recoverNested": "owner ATA and destination remain coherent, nested ATA is absent, and no balance value is invented by ATA projection"}},
|
||||
"deploymentAudit": {
|
||||
"localnet": {"status": "not_tested", "evidence": []},
|
||||
"devnet": {"status": "classic and Token-2022 CreateIdempotent creation and compatible-existing reuse confirmed; classic RecoverNested positive-balance recovery confirmed", "evidence": ["5Vw4QFpMa8oJ8MsLrvDg4RFzxqUh27mTFRFzzwJFoA4uAry8rZQc6GP92aidzWugn3CeZ5NYNxDp7tZ6bh8XyN1P", "2H9VfhDySdXMteazm2P3s2fTttJBQzMJinQtKqsLtkjDg7zbFFgiWG81UxRQF4va3bbR2pUV4QGo2qErisR8CPbP", "3wWTkx45LoDR3UaU8VUfsWxwHuLFyJyhrnDrmijCa7pXPdmJKz3rQ1LdyDvb78dvNRiKqw5ee7HuZ6Gjj1Gpfv6q", "bMMjoHcwbaTvLXerdQ4kbGXGb7wydiTyvSEwwmoKKLQcRWWBzqGUMYHZztonNnER1iWZ7yKp9p3neeMhypX4QAJ", "4RZKA3KwEbSqgU8UnemTgvd3fBgJmoSK2nmaaTNP8nJGTVh6DrFwrHVZYCBvLFvhvzUdi9p1KRFe4hHqYt7JbVSG", "37LG6GdMRp5ECG8qf1RSYxmriJk75UtCwqwzXZWpArebkKo6BQvGhYEnbDh2A6KWiUaHK28Zy1gGGZ5zihvrHskM"], "controlledToken2022Fixture": {"mint": "3DxKAUfCZkR9oRfMXeRNpiVL464eKbTieymBrVhsoKTE", "mintOwner": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "mintBaseLength": 82, "walletOwner": "DwuAapPLg6pEJ4QhmxvPg8rzkavmYdg5FW4dfEMcg8B2", "associatedTokenAccount": "6WfCJPt7GZQyMJAUS1AMHJrD7Vid8e9xUEQVgi5Q9C9Y", "accountLength": 170, "accountExtension": "immutable_owner", "creationSlot": 476702448, "existingReuseSlot": 476703343}},
|
||||
"mainnet": {"status": "search_discovered_token_2022_create_idempotent_not_yet_replayed", "evidence": ["2F9RvuCkevfJN7ju252hAWoCE58wagCXCWHQF9Wsdtsxvp3HcuNJQFCwU7zGqe3z3b8pv3vPp4xY5EH7KLcaHQCY"]},
|
||||
"rateLimitRecovery": "preserve confirmed signatures and resume bounded hydration; never recreate accounts blindly after HTTP 429"
|
||||
},
|
||||
"nonClaims": [
|
||||
"No general Token-2022 instruction or extension decoding is implemented in 0.4.5.",
|
||||
"No Token balance or final account snapshot is reconstructed from ATA instructions.",
|
||||
"No cluster binary equality is inferred from interface publication.",
|
||||
"The controlled Token-2022 proof covers ATA lifecycle only and does not claim general Token-2022 instruction or extension decoding.",
|
||||
"No dedicated SQL table is justified by this audit tranche."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"file": "docs/SPL_ELGAMAL_REGISTRY_MATRIX.json",
|
||||
"version": 1,
|
||||
"programId": "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg",
|
||||
"interface": {
|
||||
"crate": "spl-elgamal-registry-interface",
|
||||
"resolvedVersion": "0.2.1",
|
||||
"auditedCommitPrefix": "e18f9c6"
|
||||
},
|
||||
"technicalBoundaries": {
|
||||
"token2022ProgramId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb",
|
||||
"zkElgamalProofProgramId": "ZkE1Gama1Proof11111111111111111111111111111",
|
||||
"registryDecodesProofInstructions": false,
|
||||
"token2022DecoderOwnsRegistryInstructions": false
|
||||
},
|
||||
"pda": {
|
||||
"seedUtf8": "elgamal-registry",
|
||||
"ownerAddressIsSeed": true,
|
||||
"programIdIsDerivationProgram": true
|
||||
},
|
||||
"instructions": [
|
||||
{
|
||||
"name": "create_registry",
|
||||
"tag": 0,
|
||||
"wire": {
|
||||
"exactLengthBytes": 2,
|
||||
"fields": [
|
||||
{
|
||||
"name": "tag",
|
||||
"type": "u8",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"name": "proofInstructionOffset",
|
||||
"type": "i8"
|
||||
}
|
||||
]
|
||||
},
|
||||
"accounts": [
|
||||
{
|
||||
"position": 0,
|
||||
"role": "registry_account",
|
||||
"writable": true,
|
||||
"signer": false
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"role": "wallet_owner",
|
||||
"writable": false,
|
||||
"signer": true
|
||||
},
|
||||
{
|
||||
"position": 2,
|
||||
"role": "system_program",
|
||||
"writable": false,
|
||||
"signer": false
|
||||
},
|
||||
{
|
||||
"position": 3,
|
||||
"role": "instructions_sysvar_or_proof_context",
|
||||
"writable": false,
|
||||
"signer": false
|
||||
}
|
||||
],
|
||||
"builder": "create_registry",
|
||||
"status": "current"
|
||||
},
|
||||
{
|
||||
"name": "update_registry",
|
||||
"tag": 1,
|
||||
"wire": {
|
||||
"exactLengthBytes": 2,
|
||||
"fields": [
|
||||
{
|
||||
"name": "tag",
|
||||
"type": "u8",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"name": "proofInstructionOffset",
|
||||
"type": "i8"
|
||||
}
|
||||
]
|
||||
},
|
||||
"accounts": [
|
||||
{
|
||||
"position": 0,
|
||||
"role": "registry_account",
|
||||
"writable": true,
|
||||
"signer": false
|
||||
},
|
||||
{
|
||||
"position": 1,
|
||||
"role": "instructions_sysvar_or_proof_context",
|
||||
"writable": false,
|
||||
"signer": false
|
||||
},
|
||||
{
|
||||
"position": 2,
|
||||
"role": "registry_owner",
|
||||
"writable": false,
|
||||
"signer": true
|
||||
}
|
||||
],
|
||||
"builder": "update_registry",
|
||||
"status": "current"
|
||||
}
|
||||
],
|
||||
"proofLocationSemantics": {
|
||||
"offsetZero": "context_state_account",
|
||||
"offsetNonZero": "relative_zk_proof_instruction",
|
||||
"publishedInlineBuilderOffset": 1
|
||||
},
|
||||
"decoder": {
|
||||
"crate": "kb_decoder_spl_elgamal_registry",
|
||||
"coverageEqualityRequired": true,
|
||||
"unknownTagPolicy": "failed",
|
||||
"trailingBytesPolicy": "failed",
|
||||
"failedTransactionCommitted": false
|
||||
}
|
||||
}
|
||||
266
migration/khadhroony-bot2-reference/docs/SPL_MEMO_MATRIX.json
Normal file
266
migration/khadhroony-bot2-reference/docs/SPL_MEMO_MATRIX.json
Normal file
@@ -0,0 +1,266 @@
|
||||
{
|
||||
"file": "docs/SPL_MEMO_MATRIX.json",
|
||||
"version": 8,
|
||||
"matrixVersion": 4,
|
||||
"scope": "SPL Memo v1, v3 et v4",
|
||||
"interface": {
|
||||
"crate": "spl-memo-interface",
|
||||
"requestedVersion": "^2.1",
|
||||
"auditedPublishedVersion": "2.1.0",
|
||||
"lockfilePresentInInputArchive": false,
|
||||
"builder": "spl_memo_interface::instruction::build_memo",
|
||||
"builderAcceptsExplicitProgramId": true,
|
||||
"wire": "octets exacts du message, sans préfixe ni discriminator",
|
||||
"accountMetas": "ordre fourni, readonly, signer=true"
|
||||
},
|
||||
"generations": [
|
||||
{
|
||||
"generation": "v1",
|
||||
"programId": "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo",
|
||||
"surfaceCode": "spl_memo_v1",
|
||||
"status": "historique immuable",
|
||||
"instructionData": "octets UTF-8 bruts",
|
||||
"emptyPayload": "accepte",
|
||||
"accounts": "acceptes mais ignores par le runtime v1",
|
||||
"signerRequirement": "aucune",
|
||||
"accountOrder": "sans effet runtime mais conserve par le decodeur",
|
||||
"duplicateAccounts": "sans effet runtime mais conserves par le decodeur",
|
||||
"runtimeLogs": [],
|
||||
"utf8Failure": "InvalidInstructionData",
|
||||
"builderOfficial": "builder generique 2.1.0 avec program ID v1 explicite",
|
||||
"currentInvocation": {
|
||||
"mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche",
|
||||
"devnet": "non verifie",
|
||||
"localnet": "binaire historique non embarque"
|
||||
},
|
||||
"realCorpusSignatures": [
|
||||
"imGSCgR83S3tFPTcZPqWLWRcL1piFX1vkYMRVsRdJeBM4p7viDb4V6Hq2x51ovTmbMgGBMdn8kyeNiAg9Ph6MfE",
|
||||
"65J2cmiZAB5h7B3kyzhponNZQzMCbbxVn1A8cydvKKq1FYxYLEarG7ydjuuxFatXCG7Foju21XMUdRkT96jYBS3C"
|
||||
]
|
||||
},
|
||||
{
|
||||
"generation": "v3",
|
||||
"programId": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr",
|
||||
"surfaceCode": "spl_memo_v3",
|
||||
"status": "historique encore largement observe",
|
||||
"instructionData": "octets UTF-8 bruts",
|
||||
"emptyPayload": "accepte",
|
||||
"accounts": "zero ou plusieurs; chaque compte fourni doit etre signer",
|
||||
"signerRequirement": "tous les comptes fournis",
|
||||
"accountOrder": "ordre de parcours et de logs conserve",
|
||||
"duplicateAccounts": "parcourus et journalises dans leur ordre; aucun rejet dedie",
|
||||
"runtimeLogs": [
|
||||
"Signed by <pubkey> pour chaque compte signer",
|
||||
"Invalid UTF-8, from byte <offset> en cas d'erreur",
|
||||
"Memo (len <octets>): <texte> en cas de succes"
|
||||
],
|
||||
"utf8Failure": "InvalidInstructionData",
|
||||
"missingSignerFailure": "MissingRequiredSignature avant validation UTF-8",
|
||||
"builderOfficial": "builder generique 2.1.0 avec program ID v3 explicite",
|
||||
"currentInvocation": {
|
||||
"mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche",
|
||||
"devnet": "non verifie",
|
||||
"localnet": "possible avec binaire officiel, non execute"
|
||||
},
|
||||
"realCorpusSignatures": [
|
||||
"2DkiJJVAyJ1aJHn2ff8JhKA5C8wbakw76LZvgxmcQ1qtHNDprGvA6TntXXK5FSAeRMZyrDt8ujAkGMgBVcEhjsmT",
|
||||
"3G18ZUSP29Aw12NwnAYRYgix87fa559nMzaBa3LSkCQZjJ5AYEjSDnQHLbgU3QZax2efmeJHdxu1LamSCxCJzg37"
|
||||
]
|
||||
},
|
||||
{
|
||||
"generation": "v4",
|
||||
"programId": "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH",
|
||||
"surfaceCode": "spl_memo_v4",
|
||||
"status": "generation courante publiee avec implementation Pinocchio",
|
||||
"instructionData": "octets UTF-8 bruts",
|
||||
"emptyPayload": "accepte",
|
||||
"accounts": "zero ou plusieurs; chaque compte fourni doit etre signer",
|
||||
"signerRequirement": "tous les comptes fournis",
|
||||
"accountOrder": "ordre de parcours et de logs conserve",
|
||||
"duplicateAccounts": "aucun rejet dedie prouve; ordre et doublons conserves",
|
||||
"runtimeLogs": [
|
||||
"signataires puis diagnostic UTF-8 ou memo selon l'implementation officielle"
|
||||
],
|
||||
"utf8Failure": "InvalidInstructionData",
|
||||
"missingSignerFailure": "MissingRequiredSignature avant validation UTF-8",
|
||||
"builderOfficial": "builder generique 2.1.0 avec program ID v4 explicite",
|
||||
"currentInvocation": {
|
||||
"mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche",
|
||||
"devnet": "simulation et trois executions v4 confirmees avec post-validation complete le 2026-07-15",
|
||||
"localnet": "binaire officiel disponible, non execute"
|
||||
},
|
||||
"realCorpusSignatures": [
|
||||
"2vgR56q2NR6ehu1uNnpt3te3RqhX2sXryXi4SpBHKUyVaeERr1HLYSLvDFRzd3eDeNejjLX9hVZJZ5GvtsTtBr1c",
|
||||
"53aHhj6rt77hCkadHedpWS7GstdER5XqQrHDutZ7gAQrDvAxcDcktrUtwxZcCbTV2FnLzELqfwoJgvabjdLPWkQ5",
|
||||
"3NxmPdF9W3GBiD8qF2VzvUhEDrTM48EEkaosTtzVceRhQc4sGEojDFppBsXYgqK9GCrCUo4wzAWciprsqZEmRyee",
|
||||
"2njzDJUBar6TTPPcuwEDdkiW8kVLUsZXfdvUQNP7TC6La44E4kZtLFfdJ1Q8nPNJbKa9Vuby4TtiuLX2rQQTa65C"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoderContract": {
|
||||
"maximumRetainedPayloadBytes": 4096,
|
||||
"diagnosticHexPrefixBytes": 32,
|
||||
"completeTextRetainedWhenUtf8Valid": true,
|
||||
"failedTransactionRuntimeValidation": "not_proven_transaction_failed",
|
||||
"failedTransactionCommitted": false,
|
||||
"unknownProgramProducesObservation": false
|
||||
},
|
||||
"materializationContract": {
|
||||
"crate": "kb_materializer_transaction_annotations",
|
||||
"processorName": "transaction_annotations",
|
||||
"materializedFamily": "transaction_annotation",
|
||||
"acceptedEntries": ["add_memo", "memo_intent", "invalid_memo_attempt"],
|
||||
"projectedEntry": "add_memo",
|
||||
"transactionPolicy": "SuccessfulCommittedOnly",
|
||||
"failedOrUncommittedOutput": false,
|
||||
"store": "kb_sol_mat_events",
|
||||
"newMigrationRequired": false,
|
||||
"idempotence": "ledger processor/version/input hash plus deterministic output key",
|
||||
"readModel": {
|
||||
"contract": "DecodePipelineStore.list_materialized_events",
|
||||
"demo": "demo_decode_replay transaction annotation journal",
|
||||
"boundedMaximumRows": 500,
|
||||
"filters": ["processor exact", "family exact", "partial signature"],
|
||||
"visualization": "table journal, not OHLC"
|
||||
},
|
||||
"decodedOnlyFields": [
|
||||
"complete accounts and writable flags",
|
||||
"payload hex diagnostic prefix",
|
||||
"UTF-8 error offset",
|
||||
"transaction error",
|
||||
"decoder proof"
|
||||
]
|
||||
},
|
||||
"executorContract": {
|
||||
"crate": "kb_executor_spl_memo",
|
||||
"operationCode": "spl_memo.add_memo",
|
||||
"typedIntent": "SplMemoExecutionIntent",
|
||||
"officialBuilder": "spl_memo_interface::instruction::build_memo",
|
||||
"supportedGenerations": ["v4"],
|
||||
"decodeOnlyGenerations": ["v1", "v3"],
|
||||
"maximumMessageBytes": 566,
|
||||
"maximumSignerOccurrences": 32,
|
||||
"preserveSignerOrderAndDuplicates": true,
|
||||
"inventWritableAccounts": false,
|
||||
"requestedSpendLamports": 0,
|
||||
"simulationRequired": true,
|
||||
"dryRunDefault": true,
|
||||
"allClustersConstructible": true,
|
||||
"historicalGenerationSendEnabled": false,
|
||||
"mainnetSendGovernedByCommonPolicy": true,
|
||||
"clusterAvailabilityProvenBySimulation": true,
|
||||
"demoCluster": "devnet",
|
||||
"demoGeneration": "v4",
|
||||
"postExecutionValidation": [
|
||||
"canonical_insert",
|
||||
"core_extraction",
|
||||
"memo_decode_replay",
|
||||
"transaction_annotation_materialization"
|
||||
]
|
||||
},
|
||||
"requiredCorpusCases": [
|
||||
"empty_payload",
|
||||
"ascii",
|
||||
"unicode_multibyte",
|
||||
"near_practical_transaction_limit",
|
||||
"invalid_utf8",
|
||||
"zero_signer",
|
||||
"one_signer",
|
||||
"multiple_signers",
|
||||
"non_signer_account",
|
||||
"duplicate_accounts",
|
||||
"additional_writable_account",
|
||||
"outer_instruction",
|
||||
"inner_instruction",
|
||||
"successful_transaction",
|
||||
"failed_transaction",
|
||||
"malformed_base64",
|
||||
"payload_over_decoder_limit"
|
||||
],
|
||||
"mainnetReplayValidation": {
|
||||
"date": "2026-07-14",
|
||||
"acquisition": {
|
||||
"programCampaigns": 3,
|
||||
"canonicalTransactionsInserted": 300,
|
||||
"missingTransactions": 0
|
||||
},
|
||||
"coreExtraction": {
|
||||
"selected": 300,
|
||||
"extracted": 300,
|
||||
"failed": 0
|
||||
},
|
||||
"generations": [
|
||||
{
|
||||
"generation": "v1",
|
||||
"decodedInstructions": 102,
|
||||
"materializedAnnotations": 69,
|
||||
"refusedUncommittedIntents": 33
|
||||
},
|
||||
{
|
||||
"generation": "v3",
|
||||
"decodedInstructions": 439,
|
||||
"materializedAnnotations": 422,
|
||||
"refusedUncommittedIntents": 17,
|
||||
"secondNonForcedReplaySelected": 0
|
||||
},
|
||||
{
|
||||
"generation": "v4",
|
||||
"decodedInstructions": 100,
|
||||
"materializedAnnotations": 100,
|
||||
"refusedUncommittedIntents": 0
|
||||
}
|
||||
],
|
||||
"decodeFailures": 0,
|
||||
"unmatchedInstructions": 0,
|
||||
"operationalErrorLogEntries": 0
|
||||
},
|
||||
"devnetExecutionValidation": {
|
||||
"date": "2026-07-15",
|
||||
"generation": "v4",
|
||||
"programId": "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH",
|
||||
"simulationOnlyValidated": true,
|
||||
"submittedTransactions": 3,
|
||||
"confirmedTransactions": 3,
|
||||
"canonicalTransactionsInserted": 3,
|
||||
"coreTransactionsExtracted": 3,
|
||||
"decodedInstructions": 3,
|
||||
"materializedAnnotations": 3,
|
||||
"annotationPayloadLengthBytes": 34,
|
||||
"secondReplaySkipped": 3,
|
||||
"secondReplayNewMaterializedOutputs": 0,
|
||||
"decodeFailures": 0,
|
||||
"materializationRefusals": 0,
|
||||
"signatures": [
|
||||
"53aHhj6rt77hCkadHedpWS7GstdER5XqQrHDutZ7gAQrDvAxcDcktrUtwxZcCbTV2FnLzELqfwoJgvabjdLPWkQ5",
|
||||
"3NxmPdF9W3GBiD8qF2VzvUhEDrTM48EEkaosTtzVceRhQc4sGEojDFppBsXYgqK9GCrCUo4wzAWciprsqZEmRyee",
|
||||
"2njzDJUBar6TTPPcuwEDdkiW8kVLUsZXfdvUQNP7TC6La44E4kZtLFfdJ1Q8nPNJbKa9Vuby4TtiuLX2rQQTa65C"
|
||||
]
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"kind": "official_interface_source",
|
||||
"url": "https://docs.rs/spl-memo-interface/2.1.0/src/spl_memo_interface/instruction.rs.html"
|
||||
},
|
||||
{
|
||||
"kind": "official_interface_ids",
|
||||
"url": "https://docs.rs/spl-memo-interface/2.1.0/src/spl_memo_interface/lib.rs.html"
|
||||
},
|
||||
{
|
||||
"kind": "official_v1_historical_source",
|
||||
"url": "https://github.com/solana-program/memo/commit/479839141a99b7d0c3a35bc648c0fa1e4704ea3e"
|
||||
},
|
||||
{
|
||||
"kind": "official_v3_published_source",
|
||||
"url": "https://docs.rs/spl-memo/3.0.0/src/spl_memo/processor.rs.html"
|
||||
},
|
||||
{
|
||||
"kind": "official_current_source",
|
||||
"url": "https://github.com/solana-program/memo/blob/main/program/src/processor.rs"
|
||||
}
|
||||
],
|
||||
"documentedLimitations": [
|
||||
"v1 et v3 restent decodees depuis un corpus Mainnet reel et leur wire officiel reste audite, mais l'executeur les classe decode-only et ne construit que v4",
|
||||
"l'invocabilite Localnet depend du deploiement explicite du binaire de generation demande",
|
||||
"les differences de cout v3/v4 ne sont pas transformees en garantie statique; chaque plan reste soumis a la simulation et au plafond de frais"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<!-- file: docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Audit d’exécution Confidential Transfer Token-2022
|
||||
|
||||
## Frontières de programme
|
||||
|
||||
Les instructions Confidential Transfer restent des instructions du programme Token-2022. Les preuves associées sont des instructions distinctes du programme natif ZK ElGamal Proof ou des comptes de contexte déjà vérifiés. Le registre ElGamal reste une troisième surface avec son propre Program ID.
|
||||
|
||||
Aucun builder Token-2022 ne doit incorporer silencieusement une preuve, déchiffrer un solde ou générer une clé privée.
|
||||
|
||||
## Modes de preuve
|
||||
|
||||
Chaque preuve est fournie selon un seul mode explicite :
|
||||
|
||||
- `instruction_offset` : offset relatif signé non nul vers une instruction ZK distincte de la même transaction ;
|
||||
- `context_state_account` : compte readonly contenant le contexte public d’une preuve déjà vérifiée ; le wire Token-2022 encode alors l’offset `0`.
|
||||
|
||||
Un offset inline égal à zéro est invalide dans le contrat de l’exécuteur, car il rendrait le mode ambigu.
|
||||
|
||||
## Inventaire audité
|
||||
|
||||
| Opération | Preuves ordonnées | Classe d’exécution |
|
||||
|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|
|
||||
| InitializeMint | aucune | builder public |
|
||||
| UpdateMint | aucune | builder public |
|
||||
| ConfigureAccount | PubkeyValidity | entrées cryptographiques appelant |
|
||||
| ApproveAccount | aucune | builder public |
|
||||
| EmptyAccount | ZeroCiphertext | entrées cryptographiques appelant |
|
||||
| Deposit | aucune | builder public |
|
||||
| Withdraw | CiphertextCommitmentEquality, BatchedRangeProofU64 | entrées cryptographiques appelant |
|
||||
| Transfer | CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, BatchedRangeProofU128 | entrées cryptographiques appelant |
|
||||
| ApplyPendingBalance | aucune | builder public |
|
||||
| EnableConfidentialCredits | aucune | builder public |
|
||||
| DisableConfidentialCredits | aucune | builder public |
|
||||
| EnableNonConfidentialCredits | aucune | builder public |
|
||||
| DisableNonConfidentialCredits | aucune | builder public |
|
||||
| TransferWithFee | CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, PercentageWithFee, BatchedGroupedCiphertext2HandlesValidity, BatchedRangeProofU256 | entrées cryptographiques appelant |
|
||||
| ConfigureAccountWithRegistry | PubkeyValidity | entrées cryptographiques appelant |
|
||||
|
||||
La classe « builder public » signifie seulement que l’instruction Token-2022 ne nécessite pas de ciphertext ou preuve nouvellement généré. Elle ne dispense ni du préflight, ni de la simulation, ni des autorités et postconditions.
|
||||
|
||||
## Plan de livraison
|
||||
|
||||
- `pre.048` : types, modes de preuve, ordre des preuves et classification de support ;
|
||||
- `pre.049` : builders publics Initialize/Update/Approve/Deposit/Apply/credit toggles ;
|
||||
- `pre.050` : clés ElGamal et balances déchiffrables opaques, Initialize/Update Mint et Apply Pending Balance ;
|
||||
- `pre.051` : Configure Account et Configure Account With Registry ;
|
||||
- `pre.052` : Empty Account avec preuve ZeroCiphertext inline ou context-state ;
|
||||
- `pre.053` : Withdraw avec Equality et Range U64 ;
|
||||
- `pre.054` : Transfer avec Equality, Grouped Ciphertext Validity à trois handles et Range U128 ;
|
||||
- `pre.055` : audit exact de Transfer With Fee, cinq preuves, comptes et payload ;
|
||||
- `pre.056` : builder Transfer With Fee ;
|
||||
- tranches ultérieures : Confidential Transfer Fee, Confidential Mint/Burn et Permissioned Confidential Burn.
|
||||
|
||||
## `pre.053` — Withdraw
|
||||
|
||||
`Withdraw` exige deux références de preuve dans l’ordre officiel : `CiphertextCommitmentEquality`, puis `BatchedRangeProofU64`. Les modes inline et context-state peuvent être combinés. L’exécuteur transporte la nouvelle balance déchiffrable opaque sans produire ni vérifier localement les secrets ou preuves.
|
||||
|
||||
|
||||
## `pre.054` — Transfer
|
||||
|
||||
`Transfer` transporte une nouvelle balance source déchiffrable de 36 octets et deux ciphertexts auditeur ElGamal de 64 octets. Il exige exactement trois références de preuve dans l'ordre officiel : `CiphertextCommitmentEquality`, `BatchedGroupedCiphertext3HandlesValidity`, puis `BatchedRangeProofU128`. Chaque preuve peut être inline ou context-state ; l'Instructions Sysvar n'est ajouté qu'une fois lorsqu'au moins un offset est utilisé. Aucune donnée cryptographique n'est générée par l'exécuteur.
|
||||
|
||||
|
||||
## `pre.055` — audit Transfer With Fee
|
||||
|
||||
`TransferWithFee` conserve le même payload cryptographique Token-2022 que `Transfer` : une balance source déchiffrable de 36 octets et deux ciphertexts auditeur ElGamal de 64 octets. Il n’ajoute pas de ciphertext withheld ou de montant de frais au payload Token-2022. Les informations de frais et le ciphertext de frais appartiennent aux contextes publics des preuves `PercentageWithFee` et `BatchedGroupedCiphertext2HandlesValidity`.
|
||||
|
||||
Le wire exact contient le tag externe `27`, le sous-tag `13`, puis 169 octets de payload : `36 + 64 + 64 + 5 offsets i8`. La donnée complète mesure donc 171 octets.
|
||||
|
||||
Les cinq preuves restent ordonnées :
|
||||
|
||||
1. `CiphertextCommitmentEquality` ;
|
||||
2. `BatchedGroupedCiphertext3HandlesValidity` pour le montant transféré ;
|
||||
3. `PercentageWithFee` ;
|
||||
4. `BatchedGroupedCiphertext2HandlesValidity` pour le ciphertext de frais ;
|
||||
5. `BatchedRangeProofU256`.
|
||||
|
||||
L’ordre des comptes est source writable, mint readonly, destination writable, Instructions Sysvar si au moins une preuve est inline, comptes context-state dans l’ordre des cinq preuves, autorité, puis signataires multisig. Aucun type opaque Token-2022 supplémentaire n’est nécessaire avant le builder de `pre.056`.
|
||||
|
||||
## Builder Transfer With Fee — pre.056
|
||||
|
||||
Le builder `inner_transfer_with_fee` est activé avec les cinq preuves dans l’ordre officiel. Le payload reste limité à la nouvelle balance déchiffrable, aux deux ciphertexts auditeur et aux cinq offsets. Les comptes context-state suivent le même ordre que les preuves et l’Instructions Sysvar n’est présent qu’une fois lorsqu’au moins un offset inline est utilisé.
|
||||
|
||||
## Confidential Transfer Fee public builder tranche (`pre.057`)
|
||||
|
||||
The proof-free builder surface is restricted to subdiscriminants 0, 3, 4 and 5
|
||||
of the Token-2022 `ConfidentialTransferFeeExtension` envelope (external tag 37).
|
||||
Initialization accepts one writable mint, an optional configuration authority
|
||||
and one exact 32-byte ElGamal public key. Enable/disable harvest preserve simple
|
||||
or multisig authority metas. Permissionless harvest preserves an ordered,
|
||||
non-empty, duplicate-free source list bounded to 255 accounts. Withdrawals remain
|
||||
separate because they require ciphertext-equality proof context.
|
||||
|
||||
## Confidential Transfer Fee — retrait depuis plusieurs comptes (`pre.059`)
|
||||
|
||||
Le builder `inner_withdraw_withheld_tokens_from_accounts` conserve l’ordre officiel suivant : Mint readonly, destination writable, Instructions Sysvar ou compte context-state, autorité withdraw-withheld, signataires multisig, puis sources writable. Le payload encode `num_token_accounts: u8`, l’offset de preuve et la nouvelle balance déchiffrable destination.
|
||||
|
||||
La preuve requise est `CiphertextCiphertextEquality`. La liste des sources est non vide, unique, ordonnée et limitée à 255 comptes. Cette opération est explicitement sensible au front-running : une mutation d’un withheld ciphertext source après génération de la preuve fait échouer la transaction. Le parcours harvest-vers-Mint puis retrait-depuis-Mint reste l’alternative recommandée lorsque la stabilité des comptes sources ne peut pas être garantie.
|
||||
|
||||
## `pre.060` — audit Confidential Mint/Burn
|
||||
|
||||
L'interface publie six sous-instructions contiguës : `InitializeMint = 0`, `RotateSupplyElGamalPubkey = 1`, `UpdateDecryptableSupply = 2`, `Mint = 3`, `Burn = 4` et `ApplyPendingBurn = 5`. Cette famille reste dans l'enveloppe Token-2022 Confidential Mint/Burn et ne doit pas être confondue avec Permissioned Burn.
|
||||
|
||||
`InitializeMint` transporte une clé publique ElGamal de supply et une supply déchiffrable initiale. `RotateSupplyElGamalPubkey` transporte la nouvelle clé et exige `CiphertextCiphertextEquality`. `UpdateDecryptableSupply` et `ApplyPendingBurn` n'exigent aucune preuve, mais restent soumis aux autorités et préconditions d'état.
|
||||
|
||||
`Mint` et `Burn` exigent exactement trois preuves dans l'ordre : `CiphertextCommitmentEquality`, `BatchedGroupedCiphertext3HandlesValidity`, puis `BatchedRangeProofU128`. Les deux opérations transportent deux ciphertexts auditeur ; `Mint` transporte la nouvelle supply déchiffrable, tandis que `Burn` transporte la nouvelle balance disponible déchiffrable du compte source. Aucun secret, ciphertext ou preuve n'est généré par l'exécuteur.
|
||||
|
||||
Le découpage prévu est : builders publics et rotation dans `pre.061`, puis Mint/Burn prouvés dans `pre.062`. Permissioned Confidential Burn reste une tranche indépendante.
|
||||
2698
migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_MATRIX.json
Normal file
2698
migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_MATRIX.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
<!-- file: docs/SPL_TOKEN_2022_SOURCE_AUDIT.md -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# Audit des sources Token-2022 résolues
|
||||
|
||||
## Ancrage reproductible
|
||||
|
||||
L'interface Cargo résolue `spl-token-2022-interface 3.1.1` publie le commit
|
||||
`e18f9c6f9bf6044b934f48e3090e8e59e4820f02`, tagué `interface@v3.1.1` le 26 juin 2026.
|
||||
L'audit est effectué sur ce commit détaché, jamais sur la branche `main` mouvante.
|
||||
|
||||
Le `Cargo.lock` du workspace opérateur, SHA-256
|
||||
`248c02af8c78d3bb6ad747460fee0011f8141feef83364cf697e35e44bd4ceb9`, confirme que
|
||||
`kb_decoder_spl_token_2022 0.4.5` consomme directement les interfaces Token-2022 et registre
|
||||
ElGamal. Pour `spl-token-2022-interface 3.1.1`, il résout exactement :
|
||||
|
||||
- `solana-zk-elgamal-proof-interface 0.1.3` ;
|
||||
- `spl-token-confidential-transfer-proof-extraction 0.6.1` ;
|
||||
- `spl-token-group-interface 0.7.2` ;
|
||||
- `spl-token-metadata-interface 1.0.1` ;
|
||||
- `spl-type-length-value 0.9.1`.
|
||||
|
||||
La sortie `cargo tree -p spl-token-2022-interface -e features` prouve en particulier le chemin de
|
||||
consommation direct de Token Metadata `1.0.1`. Cette version diffère du `1.0.0` figé dans le
|
||||
`Cargo.lock` du processor source audité : les discriminants, layouts et builders devront donc être
|
||||
comparés différentiellement avant le décodeur, sans supposer leur égalité.
|
||||
|
||||
La comparaison officielle clôt cette dérive : `interface@v1.0.1`, publiée le 29 juin 2026 au
|
||||
commit court `7038969`, contient le correctif fusionné `fc97c60` de la pull request `87`. Le diff
|
||||
de production ajoute 78 lignes dans `interface/src/instruction.rs` sans suppression. Les
|
||||
discriminants, structures de payload et builders existants ne changent pas ; `unpack()` valide
|
||||
désormais les longueurs Borsh `u32 LE` avant `try_from_slice` pour :
|
||||
|
||||
- `Initialize` : `name`, `symbol`, `uri` ;
|
||||
- `UpdateField` : la chaîne de `Field::Key` lorsque le tag vaut `3`, puis `value` ;
|
||||
- `RemoveKey` : `key`, après l'octet booléen `idempotent`.
|
||||
|
||||
`UpdateAuthority` et `Emit` ne transportent pas de chaîne et restent inchangés. Le décodeur devra
|
||||
appliquer la garde `1.0.1` avant toute allocation, même si le processor source figé résolvait
|
||||
encore `1.0.0`.
|
||||
|
||||
Au même commit :
|
||||
|
||||
- `program/Cargo.toml` déclare le processor `spl-token-2022 11.0.0` ;
|
||||
- sa dépendance path demande l'interface `3.1.0`, satisfaite par le package workspace `3.1.1` ;
|
||||
- le `Cargo.lock` source résout `spl-token-group-interface 0.7.2`,
|
||||
`spl-token-metadata-interface 1.0.0` et `spl-elgamal-registry-interface 0.2.1` ;
|
||||
- l'égalité du binaire déployé avec cette source reste non prouvée sur Localnet, Devnet et Mainnet.
|
||||
|
||||
## Dispatch réellement implémenté
|
||||
|
||||
`Processor::_process_inner` applique cet ordre :
|
||||
|
||||
1. `PodTokenInstruction`, discriminant `u8` ;
|
||||
2. si ce décodage échoue, `TokenMetadataInstruction`, discriminant SPL de huit octets ;
|
||||
3. si ce décodage échoue, `TokenGroupInstruction`, discriminant SPL de huit octets ;
|
||||
4. sinon `InvalidInstruction`.
|
||||
|
||||
La surface routable contient donc :
|
||||
|
||||
| Famille | Enveloppes ou formes |
|
||||
|------------------------------------------------------|---------------------:|
|
||||
| Tags Token-2022 `u8` | 48 |
|
||||
| Familles d'extensions parmi ces tags | 15 |
|
||||
| Sous-instructions de ces familles | 58 |
|
||||
| Formes Token-2022 hors enveloppes | 33 |
|
||||
| Instructions Token Metadata exécutées par Token-2022 | 5 |
|
||||
| Instructions Token Group exécutées par Token-2022 | 4 |
|
||||
| Formes feuilles routables par le processor | 100 |
|
||||
|
||||
Les 48 tags ne constituent donc pas, seuls, une déclaration de couverture maximale. Les quinze
|
||||
tags d'extension sont des enveloppes et doivent être remplacés par leurs 58 sous-instructions dans
|
||||
le comptage des formes feuilles.
|
||||
|
||||
## Interfaces incorporées au processor
|
||||
|
||||
Les neuf discriminants ci-dessous sont exécutés par Token-2022 lorsqu'ils ciblent le Program ID
|
||||
`TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` :
|
||||
|
||||
| Interface | Instruction | Discriminant hexadécimal |
|
||||
|------------------------|--------------------------|--------------------------|
|
||||
| Token Metadata `1.0.0` | `initialize` | `d2e11ea258b84d8d` |
|
||||
| Token Metadata `1.0.0` | `update_field` | `dde9312db5cadcc8` |
|
||||
| Token Metadata `1.0.0` | `remove_key` | `ea122038598d25b5` |
|
||||
| Token Metadata `1.0.0` | `update_authority` | `d7e4a6e45464567b` |
|
||||
| Token Metadata `1.0.0` | `emit` | `faa6b4fa0d0cb846` |
|
||||
| Token Group `0.7.2` | `initialize_group` | `79716c2736330004` |
|
||||
| Token Group `0.7.2` | `update_group_max_size` | `6c25ab8ff81e126e` |
|
||||
| Token Group `0.7.2` | `update_group_authority` | `a1695801edddd8cb` |
|
||||
| Token Group `0.7.2` | `initialize_member` | `9820deb0dfed7486` |
|
||||
|
||||
Le propriétaire du décodage dépend toujours du Program ID exécutable : une CPI vers un programme
|
||||
tiers implémentant la même interface appartient au décodeur de ce programme tiers. Un TLV pointeur
|
||||
reste un lien et ne transfère pas la propriété du fait métier.
|
||||
|
||||
## Wire public des instructions hors enveloppes
|
||||
|
||||
Les 33 tags qui ne sont pas des enveloppes d'extension ont maintenant une ligne machine-readable
|
||||
dans `baseInstructionWireAudit`. Les tailles incluent toujours le tag `u8`. L'interface `3.1.1`
|
||||
encode les options d'instruction de manière compacte, distincte du `COption` à quatre octets des
|
||||
états de compte : `0` tient sur un octet ; `1` est suivi de 32 octets pour une clé publique ou de
|
||||
huit octets pour un `u64`.
|
||||
|
||||
Ce point donne notamment les tailles exactes suivantes :
|
||||
|
||||
- `InitializeMint` et `InitializeMint2` : 35 octets sans freeze authority, 67 avec ;
|
||||
- `SetAuthority` : 3 octets sans nouvelle autorité, 35 avec ;
|
||||
- `InitializeMintCloseAuthority` : 2 octets sans autorité, 34 avec ;
|
||||
- `UnwrapLamports` : 2 octets sans montant, 10 avec.
|
||||
|
||||
`GetAccountDataSize` et `Reallocate` consomment tout le suffixe par éléments `ExtensionType u16 LE` ;
|
||||
un suffixe impair ou un type inconnu est rejeté par l'unpacker d'interface. `UiAmountToAmount`
|
||||
consomme tout le suffixe comme UTF-8, y compris une chaîne vide au seul niveau de l'interface.
|
||||
`Batch` conserve tout le suffixe comme wire de records, dont les bornes et la validation processor
|
||||
restent à auditer.
|
||||
|
||||
La distinction critique est conservée dans la matrice : `TokenInstruction::unpack()` appelle
|
||||
`unpack_with_rest()` puis ignore le reste retourné. Pour les formes à préfixe fixe ou sans payload,
|
||||
un succès de cet unpacker public ne prouve donc pas la consommation de tout le buffer, ni
|
||||
l'acceptation du suffixe par le runtime. Le futur décodeur devra préserver et diagnostiquer tout
|
||||
suffixe borné jusqu'à ce que le chemin exact du processor figé en établisse la règle variante par
|
||||
variante.
|
||||
|
||||
## Règles de suffixe du processor figé
|
||||
|
||||
La comparaison a ensuite été menée sur les trois fichiers exacts du commit, avec leurs empreintes
|
||||
SHA-256 enregistrées dans la matrice :
|
||||
|
||||
- `interface/src/instruction.rs` : `672b3890…b3a287` ;
|
||||
- `program/src/pod_instruction.rs` : `7b546580…49c7e5` ;
|
||||
- `program/src/processor.rs` : `9d11b834…3032d`.
|
||||
|
||||
Les 33 formes hors enveloppes forment une partition complète et sans doublon :
|
||||
|
||||
| Règle processor | Tags | Nombre |
|
||||
|------------------------------------------------|----------------------------------------|-------:|
|
||||
| longueur totale Pod exacte | `2,3,4,7,8,12,13,14,15,16,18,19,23,35` | 14 |
|
||||
| préfixe Pod + option compacte, suffixe accepté | `0,6,20,25,45` | 5 |
|
||||
| tag seul, suffixe non inspecté et accepté | `1,5,9,10,11,17,22,31,32,38` | 10 |
|
||||
| reste entier interprété comme payload | `21,24,29` | 3 |
|
||||
| flux de records `Batch` | `255` | 1 |
|
||||
|
||||
`decode_instruction_data<T>()` exige exactement `1 + size_of::<T>()` octets. À l'inverse, les
|
||||
helpers privés des options compactes lisent la structure Pod, puis `0`, ou `1` suivi de la valeur,
|
||||
sans vérifier l'épuisement du buffer. Pour les dix branches sans données, le processor n'appelle
|
||||
aucun décodeur après la lecture du premier octet. Ces quinze formes acceptent donc réellement un
|
||||
suffixe, même si celui-ci ne porte aucune sémantique publiée. Le décodeur devra le préserver sous
|
||||
forme bornée et le diagnostiquer explicitement, sans créer de champ métier.
|
||||
|
||||
Pour `GetAccountDataSize` et `Reallocate`, chaque morceau de deux octets doit former un
|
||||
`ExtensionType` connu ; un dernier morceau incomplet ou un type inconnu échoue. Pour
|
||||
`UiAmountToAmount`, tout le reste doit être UTF-8, la chaîne vide restant admise à ce niveau.
|
||||
|
||||
`Batch` ne peut pas être vide : le plus petit wire total contient le tag externe, un header de deux
|
||||
octets et au moins un discriminant interne, soit quatre octets. Chaque record encode en `u8` son
|
||||
nombre de comptes et la longueur totale de son instruction interne. Un header ou payload tronqué
|
||||
échoue ; une longueur interne nulle échoue au dispatch ; un `Batch` interne est rejeté par
|
||||
`_process_inner`. Les fallbacks Token Metadata et Token Group restent en revanche joignables dans
|
||||
un record lorsque leur wire tient dans les 255 octets. Le processor n'impose pas de nombre maximal
|
||||
explicite de records au-delà des limites de transaction et de calcul : le décodeur devra donc poser
|
||||
sa propre borne.
|
||||
|
||||
## Comptes publiés par les builders de base
|
||||
|
||||
La matrice contient désormais une ligne ordonnée pour chacune des 33 formes hors enveloppes. Elle
|
||||
enregistre les metas exactes des 32 helpers Rust publiés : rôle, `writable`, `signer`, compte
|
||||
optionnel et queue multisig. `Batch` reste la seule forme sans helper Rust dédié pour construire
|
||||
ses metas ; `TokenInstruction::pack()` ne produit que ses données.
|
||||
|
||||
Les autorités simples et multisig partagent le contrat classique des builders : l'autorité est
|
||||
signataire lorsque la liste `signer_pubkeys` est vide ; sinon elle ne signe pas et chaque élément
|
||||
de la queue est ajouté en lecture seule avec le flag signataire. Le builder ne borne pas la taille
|
||||
de cette queue pour les opérations d'autorité et conserve ordre et doublons. Le runtime ne peut
|
||||
toutefois satisfaire qu'un multisig stockant de un à onze positions.
|
||||
|
||||
`InitializeMultisig` et `InitializeMultisig2` ont un contrat différent : les clés de configuration
|
||||
ne signent pas. Le builder exige `m` et `n` dans `1..=11` ainsi que `m <= n`. Le processor consomme
|
||||
tous les comptes restants comme clés de configuration, valide séparément `m` et `n`, mais ne
|
||||
répète pas le contrôle `m <= n`. Une instruction manuelle `m > n` peut donc initialiser un état que
|
||||
le builder refuse. Le décodeur doit conserver ce fait et le diagnostiquer sans le normaliser.
|
||||
|
||||
`SyncNative` publie deux helpers : le premier fournit seulement le compte natif modifiable ; le
|
||||
second ajoute Rent en lecture seule. Le processor traite effectivement le deuxième compte comme
|
||||
optionnel, mais ignore ceux qui suivraient. `Batch` découpe les comptes selon le compteur `u8` de
|
||||
chaque record ; les comptes externes restant après le dernier record ne sont pas rejetés. Ils
|
||||
devront rester visibles comme comptes non assignés.
|
||||
|
||||
Le contrat builder de `Reallocate` est fixé — compte à agrandir modifiable, payer modifiable et
|
||||
signataire, System Program, propriétaire conditionnel puis queue multisig — mais la consommation
|
||||
runtime détaillée reste ouverte jusqu'à reconfirmation du module séparé
|
||||
`program/src/extension/reallocate.rs` au même commit. Cette limite empêche de déclarer l'audit
|
||||
processor des comptes entièrement clos.
|
||||
|
||||
## Sources auditées
|
||||
|
||||
- dépôt `solana-program/token-2022`, commit figé ci-dessus ;
|
||||
- `interface/src/instruction.rs` et `interface/src/extension/**/instruction.rs` ;
|
||||
- `interface/src/extension/mod.rs` et les états d'extension ;
|
||||
- `program/src/processor.rs` et `program/src/extension/**/processor.rs` ;
|
||||
- dépôt `solana-program/token-metadata`, commit
|
||||
`552935e00710478541e3394a8b2624c9ba7f503f`, tag `interface@v1.0.0` ;
|
||||
- dépôt `solana-program/token-group`, commit
|
||||
`2645064953937118572735993ed0ac1d07db5f14`, tag `interface@v0.7.2`.
|
||||
|
||||
## Limites restantes
|
||||
|
||||
Cette tranche confirme le wire public, les règles runtime de suffixe et les metas officielles des
|
||||
builders des 33 formes hors enveloppes. Elle ne clôt pas encore l'audit par variante : consommation
|
||||
processor complète des comptes, données de preuve, builders des extensions, statuts de
|
||||
construction et compatibilité cluster doivent encore être renseignés avant le décodeur.
|
||||
|
||||
## Première tranche verticale d'extensions
|
||||
|
||||
Le décodeur structure désormais exactement douze feuilles issues de quatre enveloppes : les six
|
||||
instructions Transfer Fee, les deux Default Account State, les deux Memo Transfer et les deux CPI
|
||||
Guard. Les discriminants, champs numériques, options compactes et règles de longueur proviennent
|
||||
des interfaces figées au commit audité. Les onze autres enveloppes restent opaques et ne sont pas
|
||||
comptées comme décodées sémantiquement.
|
||||
|
||||
Cette tranche ne déclare encore aucune capacité exécuteur d'extension. Conformément à l'ordre du
|
||||
jalon, les projections propriétaires et leurs tests de non-duplication doivent être validés avant
|
||||
d'activer les builders officiels correspondants.
|
||||
|
||||
## Dispatch incorporé Token Metadata et Token Group
|
||||
|
||||
Le dispatch de production ne peut pas se limiter au premier octet de `TokenInstruction`. Après
|
||||
échec du décodage Token-2022 principal, le processor `11.0.0` audité tente successivement les
|
||||
interfaces Token Metadata puis Token Group. Les neuf discriminants déjà inventoriés dans la
|
||||
matrice sont donc désormais reconnus par le décodeur Token-2022 lorsque le Program ID réellement
|
||||
exécuté est `Tokenz...` : cinq feuilles Metadata et quatre feuilles Group.
|
||||
|
||||
Les chaînes Borsh Metadata sont bornées avant allocation et décodage. Les layouts Pod Group sont
|
||||
traités avec une longueur exacte. Cette intégration ne décode pas un programme tiers implémentant
|
||||
les mêmes interfaces : une CPI vers un autre Program ID reste la propriété du décodeur de ce
|
||||
programme.
|
||||
|
||||
L'égalité de surface à maintenir est désormais explicitement compilée et documentée :
|
||||
|
||||
```text
|
||||
33 feuilles hors enveloppes + 58 feuilles d'extensions + 5 Metadata + 4 Group = 100
|
||||
```
|
||||
|
||||
## Audit ElGamal restant
|
||||
|
||||
La présence de `spl-elgamal-registry-interface 0.2.1`, de son Program ID `regVY...` et de la seed
|
||||
`elgamal-registry` ne suffit pas à revendiquer sa couverture. La tranche dédiée doit encore fixer
|
||||
les discriminants et le wire exacts, la dérivation PDA, la relation wallet/owner, les clés ElGamal,
|
||||
les références de preuve, le lifecycle create/update, les autorités et signataires, les builders
|
||||
actuels ou expérimentaux et les preuves de déploiement cluster. Toute référence au registre depuis
|
||||
une instruction confidentielle Token-2022 reste un lien inter-programme et non une instruction du
|
||||
registre.
|
||||
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"matrixVersion": 2,
|
||||
"milestone": "0.4.6",
|
||||
"status": "partially_confirmed",
|
||||
"statusVocabulary": [
|
||||
"not_run",
|
||||
"simulated",
|
||||
"submitted",
|
||||
"confirmed",
|
||||
"unavailable",
|
||||
"failed"
|
||||
],
|
||||
"scenarios": [
|
||||
{
|
||||
"id": "offline_full_regression",
|
||||
"environment": "offline",
|
||||
"status": "confirmed",
|
||||
"requiredEvidence": [
|
||||
"test_suite",
|
||||
"clippy"
|
||||
],
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "test_suite",
|
||||
"value": "kb_pipeline 120/120"
|
||||
},
|
||||
{
|
||||
"kind": "test_suite",
|
||||
"value": "kb_executor_spl_token_2022 44/44"
|
||||
},
|
||||
{
|
||||
"kind": "test_suite",
|
||||
"value": "kb_executor_spl_elgamal_registry 6/6"
|
||||
},
|
||||
{
|
||||
"kind": "clippy",
|
||||
"value": "cargo clippy --all-targets clean"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "token_2022_public_devnet",
|
||||
"environment": "devnet",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"simulation",
|
||||
"signature",
|
||||
"canonical_hydration",
|
||||
"core_extraction",
|
||||
"decode_replay",
|
||||
"materialization",
|
||||
"second_replay"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "elgamal_registry_devnet",
|
||||
"environment": "devnet",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"simulation",
|
||||
"signature",
|
||||
"canonical_hydration",
|
||||
"core_extraction",
|
||||
"decode_replay",
|
||||
"materialization",
|
||||
"second_replay"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "confidential_transfer_localnet_or_devnet",
|
||||
"environment": "localnet_or_devnet",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"simulation",
|
||||
"proof_mode",
|
||||
"signature",
|
||||
"canonical_hydration",
|
||||
"core_extraction",
|
||||
"decode_replay",
|
||||
"materialization",
|
||||
"second_replay"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "confidential_mint_burn_localnet_or_devnet",
|
||||
"environment": "localnet_or_devnet",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"simulation",
|
||||
"proof_mode",
|
||||
"signature",
|
||||
"canonical_hydration",
|
||||
"core_extraction",
|
||||
"decode_replay",
|
||||
"materialization",
|
||||
"second_replay"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "permissioned_confidential_burn_localnet_or_devnet",
|
||||
"environment": "localnet_or_devnet",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"simulation",
|
||||
"proof_mode",
|
||||
"signature",
|
||||
"canonical_hydration",
|
||||
"core_extraction",
|
||||
"decode_replay",
|
||||
"materialization",
|
||||
"second_replay"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mainnet_observation_corpus",
|
||||
"environment": "mainnet_observation",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"signatures",
|
||||
"outer_cpi",
|
||||
"success_failure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "postgres_double_replay",
|
||||
"environment": "postgres",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"postgres_tests",
|
||||
"first_replay",
|
||||
"second_replay",
|
||||
"no_duplicates"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "tauri_smoke",
|
||||
"environment": "tauri",
|
||||
"status": "not_run",
|
||||
"requiredEvidence": [
|
||||
"startup",
|
||||
"routes",
|
||||
"journal"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
1440
migration/khadhroony-bot2-reference/docs/SPL_TOKEN_MATRIX.json
Normal file
1440
migration/khadhroony-bot2-reference/docs/SPL_TOKEN_MATRIX.json
Normal file
File diff suppressed because it is too large
Load Diff
109
migration/khadhroony-bot2-reference/docs/SQL_DEMOS.md
Normal file
109
migration/khadhroony-bot2-reference/docs/SQL_DEMOS.md
Normal file
@@ -0,0 +1,109 @@
|
||||
<!-- file: docs/SQL_DEMOS.md -->
|
||||
<!-- version: 12 -->
|
||||
|
||||
# Démos SQL
|
||||
|
||||
## Fenêtres disponibles
|
||||
|
||||
| Fenêtre | Rôle |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------|
|
||||
| `demo_sql_diag` | Profil, backend, DSN masqué, santé PostgreSQL et tables attendues. |
|
||||
| `demo_sql_pg_raw` | Transactions canoniques et observations d’acquisition. |
|
||||
| `demo_sql_pg_core` | Transactions, comptes, instructions, inner instructions, logs, balances et ledger. |
|
||||
| `demo_sql_replay_candidates` | Exploration read-only, filtrage et export des signatures, programmes, mints, owners et account keys. |
|
||||
|
||||
Depuis `0.3.4`, le diagnostic core inclut :
|
||||
|
||||
```text
|
||||
kb_sol_ops_processing_ledger
|
||||
```
|
||||
|
||||
La fenêtre `demo_core_extraction` exécute le worker puis permet d’ouvrir les diagnostics raw/core et le sélecteur de candidats. Les fenêtres SQL restent read-only et ne modifient aucune donnée.
|
||||
|
||||
## Sélecteur de candidats replay
|
||||
|
||||
`demo_sql_replay_candidates` sépare l’exploration en cinq tableaux DataTables avec intégration Bootstrap 5 :
|
||||
|
||||
1. transactions et signatures ;
|
||||
2. programmes observés dans les instructions outer, inner et dans les logs reliés ;
|
||||
3. mints ;
|
||||
4. owners ;
|
||||
5. account keys.
|
||||
|
||||
Les requêtes PostgreSQL sont statiques, paramétrées et limitées à `5 000` lignes au maximum par chargement. Aucun éditeur SQL libre n’est exposé.
|
||||
|
||||
### Transactions et signatures
|
||||
|
||||
Filtres serveur disponibles :
|
||||
|
||||
- fragment de signature ;
|
||||
- plage inclusive de slots ;
|
||||
- état raw ;
|
||||
- statut du ledger `core_extraction` ;
|
||||
- program ID exact avec portée `any`, `outer`, `inner` ou `logs` ;
|
||||
- mint, owner ou account key exact ;
|
||||
- limite et ordre des slots.
|
||||
|
||||
Le tableau affiche aussi la présence du graphe core, le statut du ledger, sa version et son nombre de tentatives, ainsi que les cardinalités outer/inner.
|
||||
|
||||
### Programmes
|
||||
|
||||
Le tableau agrège les occurrences issues de :
|
||||
|
||||
```text
|
||||
kb_sol_core_instructions
|
||||
kb_sol_core_inner_instructions
|
||||
kb_sol_core_logs
|
||||
```
|
||||
|
||||
Chaque ligne expose le code connu de `kb_program_ids` lorsqu’il existe, le nombre de transactions distinctes, d’instructions outer, d’instructions inner, de logs reliés et la plage de slots observée. Le filtre propose une liste des programmes enregistrés tout en conservant une saisie libre pour les identifiants inconnus ou partiels.
|
||||
|
||||
Pour appliquer un programme au tableau des transactions, il faut cocher exactement une ligne puis utiliser « Utiliser comme filtre transactions ». Lorsqu’aucune ligne n’est cochée, l’action accepte aussi une seule ligne restant après le filtre local DataTables.
|
||||
|
||||
### Mints, owners et comptes
|
||||
|
||||
Les trois familles disposent de tableaux et filtres indépendants. Les mints et owners proviennent de `kb_sol_core_balance_changes`; les account keys proviennent de `kb_sol_core_account_keys` et ne sont pas classifiées automatiquement comme wallet, pool, mint ou token account.
|
||||
|
||||
Chaque tableau affiche transactions distinctes, occurrences, occurrences par transaction, slots minimum/maximum et étendue. Une valeur sélectionnée peut être appliquée directement au filtre du tableau des transactions.
|
||||
|
||||
### Sélection et export
|
||||
|
||||
Dans chaque tableau :
|
||||
|
||||
- la première colonne contient une case à cocher gérée par l’extension DataTables Select ;
|
||||
- la case du header sélectionne ou désélectionne toutes les lignes correspondant au filtre local ;
|
||||
- une sélection explicite est prioritaire ;
|
||||
- sans sélection, la copie et l’export utilisent toutes les lignes correspondant au filtre local DataTables ;
|
||||
- les identifiants peuvent être copiés dans le presse-papiers sous forme multiligne ;
|
||||
- le frontend construit un CSV diagnostique avec BOM UTF-8, séparateur `;` et CRLF ;
|
||||
- une commande Rust bornée écrit le fichier dans `data/exports_csv/` et ajoute un suffixe lorsque le nom existe déjà ;
|
||||
- les valeurs longues sont tronquées visuellement, exposées intégralement par tooltip et copiables cellule par cellule ;
|
||||
- tous les tableaux affichent 10 lignes par défaut et possèdent un reset complet des filtres.
|
||||
|
||||
La copie multiligne des signatures peut être collée telle quelle dans le mode `Signatures explicites` de `demo_core_extraction`. L’export ne dépend plus du téléchargement HTML du WebView.
|
||||
|
||||
### Lecture des colonnes transactions
|
||||
|
||||
Les cardinalités sont séparées en quatre colonnes : `Outer inst.`, `Inner inst.`, `Outer pr.` et `Inner pr.`. Les deux premières comptent les instructions ; les deux dernières comptent les program IDs distincts.
|
||||
|
||||
La colonne `Core` décrit le graphe structurel :
|
||||
|
||||
- `absent` : aucune ligne `kb_sol_core_transactions` ;
|
||||
- `présent` : graphe core présent et transaction Solana réussie ;
|
||||
- `échec on-chain` : graphe core présent, mais la transaction Solana elle-même a retourné une erreur.
|
||||
|
||||
`échec on-chain` n’est donc pas un échec de l’extracteur. Le tri DataTables utilise un rang numérique distinct pour ces trois états.
|
||||
|
||||
### Pools et paires
|
||||
|
||||
Aucun onglet pool/pair n’est ajouté dans `0.3.4`. Le core connaît des account keys mais ne peut pas déterminer de façon fiable leur rôle sémantique. Les futurs décodeurs et matérialiseurs alimenteront les tables catalogues de pools et de paires ; l’onglet pourra alors reposer sur des identifiants explicites plutôt que sur une heuristique.
|
||||
|
||||
## Contrôles recommandés après extraction
|
||||
|
||||
- comparer le nombre de transactions raw `core_extracted` et de transactions core ;
|
||||
- vérifier l’absence de lignes filles orphelines ;
|
||||
- vérifier l’unicité des chemins et indices ;
|
||||
- comparer les statuts ledger avec les graphes core présents ;
|
||||
- examiner les lignes raw `failed` et leur `lifecycle_reason`.
|
||||
|
||||
Les requêtes prêtes à l’emploi sont dans `sql/validation/000_core_integrity.sql`.
|
||||
272
migration/khadhroony-bot2-reference/docs/SURFACE_CRATE_MATRIX.md
Normal file
272
migration/khadhroony-bot2-reference/docs/SURFACE_CRATE_MATRIX.md
Normal file
@@ -0,0 +1,272 @@
|
||||
<!-- file: docs/SURFACE_CRATE_MATRIX.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Matrice des crates réservés
|
||||
|
||||
Ce document liste les crates actuellement présents dans le workspace. Un crate réservé ne signifie pas que le décodage, la matérialisation ou l’exécution est validé.
|
||||
|
||||
## Principes
|
||||
|
||||
- Les surfaces non classifiées ne doivent pas avoir de crate.
|
||||
- Les décodeurs et exécuteurs sont réservés par surface canonique lorsque la surface est classifiée.
|
||||
- Les détails de validation restent dans `registry/program_registry_seed.toml` et les docs spécialisées.
|
||||
|
||||
## Solana
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|---------------|--------------------------|---------------------------|
|
||||
| `solana_core` | `kb_decoder_solana_core` | `kb_executor_solana_core` |
|
||||
|
||||
## Spl
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|--------------------------------|-------------------------------------------|--------------------------------------------|
|
||||
| `spl_account_compression` | `kb_decoder_spl_account_compression` | `kb_executor_spl_account_compression` |
|
||||
| `spl_associated_token_account` | `kb_decoder_spl_associated_token_account` | `kb_executor_spl_associated_token_account` |
|
||||
| `spl_memo` | `kb_decoder_spl_memo` | `kb_executor_spl_memo` |
|
||||
| `spl_name_service` | `kb_decoder_metadata_spl_name_service` | `kb_executor_metadata_spl_name_service` |
|
||||
| `spl_noop` | `kb_decoder_spl_noop` | `kb_executor_spl_noop` |
|
||||
| `spl_single_pool` | `kb_decoder_spl_single_pool` | `kb_executor_spl_single_pool` |
|
||||
| `spl_stake_pool` | `kb_decoder_spl_stake_pool` | `kb_executor_spl_stake_pool` |
|
||||
| `spl_token` | `kb_decoder_spl_token` | `kb_executor_spl_token` |
|
||||
| `spl_token_2022` | `kb_decoder_spl_token_2022` | `kb_executor_spl_token_2022` |
|
||||
|
||||
|
||||
## Amm
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|----------------------------|---------------------------------------|----------------------------------------|
|
||||
| `amm_aldrin_v1` | `kb_decoder_amm_aldrin_v1` | `kb_executor_amm_aldrin_v1` |
|
||||
| `amm_aldrin_v2` | `kb_decoder_amm_aldrin_v2` | `kb_executor_amm_aldrin_v2` |
|
||||
| `amm_alphaq` | `kb_decoder_amm_alphaq` | `kb_executor_amm_alphaq` |
|
||||
| `amm_believe` | `kb_decoder_amm_believe` | `kb_executor_amm_believe` |
|
||||
| `amm_bonk_swap` | `kb_decoder_amm_bonk_swap` | `kb_executor_amm_bonk_swap` |
|
||||
| `amm_fluxbeam` | `kb_decoder_amm_fluxbeam` | `kb_executor_amm_fluxbeam` |
|
||||
| `amm_goon_fi` | `kb_decoder_amm_goon_fi` | `kb_executor_amm_goon_fi` |
|
||||
| `amm_goosefx_gamma` | `kb_decoder_amm_goosefx_gamma` | `kb_executor_amm_goosefx_gamma` |
|
||||
| `amm_goosefx_v2` | `kb_decoder_amm_goosefx_v2` | `kb_executor_amm_goosefx_v2` |
|
||||
| `amm_guac_swap` | `kb_decoder_amm_guac_swap` | `kb_executor_amm_guac_swap` |
|
||||
| `amm_lifinity_swap_v2` | `kb_decoder_amm_lifinity_swap_v2` | `kb_executor_amm_lifinity_swap_v2` |
|
||||
| `amm_metadao_futarchy_amm` | `kb_decoder_amm_metadao_futarchy_amm` | `kb_executor_amm_metadao_futarchy_amm` |
|
||||
| `amm_metadao_v0_5` | `kb_decoder_amm_metadao_v0_5` | `kb_executor_amm_metadao_v0_5` |
|
||||
| `amm_meteora_damm_v1` | `kb_decoder_amm_meteora_damm_v1` | `kb_executor_amm_meteora_damm_v1` |
|
||||
| `amm_meteora_damm_v2` | `kb_decoder_amm_meteora_damm_v2` | `kb_executor_amm_meteora_damm_v2` |
|
||||
| `amm_obric_v2` | `kb_decoder_amm_obric_v2` | `kb_executor_amm_obric_v2` |
|
||||
| `amm_one_dex` | `kb_decoder_amm_one_dex` | `kb_executor_amm_one_dex` |
|
||||
| `amm_pump_swap` | `kb_decoder_amm_pump_swap` | `kb_executor_amm_pump_swap` |
|
||||
| `amm_raydium_lp_v4` | `kb_decoder_amm_raydium_lp_v4` | `kb_executor_amm_raydium_lp_v4` |
|
||||
| `amm_solfi` | `kb_decoder_amm_solfi` | `kb_executor_amm_solfi` |
|
||||
| `amm_solfi_v2` | `kb_decoder_amm_solfi_v2` | `kb_executor_amm_solfi_v2` |
|
||||
| `amm_vertigo` | `kb_decoder_amm_vertigo` | `kb_executor_amm_vertigo` |
|
||||
| `amm_virtuals` | `kb_decoder_amm_virtuals` | `kb_executor_amm_virtuals` |
|
||||
| `amm_woofi` | `kb_decoder_amm_woofi` | `kb_executor_amm_woofi` |
|
||||
| `amm_zero_fi` | `kb_decoder_amm_zero_fi` | `kb_executor_amm_zero_fi` |
|
||||
| `amm_zora` | `kb_decoder_amm_zora` | `kb_executor_amm_zora` |
|
||||
|
||||
## Cpmm
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|----------------|---------------------------|----------------------------|
|
||||
| `cpmm_raydium` | `kb_decoder_cpmm_raydium` | `kb_executor_cpmm_raydium` |
|
||||
|
||||
## Clmm
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-----------------------|----------------------------------|-----------------------------------|
|
||||
| `clmm_byreal` | `kb_decoder_clmm_byreal` | `kb_executor_clmm_byreal` |
|
||||
| `clmm_fusion` | `kb_decoder_clmm_fusion` | `kb_executor_clmm_fusion` |
|
||||
| `clmm_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `kb_executor_clmm_orca_whirlpool` |
|
||||
| `clmm_pancake_swap` | `kb_decoder_clmm_pancake_swap` | `kb_executor_clmm_pancake_swap` |
|
||||
| `clmm_raydium` | `kb_decoder_clmm_raydium` | `kb_executor_clmm_raydium` |
|
||||
| `clmm_stabble` | `kb_decoder_clmm_stabble` | `kb_executor_clmm_stabble` |
|
||||
|
||||
## Dlmm
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|----------------|---------------------------|----------------------------|
|
||||
| `dlmm_meteora` | `kb_decoder_dlmm_meteora` | `kb_executor_dlmm_meteora` |
|
||||
|
||||
## Stable Swap
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|------------------------------|-----------------------------------------|------------------------------------------|
|
||||
| `stable_swap_hylo_exchange` | `kb_decoder_stable_swap_hylo_exchange` | `kb_executor_stable_swap_hylo_exchange` |
|
||||
| `stable_swap_jupiter_stable` | `kb_decoder_stable_swap_jupiter_stable` | `kb_executor_stable_swap_jupiter_stable` |
|
||||
| `stable_swap_numeraire` | `kb_decoder_stable_swap_numeraire` | `kb_executor_stable_swap_numeraire` |
|
||||
| `stable_swap_stabble` | `kb_decoder_stable_swap_stabble` | `kb_executor_stable_swap_stabble` |
|
||||
|
||||
## Weighted Swap
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-------------------------|------------------------------------|-------------------------------------|
|
||||
| `weighted_swap_stabble` | `kb_decoder_weighted_swap_stabble` | `kb_executor_weighted_swap_stabble` |
|
||||
|
||||
## Launchpad
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-------------------------------|------------------------------------------|-------------------------------------------|
|
||||
| `launchpad_boop_fun` | `kb_decoder_launchpad_boop_fun` | `kb_executor_launchpad_boop_fun` |
|
||||
| `launchpad_metadao_ico` | `kb_decoder_launchpad_metadao_ico` | `kb_executor_launchpad_metadao_ico` |
|
||||
| `launchpad_meteora_dbc` | `kb_decoder_launchpad_meteora_dbc` | `kb_executor_launchpad_meteora_dbc` |
|
||||
| `launchpad_moonit` | `kb_decoder_launchpad_moonit` | `kb_executor_launchpad_moonit` |
|
||||
| `launchpad_orca_wavebreak` | `kb_decoder_launchpad_orca_wavebreak` | `kb_executor_launchpad_orca_wavebreak` |
|
||||
| `launchpad_printr` | `kb_decoder_launchpad_printr` | `kb_executor_launchpad_printr` |
|
||||
| `launchpad_pump_fun` | `kb_decoder_launchpad_pump_fun` | `kb_executor_launchpad_pump_fun` |
|
||||
| `launchpad_pump_pumpup_ai` | `kb_decoder_launchpad_pump_pumpup_ai` | `kb_executor_launchpad_pump_pumpup_ai` |
|
||||
| `launchpad_raydium_launchlab` | `kb_decoder_launchpad_raydium_launchlab` | `kb_executor_launchpad_raydium_launchlab` |
|
||||
|
||||
## Router
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|--------------------------------|-------------------------------------------|--------------------------------------------|
|
||||
| `router_dflow_aggregator_v4` | `kb_decoder_router_dflow_aggregator_v4` | `kb_executor_router_dflow_aggregator_v4` |
|
||||
| `router_jupiter_aggregator_v4` | `kb_decoder_router_jupiter_aggregator_v4` | `kb_executor_router_jupiter_aggregator_v4` |
|
||||
| `router_jupiter_aggregator_v6` | `kb_decoder_router_jupiter_aggregator_v6` | `kb_executor_router_jupiter_aggregator_v6` |
|
||||
| `router_jupiter_dca` | `kb_decoder_router_jupiter_dca` | `kb_executor_router_jupiter_dca` |
|
||||
| `router_okx_labs_v1` | `kb_decoder_router_okx_labs_v1` | `kb_executor_router_okx_labs_v1` |
|
||||
| `router_okx_labs_v2` | `kb_decoder_router_okx_labs_v2` | `kb_executor_router_okx_labs_v2` |
|
||||
|
||||
## Orderbook
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|------------------------------------|-----------------------------------------------|------------------------------------------------|
|
||||
| `orderbook_jupiter_limit_order` | `kb_decoder_orderbook_jupiter_limit_order` | `kb_executor_orderbook_jupiter_limit_order` |
|
||||
| `orderbook_jupiter_limit_order_v2` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `kb_executor_orderbook_jupiter_limit_order_v2` |
|
||||
| `orderbook_openbook_v2` | `kb_decoder_orderbook_openbook_v2` | `kb_executor_orderbook_openbook_v2` |
|
||||
|
||||
## Admin
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|----------------------|---------------------------------|----------------------------------|
|
||||
| `admin_jupiter_lock` | `kb_decoder_admin_jupiter_lock` | `kb_executor_admin_jupiter_lock` |
|
||||
| `admin_pump_fees` | `kb_decoder_admin_pump_fees` | `kb_executor_admin_pump_fees` |
|
||||
|
||||
## Fees
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|--------------------------|-------------------------------------|--------------------------------------|
|
||||
| `fees_bags_fee_share_v1` | `kb_decoder_fees_bags_fee_share_v1` | `kb_executor_fees_bags_fee_share_v1` |
|
||||
| `fees_bags_fee_share_v2` | `kb_decoder_fees_bags_fee_share_v2` | `kb_executor_fees_bags_fee_share_v2` |
|
||||
|
||||
## Lock
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-------------------|------------------------------|-------------------------------|
|
||||
| `lock_raydium_lp` | `kb_decoder_lock_raydium_lp` | `kb_executor_lock_raydium_lp` |
|
||||
|
||||
## Adapter
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|---------------------------------|--------------------------------------------|---------------------------------------------|
|
||||
| `adapter_saber_decimal_wrapper` | `kb_decoder_adapter_saber_decimal_wrapper` | `kb_executor_adapter_saber_decimal_wrapper` |
|
||||
|
||||
## Governance
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-------------------------------|------------------------------------------|-------------------------------------------|
|
||||
| `governance_metadao_bid_wall` | `kb_decoder_governance_metadao_bid_wall` | `kb_executor_governance_metadao_bid_wall` |
|
||||
|
||||
## Bridge
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|------------------------------------------------|-----------------------------------------------------------|------------------------------------------------------------|
|
||||
| `bridge_circle_cctp_token_messenger_minter` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `kb_executor_bridge_circle_cctp_token_messenger_minter` |
|
||||
| `bridge_circle_cctp_token_messenger_minter_v2` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `kb_executor_bridge_circle_cctp_token_messenger_minter_v2` |
|
||||
| `bridge_layer_zero_endpoint` | `kb_decoder_bridge_layer_zero_endpoint` | `kb_executor_bridge_layer_zero_endpoint` |
|
||||
| `bridge_layer_zero_executor` | `kb_decoder_bridge_layer_zero_executor` | `kb_executor_bridge_layer_zero_executor` |
|
||||
|
||||
## Rwa
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|---------------------------|--------------------------------------|---------------------------------------|
|
||||
| `rwa_ondo_global_markets` | `kb_decoder_rwa_ondo_global_markets` | `kb_executor_rwa_ondo_global_markets` |
|
||||
|
||||
## Lending
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-----------------------------------|----------------------------------------------|-----------------------------------------------|
|
||||
| `lending_clone` | `kb_decoder_lending_clone` | `kb_executor_lending_clone` |
|
||||
| `lending_jupiter_lend_borrow` | `kb_decoder_lending_jupiter_lend_borrow` | `kb_executor_lending_jupiter_lend_borrow` |
|
||||
| `lending_jupiter_lend_earn` | `kb_decoder_lending_jupiter_lend_earn` | `kb_executor_lending_jupiter_lend_earn` |
|
||||
| `lending_jupiter_lend_flash_loan` | `kb_decoder_lending_jupiter_lend_flash_loan` | `kb_executor_lending_jupiter_lend_flash_loan` |
|
||||
| `lending_jupiter_lend_liquidity` | `kb_decoder_lending_jupiter_lend_liquidity` | `kb_executor_lending_jupiter_lend_liquidity` |
|
||||
| `lending_kamino` | `kb_decoder_lending_kamino` | `kb_executor_lending_kamino` |
|
||||
| `lending_marginfi_v2` | `kb_decoder_lending_marginfi_v2` | `kb_executor_lending_marginfi_v2` |
|
||||
|
||||
## Staking
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|----------------------------|---------------------------------------|----------------------------------------|
|
||||
| `staking_kamino_farm` | `kb_decoder_staking_kamino_farm` | `kb_executor_staking_kamino_farm` |
|
||||
| `staking_marinade_finance` | `kb_decoder_staking_marinade_finance` | `kb_executor_staking_marinade_finance` |
|
||||
| `staking_solayer` | `kb_decoder_staking_solayer` | `kb_executor_staking_solayer` |
|
||||
|
||||
## Vault
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-----------------------------|----------------------------------------|-----------------------------------------|
|
||||
| `vault_carrot_defi` | `kb_decoder_vault_carrot_defi` | `kb_executor_vault_carrot_defi` |
|
||||
| `vault_hylo_stability_pool` | `kb_decoder_vault_hylo_stability_pool` | `kb_executor_vault_hylo_stability_pool` |
|
||||
| `vault_kamino` | `kb_decoder_vault_kamino` | `kb_executor_vault_kamino` |
|
||||
| `vault_kamino_v2` | `kb_decoder_vault_kamino_v2` | `kb_executor_vault_kamino_v2` |
|
||||
| `vault_kamino_yvaults` | `kb_decoder_vault_kamino_yvaults` | `kb_executor_vault_kamino_yvaults` |
|
||||
| `vault_meteora` | `kb_decoder_vault_meteora` | `kb_executor_vault_meteora` |
|
||||
|
||||
## Vesting
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|----------------------|---------------------------------|----------------------------------|
|
||||
| `vesting_streamflow` | `kb_decoder_vesting_streamflow` | `kb_executor_vesting_streamflow` |
|
||||
|
||||
## Nft
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|--------------------------|-------------------------------------|--------------------------------------|
|
||||
| `nft_metaplex_bubblegum` | `kb_decoder_nft_metaplex_bubblegum` | `kb_executor_nft_metaplex_bubblegum` |
|
||||
|
||||
## Treasury
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|---------------------------------------|--------------------------------------------------|---------------------------------------------------|
|
||||
| `treasury_helium_treasury_management` | `kb_decoder_treasury_helium_treasury_management` | `kb_executor_treasury_helium_treasury_management` |
|
||||
|
||||
## Wallet
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|--------------------------------------|-------------------------------------------------|--------------------------------------------------|
|
||||
| `wallet_jupiter_apepro_smart_wallet` | `kb_decoder_wallet_jupiter_apepro_smart_wallet` | `kb_executor_wallet_jupiter_apepro_smart_wallet` |
|
||||
|
||||
## Perpetuals
|
||||
|
||||
| Surface | Décodeur | Exécuteur |
|
||||
|-----------------------|----------------------------------|-----------------------------------|
|
||||
| `perpetuals_drift_v2` | `kb_decoder_perpetuals_drift_v2` | `kb_executor_perpetuals_drift_v2` |
|
||||
| `perpetuals_jupiter` | `kb_decoder_perpetuals_jupiter` | `kb_executor_perpetuals_jupiter` |
|
||||
| `perpetuals_zeta` | `kb_decoder_perpetuals_zeta` | `kb_executor_perpetuals_zeta` |
|
||||
|
||||
## Matérialisateurs
|
||||
|
||||
| Crate |
|
||||
|---------------------------------------|
|
||||
| `kb_materializer_admin` |
|
||||
| `kb_materializer_bridge` |
|
||||
| `kb_materializer_compliance_audit` |
|
||||
| `kb_materializer_fees` |
|
||||
| `kb_materializer_governance` |
|
||||
| `kb_materializer_lending` |
|
||||
| `kb_materializer_lifecycle` |
|
||||
| `kb_materializer_liquidity` |
|
||||
| `kb_materializer_metadata` |
|
||||
| `kb_materializer_nft` |
|
||||
| `kb_materializer_oracle` |
|
||||
| `kb_materializer_orderbook` |
|
||||
| `kb_materializer_perpetuals` |
|
||||
| `kb_materializer_pool_state` |
|
||||
| `kb_materializer_rewards` |
|
||||
| `kb_materializer_risk` |
|
||||
| `kb_materializer_routing` |
|
||||
| `kb_materializer_staking` |
|
||||
| `kb_materializer_token_accounts` |
|
||||
| `kb_materializer_token_metadata_risk` |
|
||||
| `kb_materializer_trades` |
|
||||
| `kb_materializer_vault` |
|
||||
168
migration/khadhroony-bot2-reference/docs/TRACING_CONTRACT.md
Normal file
168
migration/khadhroony-bot2-reference/docs/TRACING_CONTRACT.md
Normal file
@@ -0,0 +1,168 @@
|
||||
<!-- file: docs/TRACING_CONTRACT.md -->
|
||||
<!-- version: 12 -->
|
||||
|
||||
# Contrat de tracing par crate
|
||||
|
||||
## Objectif
|
||||
|
||||
Chaque événement est émis par la crate qui prend réellement la décision. `kb_app_demo` journalise les frontières Tauri, les actions utilisateur, l’état des fenêtres et les résumés d’orchestration ; il ne recopie pas les décisions internes du RPC, du pipeline, du store, d’un décodeur, d’un matérialiseur ou d’un exécuteur.
|
||||
|
||||
## Classification des crates
|
||||
|
||||
Une crate est opérationnelle lorsqu’elle réalise au moins une des actions suivantes :
|
||||
|
||||
- I/O réseau ou base de données ;
|
||||
- orchestration concurrente, retry, pacing ou annulation ;
|
||||
- décodage ou matérialisation ;
|
||||
- construction ou simulation d’un plan d’exécution ;
|
||||
- décision runtime mutable ayant un effet observable.
|
||||
|
||||
Une crate passive contient uniquement des types, contrats, DTO, traits sans implémentation, registres ou constantes. Elle ne dépend pas de `tracing`. `kb_config` reste une exception de bootstrap : son chargement et sa validation précèdent l’installation du subscriber.
|
||||
|
||||
## Cible canonique
|
||||
|
||||
Toute crate qui déclare `tracing.workspace = true` doit :
|
||||
|
||||
- posséder `src/constants.rs` ;
|
||||
- définir exactement une constante `pub(crate) const TRACING_TARGET` ;
|
||||
- utiliser comme valeur le nom exact du package Cargo ;
|
||||
- appeler les macros avec `target: crate::constants::TRACING_TARGET` ;
|
||||
- déplacer la granularité interne dans des champs structurés.
|
||||
|
||||
Exemple :
|
||||
|
||||
```rust
|
||||
tracing::error!(
|
||||
target: crate::constants::TRACING_TARGET,
|
||||
action = "decoder_outcome_failure",
|
||||
campaign_id = %campaign_id,
|
||||
signature = %input.signature,
|
||||
instruction_path = %input.instruction_path,
|
||||
program_id = %input.program_id,
|
||||
processor_name = %identity.name,
|
||||
processor_version = %identity.version,
|
||||
status = ?result.status,
|
||||
diagnostics = ?result.diagnostics,
|
||||
"contextual instruction was not decoded successfully"
|
||||
);
|
||||
```
|
||||
|
||||
Les targets `khbot.*`, les targets suffixés par module et les targets de fenêtre ne sont plus utilisés par les crates opérationnelles migrées. Les identifiants frontend historiques restent acceptés comme valeurs du champ `frontend_target`, mais l’événement Rust est émis sous `kb_app_demo`.
|
||||
|
||||
## Crates actuellement routées
|
||||
|
||||
La configuration couvre toutes les crates qui déclarent actuellement `tracing.workspace = true` :
|
||||
|
||||
```text
|
||||
kb_app_demo
|
||||
kb_decoder_solana_core
|
||||
kb_decoder_spl_associated_token_account
|
||||
kb_decoder_spl_memo
|
||||
kb_decoder_spl_token
|
||||
kb_decoder_spl_token_2022
|
||||
kb_executor_metadata_spl_name_service
|
||||
kb_execution_solana
|
||||
kb_executor_solana_core
|
||||
kb_executor_spl_account_compression
|
||||
kb_executor_spl_associated_token_account
|
||||
kb_executor_spl_memo
|
||||
kb_executor_spl_noop
|
||||
kb_executor_spl_single_pool
|
||||
kb_logging
|
||||
kb_materializer_admin
|
||||
kb_materializer_compliance_audit
|
||||
kb_materializer_lifecycle
|
||||
kb_materializer_staking
|
||||
kb_materializer_transaction_annotations
|
||||
kb_pipeline
|
||||
kb_rpc
|
||||
kb_store_pg
|
||||
kb_wallet
|
||||
```
|
||||
|
||||
Le test `every_tracing_crate_has_one_canonical_target_constant` détecte automatiquement toute crate avec `tracing.workspace = true` et vérifie la présence d’une unique constante canonique. Le test de `kb_config` vérifie parallèlement la matrice de routes de chaque profil.
|
||||
|
||||
Les routes sont filtrées au niveau de leur writer plutôt que par un `Filtered` layer distinct. Un seul filtre d’admission agrégé empêche en amont le formatage des événements refusés par toutes les routes. Cette architecture préserve toutes les sorties globales et par crate au-delà de la limite interne de 64 identifiants de filtres de `tracing-subscriber`. Le test `more_than_64_routes_compose_without_filtered_layer_ids` protège explicitement ce contrat.
|
||||
|
||||
## Granularité et corrélation
|
||||
|
||||
Le target identifie la crate. Les champs décrivent l’action et le contexte :
|
||||
|
||||
```text
|
||||
action
|
||||
stage
|
||||
window
|
||||
campaign_id
|
||||
capture_session_id
|
||||
signature
|
||||
slot
|
||||
instruction_path
|
||||
program_id
|
||||
processor_name
|
||||
processor_version
|
||||
materializer_name
|
||||
materializer_version
|
||||
input_key
|
||||
input_hash
|
||||
status
|
||||
decision
|
||||
error_code
|
||||
```
|
||||
|
||||
Les spans de campagne et d’input utilisent eux aussi le target canonique `kb_pipeline`. Les événements des autres crates conservent leur propre target et reprennent les identifiants de corrélation utiles dans leurs champs.
|
||||
|
||||
## Politique des erreurs de décodage et de matérialisation
|
||||
|
||||
Un événement `error` est obligatoire pour :
|
||||
|
||||
- un input sélectionné sans décodeur compatible ;
|
||||
- un résultat de décodeur `failed` ;
|
||||
- un résultat de décodeur `unsupported` après dispatch vers une surface déclarée compatible ;
|
||||
- un résultat de décodeur invalide au regard du contrat API ;
|
||||
- un résultat de matérialiseur `failed` ;
|
||||
- une erreur de persistance decode, couverture, ledger ou matérialisation ;
|
||||
- une campagne qui termine avec `unmatched > 0` ou `failed_inputs > 0`.
|
||||
|
||||
L’événement doit permettre de retrouver rapidement l’input et la cause. Il conserve, lorsque disponibles, la campagne, la signature, le slot, le chemin d’instruction, le program ID, l’identité et la version du processor, la clé/hash d’input, le statut et les diagnostics structurés.
|
||||
|
||||
Les cas suivants ne sont pas des erreurs logicielles :
|
||||
|
||||
- transaction Solana échouée mais payload correctement décodé ;
|
||||
- observation non commitée conformément au statut on-chain ;
|
||||
- matérialisation `refused` par la politique de transaction ;
|
||||
- entrée volontairement `ignored` avec justification ;
|
||||
- annulation coopérative demandée par l’opérateur.
|
||||
|
||||
Ils sont journalisés en `debug`, `info` ou `warn` selon leur impact.
|
||||
|
||||
## Responsabilité
|
||||
|
||||
- `kb_rpc` : sélection d’endpoint, requêtes, réponses bornées, retry, rate limit et transport.
|
||||
- `kb_execution_solana` : assemblage du message, contrôle du contrat de signataires, liaison de simulation et signature transactionnelle.
|
||||
- `kb_store_pg` : transactions SQL, commit/rollback, compteurs et erreurs de persistance.
|
||||
- `kb_pipeline` : sélection, dispatch, concurrence, annulation, backfill et agrégation.
|
||||
- décodeur : reconnaissance, validation du format, décision et diagnostic borné.
|
||||
- matérialiseur : applicabilité exacte, politique de transaction et sorties produites.
|
||||
- exécuteur : support, construction, simulation et garde-fous.
|
||||
- application : invocation Tauri, fenêtre, progression utilisateur et résumé final.
|
||||
|
||||
## Données interdites
|
||||
|
||||
Ne jamais journaliser :
|
||||
|
||||
- clé privée, seed phrase ou transaction à signer ;
|
||||
- DSN ou URL contenant un secret non masqué ;
|
||||
- payload brut complet ou réponse RPC volumineuse ;
|
||||
- donnée dynamique non bornée.
|
||||
|
||||
Conserver à la place la taille, un préfixe borné et un SHA-256 lorsque l’audit du payload est nécessaire.
|
||||
|
||||
## Évolution
|
||||
|
||||
L’ajout ou la suppression de `tracing.workspace = true` impose dans le même delta :
|
||||
|
||||
1. la constante canonique ou sa suppression ;
|
||||
2. les événements réels de la crate ;
|
||||
3. la mise à jour de `config/example.config.json` ;
|
||||
4. la mise à jour des tests de contrat ;
|
||||
5. la mise à jour de la liste ci-dessus.
|
||||
@@ -0,0 +1,137 @@
|
||||
<!-- file: docs/TRANSACTION_ACQUISITION_MODEL.md -->
|
||||
<!-- version: 5 -->
|
||||
|
||||
# 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.
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<!-- file: docs/V0_4_6_VALIDATION.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Validation finale `0.4.6`
|
||||
|
||||
## Périmètre
|
||||
|
||||
Le jalon couvre Token-2022 et le registre ElGamal comme programmes distincts, ainsi que la régression des décodeurs Solana Core/SPL livrés auparavant. Il ne transforme pas Anchor, Metaplex ou les futurs DEX en surfaces implicitement couvertes.
|
||||
|
||||
## Résultats de tests
|
||||
|
||||
| Suite | Résultat |
|
||||
|------------------------------------|---------:|
|
||||
| `kb_store_core` | 41/41 |
|
||||
| `kb_store_pg` avec PostgreSQL réel | 46/46 |
|
||||
| `kb_pipeline` | 124/124 |
|
||||
| `kb_app_demo` | 118/118 |
|
||||
| `cargo clippy --all-targets` | propre |
|
||||
|
||||
Les contrôles SQL sur `kb_sol_decode_events` et `kb_sol_mat_events` ne révèlent aucun doublon d’identité.
|
||||
|
||||
## Token-2022 et ElGamal Registry
|
||||
|
||||
- 55 instructions Token-2022 Mainnet ont été décodées sans échec persistant.
|
||||
- Huit opérations publiques Token-2022 ont été confirmées sur Devnet : `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, `BurnChecked`, `FreezeAccount`, `ThawAccount` et `CloseAccount`.
|
||||
- Chaque parcours Devnet a été simulé, confirmé, hydraté, extrait, décodé, matérialisé puis rejoué idempotemment.
|
||||
- ElGamal Registry est validé synthétiquement/offline pour le wire, le PDA, l’état, les lectures RPC, les préflights et les preuves.
|
||||
- La validation publique ElGamal Registry reste indisponible sur Devnet/Mainnet au 20 juillet 2026 ; aucune preuve réseau n’est revendiquée.
|
||||
|
||||
## Loader immuable et Loader v4
|
||||
|
||||
Le parser `Write` du loader immuable lit désormais la longueur vectorielle en `u32` et les données à l’offset 12. Le replay Mainnet a supprimé 68 diagnostics `loader_vector_length_mismatch`.
|
||||
|
||||
Les rejets persistants restants sont tous issus de transactions Solana échouées :
|
||||
|
||||
| Diagnostic | Nombre |
|
||||
|----------------------------------|-------:|
|
||||
| `immutable_loader_tag_truncated` | 31 |
|
||||
| `loader_v4_tag_truncated` | 44 |
|
||||
| `loader_accounts_invalid` | 1 |
|
||||
|
||||
Quatre variantes Loader v4 inconnues sont persistées comme traitement réussi avec résultat métier `Unsupported`. Elles ne sont pas assimilées à des décodages complets.
|
||||
|
||||
## Replay des signatures incomplètes
|
||||
|
||||
Le mode `incomplete_signatures` sélectionne les signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou `unsupported`. La limite est appliquée au nombre de signatures avant expansion vers les instructions compatibles. Ce mode ne nécessite pas de force replay global.
|
||||
|
||||
Deux campagnes PostgreSQL réelles successives ont produit exactement :
|
||||
|
||||
```text
|
||||
selected=81
|
||||
started=81
|
||||
completed=81
|
||||
unmatched=0
|
||||
notStarted=0
|
||||
failedInputs=76
|
||||
decoded=1
|
||||
unsupported=4
|
||||
failed=76
|
||||
```
|
||||
|
||||
L’instruction décodée appartient à une signature incomplète contenant également une instruction valide. La répétition identique du second passage confirme la sélection stable ; les événements et matérialisations restent idempotents.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Tous les décodeurs livrés jusqu’à `0.4.6` respectent leur contrat sur les tests synthétiques, les scénarios stateful et les données réseau observées. Les payloads malformés sont rejetés fail-closed et les variantes inconnues sont classées explicitement `Unsupported`. Cette conclusion ne prétend pas que chaque variante rare possible a été observée sur un cluster public.
|
||||
@@ -0,0 +1,30 @@
|
||||
<!-- file: docs/VERSION_HEADER_AUDIT.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# Audit des en-têtes de version
|
||||
|
||||
Ce document regroupe les commandes utiles pour vérifier les en-têtes `file:` et `version:`.
|
||||
|
||||
## Lignes `version` différentes de 1
|
||||
|
||||
La commande suivante utilise deux passes pour éviter les problèmes de lookahead et d'expansion d'historique Bash :
|
||||
|
||||
```bash
|
||||
rg -n -P '^\s*(//|#|<!--|--|/\*)\s*version\s*:\s*[0-9]+' -g '!target/**' -g '!node_modules/**' -g '!dist/**' -g '!idls/**/*.json' . | rg -v -P 'version\s*:\s*1\s*(-->|\*/)?\s*$'
|
||||
```
|
||||
|
||||
## Fichiers concernés uniquement
|
||||
|
||||
```bash
|
||||
rg -n -P '^\s*(//|#|<!--|--|/\*)\s*version\s*:\s*[0-9]+' -g '!target/**' -g '!node_modules/**' -g '!dist/**' -g '!idls/**/*.json' . | rg -v -P 'version\s*:\s*1\s*(-->|\*/)?\s*$' | cut -d: -f1 | sort -u
|
||||
```
|
||||
|
||||
## Fichiers commentables sans ligne `version`
|
||||
|
||||
```bash
|
||||
find . -path './target' -prune -o -path './node_modules' -prune -o -path './dist' -prune -o -path './idls' -prune -o -type f \( -name '*.rs' -o -name '*.toml' -o -name '*.sql' -o -name '*.md' -o -name '*.ts' -o -name '*.js' -o -name '*.html' -o -name '*.scss' -o -name '*.sass' \) -print | while read -r f; do
|
||||
if ! head -n 5 "$f" | rg -q 'version\s*:'; then
|
||||
echo "$f"
|
||||
fi
|
||||
done
|
||||
```
|
||||
69
migration/khadhroony-bot2-reference/docs/WS_LISTENERS.md
Normal file
69
migration/khadhroony-bot2-reference/docs/WS_LISTENERS.md
Normal file
@@ -0,0 +1,69 @@
|
||||
<!-- file: docs/WS_LISTENERS.md -->
|
||||
<!-- version: 4 -->
|
||||
|
||||
# Listeners WebSocket
|
||||
|
||||
## Statut
|
||||
|
||||
Les listeners temps réel ne dirigent plus `0.3.x`. Ils seront repris après les décodeurs Core, Pump, Meteora, Raydium, Orca et Jupiter.
|
||||
|
||||
## Méthodes déjà présentes dans `demo_ws`
|
||||
|
||||
```text
|
||||
slotSubscribe
|
||||
slotsUpdatesSubscribe
|
||||
rootSubscribe
|
||||
logsSubscribe
|
||||
accountSubscribe
|
||||
programSubscribe
|
||||
signatureSubscribe
|
||||
blockSubscribe
|
||||
voteSubscribe
|
||||
```
|
||||
|
||||
L’audit confirme encore l’absence de :
|
||||
|
||||
```text
|
||||
transactionSubscribe
|
||||
transactionUnsubscribe
|
||||
request kind transaction_subscribe
|
||||
classification logs_subscribe_all
|
||||
```
|
||||
|
||||
Ces ajouts sont reportés à `0.10.x`.
|
||||
|
||||
## Modèle futur
|
||||
|
||||
```text
|
||||
Helius transactionSubscribe
|
||||
Yellowstone gRPC transactions
|
||||
logsSubscribe + hydration
|
||||
|
|
||||
v
|
||||
transaction canonique commune
|
||||
|
|
||||
+--> kb_sol_raw_transactions
|
||||
+--> kb_sol_obs_transaction_observations
|
||||
```
|
||||
|
||||
Le payload complet de la notification WebSocket ne doit pas être stocké durablement lorsqu’une transaction canonique existe.
|
||||
|
||||
## Cas d’usage
|
||||
|
||||
Les flux devront détecter rapidement :
|
||||
|
||||
- création de token ou pool ;
|
||||
- premiers swaps ;
|
||||
- migration ;
|
||||
- changement de liquidité ;
|
||||
- activité administrative dangereuse ;
|
||||
- frais et buybacks ;
|
||||
- changements sur les comptes critiques.
|
||||
|
||||
## Sécurité
|
||||
|
||||
- Aucun ordre direct depuis la couche transport.
|
||||
- Toute perte du flux principal désactive le trading.
|
||||
- Les événements rejoués ou backfillés mettent à jour l’état sans déclencher rétroactivement un ordre.
|
||||
- Les queues, tailles, retries et exports de diagnostic restent bornés.
|
||||
|
||||
Reference in New Issue
Block a user