Files
2026-07-30 17:50:29 +02:00

90 lines
4.8 KiB
Markdown

<!-- file: kb_decoder_spl_token/README.md -->
<!-- version: 4 -->
# kb_decoder_spl_token
Ce crate décode exclusivement le programme SPL Token classique
`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`. Token-2022, ATA, les interfaces
de métadonnées et les programmes qui invoquent Token par CPI ne sont pas absorbés dans sa
surface. Une instruction Token inner reste néanmoins décodée grâce à son Program ID exact et à
son chemin core stable.
## Surface couverte
La couverture compilée contient les 28 tags publiés par `spl-token-interface 3.0.0` : `0..24`,
`38`, `45` et `255`. Elle inclut les variantes historiques, checked, les deux générations sans
Rent, les conversions montant/UI, `WithdrawExcessLamports`, `UnwrapLamports` et `Batch`.
La publication d'un tag et son déploiement sont traités séparément. `UnwrapLamports` et `Batch`
sont constructibles dans l'interface `3.0.0` et implémentés par p-token `1.0.0`. Leur déploiement
sur le programme classique Devnet est observé par deux simulations réussies le 16 juillet 2026 ;
Localnet et Mainnet restent non testés pour ces tags récents. Les opérations classiques observées
sur Mainnet ou exécutées sur Devnet portent séparément leurs preuves réelles. La matrice normative
est `docs/SPL_TOKEN_MATRIX.json`.
## Contrat de décodage
`SplTokenDecoder` implémente les deux contrats communs :
- `ProtocolDecoder` annonce `Yes` uniquement pour le Program ID Token classique exact ;
- `InstructionDecoder` fournit une surface, 28 déclarations de couverture, une reconnaissance par
tag et un résultat contextualisé pour le replay commun.
Chaque observation conserve le tag, le wire borné et son SHA-256, le chemin outer/inner, les
comptes dans leur ordre d'origine avec doublons et flags, les rôles prouvables, les paramètres bruts,
la forme d'autorité structurelle et les diagnostics. Les montants `u64` sont écrits en chaînes
décimales dans le JSON pour éviter toute perte à la frontière Tauri.
Un `Transfer` non checked ne reçoit jamais de mint ou de decimals inventés. Le champ d'inférence
reste explicitement vide tant qu'aucune corrélation core prouvée n'est implémentée. Le décodeur
n'effectue aucune lecture RPC.
## Transactions échouées et autorités
Une transaction échouée mais lisible produit une intention structurée avec `committed=false`. Elle
ne prouve aucune mutation de compte Token. Les formes simple et multisig sont distinguées depuis
les metas fournies ; la validation de `M/N` et des membres d'un multisig existant exige un snapshot
stateful et n'est jamais inventée par le décodeur.
Pour `InitializeMultisig` et `InitializeMultisig2`, le wire `M`, le nombre `N` déduit des comptes et
les bornes officielles `1 <= M <= N <= 11` sont contrôlés structurellement.
## Batch
`Batch` est analysé avec des bornes locales de 64 sous-instructions, 512 comptes cumulés et
16 384 octets de payload. Chaque entrée conserve son slice de comptes, son offset de données, son
ordre et un chemin dérivé `<parent>/batch/<position>`. Un batch vide, tronqué, avec longueur nulle,
comptes insuffisants ou batch imbriqué échoue avec un diagnostic borné.
## Limites actuelles
- Le contrat `CoreInstructionReplayInput` ne transporte pas encore les return data canoniques ; les
trois conversions concernées restent décodées comme intentions, avec validation de return data
explicitement indisponible.
- Le corpus Mainnet prouve les opérations réellement observées, pas l'exhaustivité des 28 tags sur
ce cluster. Les initialisations normalisées par la matérialisation ne sont pas attribuées à une
variante wire sans preuve supplémentaire.
- Localnet et Mainnet restent sans preuve de déploiement de `UnwrapLamports` et `Batch` ; la preuve
Devnet n'est pas extrapolée aux autres clusters.
## Corpus réel
Le replay Mainnet du 15 juillet 2026 a décodé 370 instructions sans entrée unsupported, failed ou
unmatched. Il a produit 371 sorties commitées, huit refus correspondant exactement aux huit
transactions échouées et un second replay avec zéro sélection. Les signatures représentatives de
`Transfer`, `Approve`, `CloseAccount`, `TransferChecked`, `BurnChecked` et `SyncNative` sont
conservées par instruction dans la matrice. Le lifecycle Devnet y conserve séparément les
signatures exactes de ses opérations courantes.
Le probe Devnet du 16 juillet 2026 a simulé sans signature ni envoi un `Batch` contenant un
`TransferChecked` de montant nul, puis un `UnwrapLamports` d'un lamport depuis un compte wrapped SOL
auxiliaire classique. Les deux simulations ont réussi sans erreur runtime, avec respectivement 270
et 140 compute units.
## Exemple
```rust
let decoder = kb_decoder_spl_token::SplTokenDecoder;
let result = kb_decoder_api::InstructionDecoder::decode(&decoder, &input);
```