v0.4.8-pre.006
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 27
|
# version: 28
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -18,7 +18,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.4.8-pre.5"
|
version = "0.4.8-pre.6"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/IDL_TO_KB_LIB_NOMENCLATURE.md -->
|
<!-- file: docs/IDL_TO_KB_LIB_NOMENCLATURE.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 6 -->
|
||||||
|
|
||||||
# Correspondance IDL ↔ `kb-lib`
|
# Correspondance IDL ↔ `kb-lib`
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ Ce tableau constitue la cible de classification pour les futurs décodeurs et ex
|
|||||||
Il ne signifie pas que tous les modules proposés sont déjà implémentés.
|
Il ne signifie pas que tous les modules proposés sont déjà implémentés.
|
||||||
|
|
||||||
| IDL v3 | Module actuel | Module cible | Action Rust |
|
| IDL v3 | Module actuel | Module cible | Action Rust |
|
||||||
|-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------|------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------|------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `adapter.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.saber_decimal_wrapper.V0_0_0.from_solscan.json` | `adapter/saber_decimal_wrapper` | `adapter/saber_decimal_wrapper` | Aucune |
|
| `adapter.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.saber_decimal_wrapper.V0_0_0.from_solscan.json` | `adapter/saber_decimal_wrapper` | `adapter/saber_decimal_wrapper` | Aucune |
|
||||||
| `adapter.TwRapQCDhWkZRrDaHfZGuHxkZ91gHDRkyuzNqeU5MgR.spl_token_wrap.V0_1_0.from_github_solana_program.json` | `—` | `adapter/spl_token_wrap` | Créer `adapter/spl_token_wrap` |
|
| `adapter.TwRapQCDhWkZRrDaHfZGuHxkZ91gHDRkyuzNqeU5MgR.spl_token_wrap.V0_1_0.from_github_solana_program.json` | `—` | `adapter/spl_token_wrap` | Créer `adapter/spl_token_wrap` |
|
||||||
| `amm.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.lifinity_swap_v2.V0_1_1.from_solscan.json` | `amm/lifinity_swap_v2` | `amm/lifinity_swap_v2` | Aucune |
|
| `amm.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.lifinity_swap_v2.V0_1_1.from_solscan.json` | `amm/lifinity_swap_v2` | `amm/lifinity_swap_v2` | Aucune |
|
||||||
@@ -51,7 +51,7 @@ Il ne signifie pas que tous les modules proposés sont déjà implémentés.
|
|||||||
| `lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.kamino.V1_23_0.from_solscan.json` | `lending/kamino` | `lending/kamino` | Aucune |
|
| `lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.kamino.V1_23_0.from_solscan.json` | `lending/kamino` | `lending/kamino` | Aucune |
|
||||||
| `lending.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.marginfi_v2.V0_1_8.from_solscan.json` | `lending/marginfi_v2` | `lending/marginfi_v2` | Aucune |
|
| `lending.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.marginfi_v2.V0_1_8.from_solscan.json` | `lending/marginfi_v2` | `lending/marginfi_v2` | Aucune |
|
||||||
| `lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.raydium_lp.V0_1_0.from_solscan.json` | `lock/raydium_lp` | `lock/raydium_lp` | Aucune |
|
| `lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.raydium_lp.V0_1_0.from_solscan.json` | `lock/raydium_lp` | `lock/raydium_lp` | Aucune |
|
||||||
| `metadata.ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S.solana_program_metadata.V0_0_0.from_github_solana_program.json` | `metadata/solana_program_metadata` | `metadata/solana_program_metadata` | Modèles wire, décodeurs de comptes et neuf instructions stables présents ; implémenter la matérialisation et l’exécuteur |
|
| `metadata.ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S.solana_program_metadata.V0_0_0.from_github_solana_program.json` | `metadata/solana_program_metadata` | `metadata/solana_program_metadata` | Modèles wire, décodeurs de comptes, neuf instructions stables et matérialisation exhaustive présents ; implémenter l’exécuteur |
|
||||||
| `metadata.metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s.metaplex_token_metadata.V1_14_0.from_solscan.json` | `metadata/metaplex_token_metadata` | `metadata/metaplex_token_metadata` | Aucune |
|
| `metadata.metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s.metaplex_token_metadata.V1_14_0.from_solscan.json` | `metadata/metaplex_token_metadata` | `metadata/metaplex_token_metadata` | Aucune |
|
||||||
| `nft.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.metaplex_bubblegum.V0_12_0.from_solscan.json` | `nft/metaplex_bubblegum` | `nft/metaplex_bubblegum` | Aucune |
|
| `nft.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.metaplex_bubblegum.V0_12_0.from_solscan.json` | `nft/metaplex_bubblegum` | `nft/metaplex_bubblegum` | Aucune |
|
||||||
| `nft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.tensor_cnft.V2_0_1.from_solscan.json` | `—` | `nft/tensor_cnft` | Créer `nft/tensor_cnft` |
|
| `nft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.tensor_cnft.V2_0_1.from_solscan.json` | `—` | `nft/tensor_cnft` | Créer `nft/tensor_cnft` |
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<!-- file: docs/plans/V0_4_8_SOLANA_PROGRAM_METADATA_AND_TOKEN_2022_COMPLETENESS_PLAN.md -->
|
<!-- file: docs/plans/V0_4_8_SOLANA_PROGRAM_METADATA_AND_TOKEN_2022_COMPLETENESS_PLAN.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Plan `0.4.8` — Solana Program Metadata et complétude Token-2022
|
# Plan `0.4.8` — Solana Program Metadata et complétude Token-2022
|
||||||
|
|
||||||
## 1. Statut et rôle
|
## 1. Statut et rôle
|
||||||
|
|
||||||
Ce document est le livrable principal de `0.4.8-pre.001`, réconcilié après l’audit contractuel de `0.4.8-pre.002`, les modèles de `pre.003`, les décodeurs de comptes de `pre.004` et le décodeur d’instructions de `pre.005`. Il constitue le plan vivant de la version et reste modifiable lorsque l’audit du code, des interfaces officielles, de l’IDL ou des validations Devnet impose un ajustement.
|
Ce document est le livrable principal de `0.4.8-pre.001`, réconcilié après l’audit contractuel de `0.4.8-pre.002`, les modèles de `pre.003`, les décodeurs de comptes de `pre.004`, le décodeur d’instructions de `pre.005` et la matérialisation de `pre.006`. Il constitue le plan vivant de la version et reste modifiable lorsque l’audit du code, des interfaces officielles, de l’IDL ou des validations Devnet impose un ajustement.
|
||||||
|
|
||||||
Il doit être maintenu pendant chaque prerelease, puis archivé pendant la dernière prerelease après transfert des décisions durables vers le ROADMAP, les matrices, les guides, les rapports, les TODO et les changelogs concernés.
|
Il doit être maintenu pendant chaque prerelease, puis archivé pendant la dernière prerelease après transfert des décisions durables vers le ROADMAP, les matrices, les guides, les rapports, les TODO et les changelogs concernés.
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ Il doit être maintenu pendant chaque prerelease, puis archivé pendant la derni
|
|||||||
### 2.1 Surfaces strictement distinctes
|
### 2.1 Surfaces strictement distinctes
|
||||||
|
|
||||||
| Surface | Program ID ou frontière | Rôle dans `0.4.8` |
|
| Surface | Program ID ou frontière | Rôle dans `0.4.8` |
|
||||||
|-----------------------------------|--------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
|---|---|---|
|
||||||
| Metaplex Token Metadata | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | surface clôturée en `0.4.7`, hors développement principal |
|
| Metaplex Token Metadata | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | surface clôturée en `0.4.7`, hors développement principal |
|
||||||
| Token-2022 Token Metadata | programme Token-2022 `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | compléter le contrat déjà partiellement pris en charge |
|
| Token-2022 Token Metadata | programme Token-2022 `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | compléter le contrat déjà partiellement pris en charge |
|
||||||
| `spl-token-metadata-interface` | interface sans Program ID autonome imposé | source contractuelle des instructions metadata implémentées par Token-2022 |
|
| `spl-token-metadata-interface` | interface sans Program ID autonome imposé | source contractuelle des instructions metadata implémentées par Token-2022 |
|
||||||
@@ -109,6 +109,20 @@ La cible est de compléter les cinq opérations de l’interface dans les module
|
|||||||
- scénarios synthétiques et Devnet ;
|
- scénarios synthétiques et Devnet ;
|
||||||
- panneau desktop distinct de Solana Program Metadata.
|
- panneau desktop distinct de Solana Program Metadata.
|
||||||
|
|
||||||
|
### 4.3 Règle transversale de complétude
|
||||||
|
|
||||||
|
Pour chaque surface retenue dans `0.4.8` :
|
||||||
|
|
||||||
|
- le décodeur couvre toutes les formes officielles actuelles, historiques, remplacées ou obsolètes que les sources permettent d’identifier exactement ;
|
||||||
|
- toute observation ou tout état décodé possède une matérialisation explicite, avec refus des mutations non committées lorsque la projection exige un succès ;
|
||||||
|
- toutes les opérations courantes doivent être constructibles et exécutables, y compris les mutations destructrices, irréversibles ou susceptibles d’écraser un état existant ;
|
||||||
|
- le caractère dangereux d’une opération ne retire pas sa capacité d’exécution : son plan doit déclarer ses coûts, signataires, confirmations et postconditions, puis être évalué par le cadre commun `crate::ExSafetyChecker` avant simulation, signature et envoi ;
|
||||||
|
- une opération officiellement obsolète reste constructible uniquement avec une annotation `#[deprecated]`, un statut explicite et une approbation renforcée ; elle reste elle aussi soumise au cadre commun de sécurité ;
|
||||||
|
- une ancienne version officiellement remplacée par une autre forme wire reste decode-only et ne reçoit pas de builder concurrent ;
|
||||||
|
- aucune classification `Deprecated`, remplacée ou decode-only ne doit être inventée sans preuve officielle.
|
||||||
|
|
||||||
|
Pour Solana Program Metadata, l’audit courant classe les neuf instructions stables comme actuelles : la cible de l’exécuteur est donc de couvrir les neuf, notamment `Write`, `SetData`, `SetImmutable`, `Trim` et `Close`, sans neutraliser les opérations destructrices au niveau du builder.
|
||||||
|
|
||||||
## 5. Stratégie de validation obligatoire
|
## 5. Stratégie de validation obligatoire
|
||||||
|
|
||||||
Pour chaque capacité exécutable retenue :
|
Pour chaque capacité exécutable retenue :
|
||||||
@@ -118,7 +132,7 @@ Pour chaque capacité exécutable retenue :
|
|||||||
3. fixture créée par les APIs Rust du workspace lorsque cela est approprié ;
|
3. fixture créée par les APIs Rust du workspace lorsque cela est approprié ;
|
||||||
4. construction du message exact ;
|
4. construction du message exact ;
|
||||||
5. simulation RPC conservée ;
|
5. simulation RPC conservée ;
|
||||||
6. soumission Devnet lorsque l’opération est sûre et disponible ;
|
6. soumission Devnet sur une fixture contrôlée lorsque l’opération est réalisable, après une décision compatible du cadre commun `ExSafetyChecker` ;
|
||||||
7. confirmation et signature conservées ;
|
7. confirmation et signature conservées ;
|
||||||
8. lectures stateful avant/après ;
|
8. lectures stateful avant/après ;
|
||||||
9. postconditions explicites ;
|
9. postconditions explicites ;
|
||||||
@@ -221,7 +235,7 @@ Résultat durable : `kb-lib` expose des snapshots et parseurs bornés pour `Buff
|
|||||||
|
|
||||||
Validation définitive reçue le 5 août 2026 : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et l’audit workspace sont propres ; `cargo test -p kb-lib` réussit avec 653 tests unitaires, six tests d’intégration et les doc-tests.
|
Validation définitive reçue le 5 août 2026 : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et l’audit workspace sont propres ; `cargo test -p kb-lib` réussit avec 653 tests unitaires, six tests d’intégration et les doc-tests.
|
||||||
|
|
||||||
### `0.4.8-pre.005` — décodeur d’instructions Solana Program Metadata — terminé côté delta
|
### `0.4.8-pre.005` — décodeur d’instructions Solana Program Metadata — terminé
|
||||||
|
|
||||||
Résultat durable : les releases stables `program@v1.0.0` et `program@v1.0.1` exposent exactement neuf discriminants `0..8`. Le tag `5`, nommé `WithdrawExcessLamports` pendant une phase pré-stable, est couvert uniquement comme `Trim` et conserve cet ancien nom comme provenance. Les variantes runtime actuelles qui dépassent l’IDL sont explicitement préservées.
|
Résultat durable : les releases stables `program@v1.0.0` et `program@v1.0.1` exposent exactement neuf discriminants `0..8`. Le tag `5`, nommé `WithdrawExcessLamports` pendant une phase pré-stable, est couvert uniquement comme `Trim` et conserve cet ancien nom comme provenance. Les variantes runtime actuelles qui dépassent l’IDL sont explicitement préservées.
|
||||||
|
|
||||||
@@ -232,19 +246,29 @@ Résultat durable : les releases stables `program@v1.0.0` et `program@v1.0.1` ex
|
|||||||
- matrice contractuelle et audit historique dédiés ;
|
- matrice contractuelle et audit historique dédiés ;
|
||||||
- tests synthétiques et test de façade publique.
|
- tests synthétiques et test de façade publique.
|
||||||
|
|
||||||
Critère atteint côté delta : inventaire stable et historique fermé, décodeur fail-closed et frontières off-chain conservées ; compilation, Clippy et tests Rust restent à exécuter dans l’environnement du projet avant validation définitive.
|
Validation définitive reçue le 5 août 2026 : `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets` et l’audit workspace sont propres ; `cargo test -p kb-lib` réussit avec 660 tests unitaires, les tests d’API externes et la matrice de nomenclature.
|
||||||
|
|
||||||
### `0.4.8-pre.006` — matérialisation Solana Program Metadata
|
### `0.4.8-pre.006` — matérialisation Solana Program Metadata — terminé côté delta
|
||||||
|
|
||||||
- snapshots et observations dédiés ;
|
Résultat durable : les deux comptes décodés et les neuf instructions stables possèdent une projection. Les comptes `Buffer` et `Metadata` produisent des états autoritatifs bornés ; les instructions produisent des faits de mutation committés non autoritatifs. Les données directes et Buffer sont conservées en base64 avec SHA-256 ; les URL et références externes restent strictement on-chain et ne déclenchent aucun fetch.
|
||||||
- autorité, mutabilité, source, format, encodage, compression et données on-chain pertinentes ;
|
|
||||||
- aucune résolution de contenu distant.
|
- snapshots autoritatifs dédiés pour `Buffer` et `Metadata` ;
|
||||||
|
- faits committés dédiés pour les neuf instructions stables ;
|
||||||
|
- autorité, mutabilité, source, format, encodage, compression, capacité et données on-chain pertinentes ;
|
||||||
|
- refus des observations échouées ou non committées ;
|
||||||
|
- matrice de onze projections et test de façade externe ;
|
||||||
|
- aucune résolution de contenu distant ni lecture implicite d’un compte externe.
|
||||||
|
|
||||||
|
Critère atteint côté delta : tout ce qui est décodé dans la surface `ProgM6…` est matérialisable. Compilation, Clippy et tests Rust restent à exécuter dans l’environnement du projet avant validation définitive.
|
||||||
|
|
||||||
### `0.4.8-pre.007` — builders et exécuteur Solana Program Metadata
|
### `0.4.8-pre.007` — builders et exécuteur Solana Program Metadata
|
||||||
|
|
||||||
- PDA, calculs de taille et rent ;
|
- PDA, calculs de taille et rent ;
|
||||||
- builders contractuellement équivalents ;
|
- builders contractuellement équivalents pour toutes les opérations courantes, y compris les mutations destructrices, irréversibles ou écrasantes ;
|
||||||
- politiques de signataires, autorités et capacités.
|
- politiques de signataires, autorités, coûts, confirmations, postconditions et capacités ;
|
||||||
|
- intégration explicite au cadre commun `crate::ExSafetyChecker` : le builder expose le plan complet, tandis que la politique de sécurité décide si ce plan peut progresser vers la simulation, la signature et l’envoi ;
|
||||||
|
- classification opérationnelle conforme aux règles : opérations courantes exécutables, opérations officiellement obsolètes annotées `#[deprecated]` avec approbation explicite, versions officiellement remplacées conservées en decode-only ;
|
||||||
|
- état audit actuel : les neuf instructions stables `0..8` sont courantes, sans instruction remplacée ou obsolète, donc toutes les neuf doivent être ciblées par l’exécuteur sauf découverte officielle contraire documentée avant code.
|
||||||
|
|
||||||
### `0.4.8-pre.008` — pipeline stateful Solana Program Metadata
|
### `0.4.8-pre.008` — pipeline stateful Solana Program Metadata
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
<!-- file: kb-lib/CHANGELOG.md -->
|
<!-- file: kb-lib/CHANGELOG.md -->
|
||||||
<!-- version: 21 -->
|
<!-- version: 23 -->
|
||||||
|
|
||||||
# CHANGELOG — kb-lib
|
# CHANGELOG — kb-lib
|
||||||
|
|
||||||
|
## 0.4.8-pre.006 — matérialisation Solana Program Metadata
|
||||||
|
|
||||||
|
- active `MtMetadataMaterializer` comme matérialiseur contextualisé de la surface exacte `metadata.solana_program_metadata` ;
|
||||||
|
- matérialise les neuf instructions stables comme faits de mutation committés, sans leur attribuer l’autorité de l’état final ;
|
||||||
|
- matérialise les snapshots `Buffer` et `Metadata` comme états autoritatifs bornés, avec conservation intégrale des données directes et Buffer en base64 et SHA-256 ;
|
||||||
|
- conserve les URL et références `External` comme données on-chain sans fetch distant ni lecture implicite du compte référencé ;
|
||||||
|
- refuse les instructions échouées ou non committées et ignore les programmes, familles ou entrées étrangers ;
|
||||||
|
- ferme une matrice de onze projections : deux états de comptes et neuf faits d’instructions ;
|
||||||
|
- confirme que les neuf instructions stables sont courantes, non dépréciées et non remplacées ; l’ancien nom pré-stable du tag `5` reste une provenance de `Trim` seulement ;
|
||||||
|
- ajoute les helpers publics de matérialisation d’états et le test d’API externe correspondant.
|
||||||
|
- corrige le helper de test de snapshot afin d’utiliser les variantes wire existantes `Encoding::Utf8` et `Format::Json`.
|
||||||
|
- précise que les opérations destructrices, irréversibles ou écrasantes restent exécutables : leur progression vers simulation, signature et envoi doit être gouvernée par le cadre commun `ExSafetyChecker`, et non supprimée du périmètre de l’exécuteur.
|
||||||
|
|
||||||
## 0.4.8-pre.005 — instructions Solana Program Metadata
|
## 0.4.8-pre.005 — instructions Solana Program Metadata
|
||||||
|
|
||||||
- active `DcMetadataSolanaProgramMetadataDecoder` pour les neuf discriminants stables `0..8` ;
|
- active `DcMetadataSolanaProgramMetadataDecoder` pour les neuf discriminants stables `0..8` ;
|
||||||
@@ -14,6 +27,7 @@
|
|||||||
- retire le décodeur Solana Program Metadata de l’inventaire des décodeurs réservés.
|
- retire le décodeur Solana Program Metadata de l’inventaire des décodeurs réservés.
|
||||||
- aligne la structure du composant sur les règles du workspace : façade limitée aux sous-modules et réexports, implémentation déplacée dans `decoder.rs`, constantes centralisées dans `constants.rs` et accès internes via `crate::`.
|
- aligne la structure du composant sur les règles du workspace : façade limitée aux sous-modules et réexports, implémentation déplacée dans `decoder.rs`, constantes centralisées dans `constants.rs` et accès internes via `crate::`.
|
||||||
- corrige les vecteurs négatifs `Initialize` afin de placer explicitement les valeurs invalides après les 16 octets du seed et de vérifier séparément `Encoding`, `Compression`, `Format` et `DataSource`.
|
- corrige les vecteurs négatifs `Initialize` afin de placer explicitement les valeurs invalides après les 16 octets du seed et de vérifier séparément `Encoding`, `Compression`, `Format` et `DataSource`.
|
||||||
|
- validation reçue le 5 août 2026 : `cargo fmt`, `cargo check --workspace`, `cargo clippy --all-targets`, audit workspace et 660 tests unitaires `kb-lib` réussis, ainsi que les tests d’API externes et la matrice de nomenclature.
|
||||||
|
|
||||||
## 0.4.8-pre.004 — comptes Solana Program Metadata
|
## 0.4.8-pre.004 — comptes Solana Program Metadata
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: kb-lib/README.md -->
|
<!-- file: kb-lib/README.md -->
|
||||||
<!-- version: 22 -->
|
<!-- version: 23 -->
|
||||||
|
|
||||||
# kb-lib
|
# kb-lib
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
## Périmètre
|
## Périmètre
|
||||||
|
|
||||||
La crate couvre actuellement Solana Core, SPL Memo, SPL Token classique, SPL Associated Token Account, Token-2022, le registre SPL ElGamal et Metaplex Token Metadata. Elle expose également les modèles wire bornés, les décodeurs de comptes `Buffer` et `Metadata` et le décodeur contextualisé des neuf instructions stables de Solana Program Metadata.
|
La crate couvre actuellement Solana Core, SPL Memo, SPL Token classique, SPL Associated Token Account, Token-2022, le registre SPL ElGamal et Metaplex Token Metadata. Elle expose également les modèles wire bornés, les décodeurs de comptes `Buffer` et `Metadata`, le décodeur contextualisé des neuf instructions stables et la matérialisation exhaustive de Solana Program Metadata.
|
||||||
|
|
||||||
Pour Solana Program Metadata, la surface publique fondatrice comprend :
|
Pour Solana Program Metadata, la surface publique fondatrice comprend :
|
||||||
|
|
||||||
@@ -21,6 +21,9 @@ Pour Solana Program Metadata, la surface publique fondatrice comprend :
|
|||||||
- les neuf instructions stables `Write`, `Initialize`, `SetAuthority`, `SetData`, `SetImmutable`, `Trim`, `Close`, `Allocate` et `Extend` ;
|
- les neuf instructions stables `Write`, `Initialize`, `SetAuthority`, `SetData`, `SetImmutable`, `Trim`, `Close`, `Allocate` et `Extend` ;
|
||||||
- les formes runtime actuelles non produites par le SDK, notamment `SetData` sans remplacement de données et les suffixes ignorés de `SetImmutable`, `Trim` et `Close` ;
|
- les formes runtime actuelles non produites par le SDK, notamment `SetData` sans remplacement de données et les suffixes ignorés de `SetImmutable`, `Trim` et `Close` ;
|
||||||
- une frontière historique où l’ancien nom pré-stable `WithdrawExcessLamports` du tag `5` reste une provenance de `Trim`, sans dixième entrée de couverture.
|
- une frontière historique où l’ancien nom pré-stable `WithdrawExcessLamports` du tag `5` reste une provenance de `Trim`, sans dixième entrée de couverture.
|
||||||
|
- deux projections d’état autoritatives pour `Buffer` et `Metadata`, avec conservation bornée du contenu on-chain ;
|
||||||
|
- neuf faits de mutation d’instructions committées, non autoritatifs vis-à-vis de l’état final et réconciliables avec les snapshots post-exécution ;
|
||||||
|
- une classification où les neuf instructions stables sont courantes, sans opération officiellement remplacée ou obsolète à annoter `Deprecated`.
|
||||||
|
|
||||||
Pour Metaplex Token Metadata, elle fournit :
|
Pour Metaplex Token Metadata, elle fournit :
|
||||||
|
|
||||||
@@ -53,7 +56,7 @@ Pour Metaplex Token Metadata, elle fournit :
|
|||||||
- contrats `MtApi*` et matérialisateurs concrets ;
|
- contrats `MtApi*` et matérialisateurs concrets ;
|
||||||
- contrats `ExApi*`, politiques de sécurité et exécuteurs `Ex*Executor` ;
|
- contrats `ExApi*`, politiques de sécurité et exécuteurs `Ex*Executor` ;
|
||||||
- `ExMetadataMetaplexTokenMetadataExecutor`, `ExMetaplexTokenMetadataExecutionIntent` et `ExMetaplexTokenMetadataOperation` ;
|
- `ExMetadataMetaplexTokenMetadataExecutor`, `ExMetaplexTokenMetadataExecutionIntent` et `ExMetaplexTokenMetadataOperation` ;
|
||||||
- `DcMetadataSolanaProgramMetadataDecoder`, modèles `DcMetadataSpm*`, constantes `DC_METADATA_SPM_*` et fonctions `decoder_metadata_solana_program_metadata_decode_*_account` pour le contrat Solana Program Metadata ;
|
- `DcMetadataSolanaProgramMetadataDecoder`, modèles `DcMetadataSpm*`, constantes `DC_METADATA_SPM_*`, fonctions `decoder_metadata_solana_program_metadata_decode_*_account` et helpers `materializer_metadata_materialize_solana_program_metadata_*_snapshot` ;
|
||||||
- modèles canoniques `Md*` réexportés par la façade.
|
- modèles canoniques `Md*` réexportés par la façade.
|
||||||
|
|
||||||
Les exemples d’appel et invariants sont documentés dans [USAGE.md](USAGE.md).
|
Les exemples d’appel et invariants sont documentés dans [USAGE.md](USAGE.md).
|
||||||
@@ -68,7 +71,7 @@ Les exemples d’appel et invariants sont documentés dans [USAGE.md](USAGE.md).
|
|||||||
|
|
||||||
La surface fonctionnelle Metaplex de la crate est achevée. Les campagnes Devnet restantes relèvent de `kb-pipeline-demo-scenarios` et de `kb-app-demo-desktop`, non d’un manque de builder dans `kb-lib`.
|
La surface fonctionnelle Metaplex de la crate est achevée. Les campagnes Devnet restantes relèvent de `kb-pipeline-demo-scenarios` et de `kb-app-demo-desktop`, non d’un manque de builder dans `kb-lib`.
|
||||||
|
|
||||||
Pour Solana Program Metadata, les modèles wire, les décodeurs bornés de comptes et le décodeur contextualisé des neuf instructions stables sont actifs. `Buffer` accepte tout reliquat comme données allouées sans inventer de PDA stable après changement d’autorité ; `Metadata` valide la dérivation canonical ou non-canonical et distingue le contenu logique de la capacité ajoutée par `Extend`. Les variantes qui dépendent de l’état du compte restent des intentions wire jusqu’aux lectures stateful prévues, et aucune forme pré-stable n’est déclarée compatible réseau sans preuve de cluster.
|
Pour Solana Program Metadata, les modèles wire, les décodeurs bornés de comptes, le décodeur contextualisé et la matérialisation des deux comptes et des neuf instructions stables sont actifs. `Buffer` accepte tout reliquat comme données allouées sans inventer de PDA stable après changement d’autorité ; `Metadata` valide la dérivation canonical ou non-canonical et distingue le contenu logique de la capacité ajoutée par `Extend`. Les instructions produisent des faits committés non autoritatifs ; les snapshots post-exécution restent la source de l’état final. Les variantes qui dépendent de l’état du compte restent des intentions wire jusqu’aux lectures stateful prévues, et aucune forme pré-stable n’est déclarée compatible réseau sans preuve de cluster.
|
||||||
|
|
||||||
Le registre ElGamal n’est pas déclaré validé réellement sur réseau.
|
Le registre ElGamal n’est pas déclaré validé réellement sur réseau.
|
||||||
|
|
||||||
@@ -80,4 +83,5 @@ Le registre ElGamal n’est pas déclaré validé réellement sur réseau.
|
|||||||
- [Matrice contractuelle Metaplex](../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json)
|
- [Matrice contractuelle Metaplex](../test-fixtures/contract-matrices/METAPLEX_TOKEN_METADATA_MATRIX.json)
|
||||||
- [Matrice des comptes Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_ACCOUNT_MATRIX.json)
|
- [Matrice des comptes Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_ACCOUNT_MATRIX.json)
|
||||||
- [Matrice des instructions Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json)
|
- [Matrice des instructions Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_INSTRUCTION_MATRIX.json)
|
||||||
|
- [Matrice de matérialisation Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json)
|
||||||
- [Audit de l’inventaire et de l’historique des instructions](../docs/audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md)
|
- [Audit de l’inventaire et de l’historique des instructions](../docs/audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: kb-lib/USAGE.md -->
|
<!-- file: kb-lib/USAGE.md -->
|
||||||
<!-- version: 11 -->
|
<!-- version: 12 -->
|
||||||
|
|
||||||
# Utilisation de kb-lib
|
# Utilisation de kb-lib
|
||||||
|
|
||||||
@@ -153,6 +153,35 @@ println!("status={:?}", result.status);
|
|||||||
|
|
||||||
Le tag `5` est toujours publié comme `trim`. Son ancien nom pré-stable `withdraw_excess_lamports` est conservé uniquement comme provenance, car aucune release stable ne l’expose comme instruction distincte. Les URL et références externes restent des données on-chain ; le décodeur ne réalise aucun fetch.
|
Le tag `5` est toujours publié comme `trim`. Son ancien nom pré-stable `withdraw_excess_lamports` est conservé uniquement comme provenance, car aucune release stable ne l’expose comme instruction distincte. Les URL et références externes restent des données on-chain ; le décodeur ne réalise aucun fetch.
|
||||||
|
|
||||||
|
Le matérialiseur `MtMetadataMaterializer` accepte les neuf observations d’instructions de la surface exacte `metadata.solana_program_metadata`. Il refuse les transactions échouées et produit un fait de mutation par instruction. Ces faits ne remplacent pas un snapshot de compte : l’état final doit être matérialisé depuis la lecture post-exécution.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let materializer = kb_lib::MtMetadataMaterializer;
|
||||||
|
if kb_lib::MtApiEventMaterializer::accepts_observation(&materializer, &observation) {
|
||||||
|
let result = kb_lib::MtApiEventMaterializer::materialize(&materializer, &observation);
|
||||||
|
println!("materialization status={:?}", result.status);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Les trois helpers d’état acceptent les snapshots déjà validés par le décodeur :
|
||||||
|
|
||||||
|
- `materializer_metadata_materialize_solana_program_metadata_account_snapshot` distribue `Buffer` ou `Metadata` ;
|
||||||
|
- `materializer_metadata_materialize_solana_program_metadata_buffer_snapshot` conserve tous les octets du Buffer sous une forme bornée avec base64 et SHA-256 ;
|
||||||
|
- `materializer_metadata_materialize_solana_program_metadata_metadata_snapshot` conserve le contenu direct, l’URL ou la référence externe, ainsi que l’autorité, la mutabilité, le format et la capacité allouée.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let output = match kb_lib::materializer_metadata_materialize_solana_program_metadata_account_snapshot(
|
||||||
|
slot,
|
||||||
|
&snapshot,
|
||||||
|
) {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
};
|
||||||
|
println!("projection={}", output.payload_json);
|
||||||
|
```
|
||||||
|
|
||||||
|
Une URL reste une chaîne observée avec `fetched = false`. Une référence externe reste une adresse, un offset et une longueur avec `accountRead = false`. La matérialisation canonique ne réalise aucun enrichissement off-chain.
|
||||||
|
|
||||||
## Comptes Metaplex Token Metadata
|
## Comptes Metaplex Token Metadata
|
||||||
|
|
||||||
Les fonctions `decoder_metadata_metaplex_token_metadata_decode_*_account` décodent les familles de comptes publiques prises en charge. Le consommateur doit fournir les données et le contexte attendus par la signature exacte de la fonction concernée. Les parseurs conservent les variantes historiques et refusent les longueurs, propriétaires ou suffixes incompatibles.
|
Les fonctions `decoder_metadata_metaplex_token_metadata_decode_*_account` décodent les familles de comptes publiques prises en charge. Le consommateur doit fournir les données et le contexte attendus par la signature exacte de la fonction concernée. Les parseurs conservent les variantes historiques et refusent les longueurs, propriétaires ou suffixes incompatibles.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/lib.rs
|
// file: kb-lib/src/lib.rs
|
||||||
// version: 32
|
// version: 33
|
||||||
|
|
||||||
//! Consolidated decoder, executor, materializer and shared model library.
|
//! Consolidated decoder, executor, materializer and shared model library.
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
@@ -1391,7 +1391,7 @@ pub use self::materializer::MtLifecycleMaterializer;
|
|||||||
pub use self::materializer::MtLiquidityMaterializer;
|
pub use self::materializer::MtLiquidityMaterializer;
|
||||||
/// Exposes the legacy common business materializer trait.
|
/// Exposes the legacy common business materializer trait.
|
||||||
pub use self::materializer::MtMaterializer;
|
pub use self::materializer::MtMaterializer;
|
||||||
/// Token-2022 and Metaplex metadata snapshot materializer.
|
/// Token-2022, Metaplex and Solana Program Metadata materializer.
|
||||||
pub use self::materializer::MtMetadataMaterializer;
|
pub use self::materializer::MtMetadataMaterializer;
|
||||||
/// Reserved NFT materializer.
|
/// Reserved NFT materializer.
|
||||||
pub use self::materializer::MtNftMaterializer;
|
pub use self::materializer::MtNftMaterializer;
|
||||||
@@ -1439,6 +1439,12 @@ pub use self::materializer::materializer_fees_materialize_token_2022_state_snaps
|
|||||||
pub use self::materializer::materializer_metadata_materialize_metaplex_account_snapshot;
|
pub use self::materializer::materializer_metadata_materialize_metaplex_account_snapshot;
|
||||||
/// Materializes one canonical Metaplex Metadata account snapshot.
|
/// Materializes one canonical Metaplex Metadata account snapshot.
|
||||||
pub use self::materializer::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
pub use self::materializer::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
||||||
|
/// Materializes one decoded Solana Program Metadata account snapshot.
|
||||||
|
pub use self::materializer::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
|
||||||
|
pub use self::materializer::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
|
||||||
|
pub use self::materializer::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
|
||||||
/// Materializes Token-2022 embedded metadata and group snapshots.
|
/// Materializes Token-2022 embedded metadata and group snapshots.
|
||||||
pub use self::materializer::materializer_metadata_materialize_token_2022_snapshot;
|
pub use self::materializer::materializer_metadata_materialize_token_2022_snapshot;
|
||||||
/// Materializes one bounded Token-2022 account state snapshot.
|
/// Materializes one bounded Token-2022 account state snapshot.
|
||||||
@@ -2048,6 +2054,12 @@ pub(crate) use self::executor::executor_spl_token_2022_build_prepared_plan;
|
|||||||
pub(crate) use self::executor::executor_spl_token_build_prepared_plan;
|
pub(crate) use self::executor::executor_spl_token_build_prepared_plan;
|
||||||
/// Canonical processor name for the native and SPL administration materializer.
|
/// Canonical processor name for the native and SPL administration materializer.
|
||||||
pub(crate) use self::materializer::MT_ADMIN_PROCESSOR_NAME;
|
pub(crate) use self::materializer::MT_ADMIN_PROCESSOR_NAME;
|
||||||
|
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
||||||
|
pub(crate) use self::materializer::MT_METADATA_SPM_ACCEPTED_FAMILIES;
|
||||||
|
/// Stable processor name for Solana Program Metadata projections.
|
||||||
|
pub(crate) use self::materializer::MT_METADATA_SPM_PROCESSOR_NAME;
|
||||||
|
/// Stable projection schema version for Solana Program Metadata outputs.
|
||||||
|
pub(crate) use self::materializer::MT_METADATA_SPM_PROJECTION_VERSION;
|
||||||
/// Stable processor name for SPL Token and ATA risk facts.
|
/// Stable processor name for SPL Token and ATA risk facts.
|
||||||
pub(crate) use self::materializer::MT_RISK_PROCESSOR_NAME;
|
pub(crate) use self::materializer::MT_RISK_PROCESSOR_NAME;
|
||||||
/// Stable projection contract version for SPL Token and ATA risk facts.
|
/// Stable projection contract version for SPL Token and ATA risk facts.
|
||||||
@@ -2068,6 +2080,8 @@ pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_ADMIN;
|
|||||||
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
|
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
|
||||||
/// Canonical tracing target for the native lifecycle materializer.
|
/// Canonical tracing target for the native lifecycle materializer.
|
||||||
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
|
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
|
||||||
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
|
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
|
||||||
/// Canonical tracing target for SPL Token and ATA risk facts.
|
/// Canonical tracing target for SPL Token and ATA risk facts.
|
||||||
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_RISK;
|
pub(crate) use self::materializer::TRACING_TARGET_MATERIALIZER_RISK;
|
||||||
/// Canonical tracing target for the native stake and vote materializer.
|
/// Canonical tracing target for the native stake and vote materializer.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/src/materializer.rs
|
// file: kb-lib/src/materializer.rs
|
||||||
// version: 8
|
// version: 9
|
||||||
|
|
||||||
//! Consolidated materializer modules.
|
//! Consolidated materializer modules.
|
||||||
|
|
||||||
@@ -73,12 +73,18 @@ pub use self::lending::MtLendingMaterializer;
|
|||||||
pub use self::lifecycle::MtLifecycleMaterializer;
|
pub use self::lifecycle::MtLifecycleMaterializer;
|
||||||
/// Reserved liquidity materializer.
|
/// Reserved liquidity materializer.
|
||||||
pub use self::liquidity::MtLiquidityMaterializer;
|
pub use self::liquidity::MtLiquidityMaterializer;
|
||||||
/// Token-2022 and Metaplex metadata snapshot materializer.
|
/// Token-2022, Metaplex and Solana Program Metadata materializer.
|
||||||
pub use self::metadata::MtMetadataMaterializer;
|
pub use self::metadata::MtMetadataMaterializer;
|
||||||
/// Materializes one canonical non-Metadata Metaplex account snapshot.
|
/// Materializes one canonical non-Metadata Metaplex account snapshot.
|
||||||
pub use self::metadata::materializer_metadata_materialize_metaplex_account_snapshot;
|
pub use self::metadata::materializer_metadata_materialize_metaplex_account_snapshot;
|
||||||
/// Materializes one canonical Metaplex Metadata account snapshot.
|
/// Materializes one canonical Metaplex Metadata account snapshot.
|
||||||
pub use self::metadata::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
pub use self::metadata::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
||||||
|
/// Materializes one decoded Solana Program Metadata account snapshot.
|
||||||
|
pub use self::metadata::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
|
||||||
|
pub use self::metadata::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
|
||||||
|
pub use self::metadata::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
|
||||||
/// Materializes Token-2022 embedded metadata and group snapshots.
|
/// Materializes Token-2022 embedded metadata and group snapshots.
|
||||||
pub use self::metadata::materializer_metadata_materialize_token_2022_snapshot;
|
pub use self::metadata::materializer_metadata_materialize_token_2022_snapshot;
|
||||||
/// Reserved NFT materializer.
|
/// Reserved NFT materializer.
|
||||||
@@ -120,6 +126,14 @@ pub(crate) use self::admin::TRACING_TARGET_MATERIALIZER_ADMIN;
|
|||||||
pub(crate) use self::compliance::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
|
pub(crate) use self::compliance::TRACING_TARGET_MATERIALIZER_COMPLIANCE_AUDIT;
|
||||||
/// Canonical tracing target for the native lifecycle materializer.
|
/// Canonical tracing target for the native lifecycle materializer.
|
||||||
pub(crate) use self::lifecycle::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
|
pub(crate) use self::lifecycle::TRACING_TARGET_MATERIALIZER_LIFECYCLE;
|
||||||
|
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
||||||
|
pub(crate) use self::metadata::MT_METADATA_SPM_ACCEPTED_FAMILIES;
|
||||||
|
/// Stable processor name for Solana Program Metadata projections.
|
||||||
|
pub(crate) use self::metadata::MT_METADATA_SPM_PROCESSOR_NAME;
|
||||||
|
/// Stable projection schema version for Solana Program Metadata outputs.
|
||||||
|
pub(crate) use self::metadata::MT_METADATA_SPM_PROJECTION_VERSION;
|
||||||
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
|
pub(crate) use self::metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
|
||||||
/// Stable processor name for SPL Token and ATA risk facts.
|
/// Stable processor name for SPL Token and ATA risk facts.
|
||||||
pub(crate) use self::risk::MT_RISK_PROCESSOR_NAME;
|
pub(crate) use self::risk::MT_RISK_PROCESSOR_NAME;
|
||||||
/// Stable projection contract version for SPL Token and ATA risk facts.
|
/// Stable projection contract version for SPL Token and ATA risk facts.
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// file: kb-lib/src/materializer/metadata.rs
|
// file: kb-lib/src/materializer/metadata.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
//! `metadata` materializer family.
|
//! `metadata` materializer family.
|
||||||
|
|
||||||
mod core;
|
mod core;
|
||||||
|
mod solana_program_metadata;
|
||||||
|
|
||||||
/// Token-2022 and Metaplex metadata snapshot materializer.
|
/// Token-2022, Metaplex and Solana Program Metadata materializer.
|
||||||
pub use self::core::MtMetadataMaterializer;
|
pub use self::core::MtMetadataMaterializer;
|
||||||
/// Materializes one canonical non-Metadata Metaplex account snapshot.
|
/// Materializes one canonical non-Metadata Metaplex account snapshot.
|
||||||
pub use self::core::materializer_metadata_materialize_metaplex_account_snapshot;
|
pub use self::core::materializer_metadata_materialize_metaplex_account_snapshot;
|
||||||
@@ -13,3 +14,18 @@ pub use self::core::materializer_metadata_materialize_metaplex_account_snapshot;
|
|||||||
pub use self::core::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
pub use self::core::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
||||||
/// Materializes Token-2022 embedded metadata and group snapshots.
|
/// Materializes Token-2022 embedded metadata and group snapshots.
|
||||||
pub use self::core::materializer_metadata_materialize_token_2022_snapshot;
|
pub use self::core::materializer_metadata_materialize_token_2022_snapshot;
|
||||||
|
/// Materializes one decoded Solana Program Metadata account snapshot.
|
||||||
|
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
|
||||||
|
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
|
||||||
|
pub use self::solana_program_metadata::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
|
||||||
|
|
||||||
|
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
||||||
|
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_ACCEPTED_FAMILIES;
|
||||||
|
/// Stable processor name for Solana Program Metadata projections.
|
||||||
|
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_PROCESSOR_NAME;
|
||||||
|
/// Stable projection schema version for Solana Program Metadata outputs.
|
||||||
|
pub(crate) use self::solana_program_metadata::MT_METADATA_SPM_PROJECTION_VERSION;
|
||||||
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
|
pub(crate) use self::solana_program_metadata::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// file: kb-lib/src/materializer/metadata/core.rs
|
// file: kb-lib/src/materializer/metadata/core.rs
|
||||||
// version: 6
|
// version: 7
|
||||||
|
|
||||||
//! Stable Token-2022 and Metaplex metadata snapshot materializer.
|
//! Stable Token-2022, Metaplex and Solana Program Metadata materializer.
|
||||||
|
|
||||||
/// Initial materializer implementation placeholder.
|
/// Initial materializer implementation placeholder.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@@ -16,8 +16,13 @@ impl crate::MtMaterializer for crate::MtMetadataMaterializer {
|
|||||||
return env!("CARGO_PKG_VERSION");
|
return env!("CARGO_PKG_VERSION");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accepts_event(&self, _event: &crate::MdDecodedProtocolEvent) -> bool {
|
fn accepts_event(&self, event: &crate::MdDecodedProtocolEvent) -> bool {
|
||||||
return false;
|
return event.event_family == crate::MdEventFamily::Metadata
|
||||||
|
&& event.program_id.0 == kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID
|
||||||
|
&& event.surface_code.0 == crate::DC_METADATA_SPM_SURFACE_CODE
|
||||||
|
&& crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES
|
||||||
|
.iter()
|
||||||
|
.any(|(_, code)| return *code == event.event_name.0.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn materialize_event(
|
fn materialize_event(
|
||||||
|
|||||||
24
kb-lib/src/materializer/metadata/solana_program_metadata.rs
Normal file
24
kb-lib/src/materializer/metadata/solana_program_metadata.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// file: kb-lib/src/materializer/metadata/solana_program_metadata.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Solana Program Metadata materialization surface.
|
||||||
|
|
||||||
|
mod constants;
|
||||||
|
mod instruction;
|
||||||
|
mod state;
|
||||||
|
|
||||||
|
/// Materializes one decoded Solana Program Metadata account snapshot.
|
||||||
|
pub use self::state::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
|
||||||
|
pub use self::state::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
||||||
|
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
|
||||||
|
pub use self::state::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
|
||||||
|
|
||||||
|
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
||||||
|
pub(crate) use self::constants::MT_METADATA_SPM_ACCEPTED_FAMILIES;
|
||||||
|
/// Stable processor name for Solana Program Metadata projections.
|
||||||
|
pub(crate) use self::constants::MT_METADATA_SPM_PROCESSOR_NAME;
|
||||||
|
/// Stable projection schema version for Solana Program Metadata outputs.
|
||||||
|
pub(crate) use self::constants::MT_METADATA_SPM_PROJECTION_VERSION;
|
||||||
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
|
pub(crate) use self::constants::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// file: kb-lib/src/materializer/metadata/solana_program_metadata/constants.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Solana Program Metadata materializer constants.
|
||||||
|
|
||||||
|
/// Event families accepted by the Solana Program Metadata instruction materializer.
|
||||||
|
pub(crate) const MT_METADATA_SPM_ACCEPTED_FAMILIES: &[crate::MdEventFamily] =
|
||||||
|
&[crate::MdEventFamily::Metadata];
|
||||||
|
/// Stable processor name for Solana Program Metadata projections.
|
||||||
|
pub(crate) const MT_METADATA_SPM_PROCESSOR_NAME: &str =
|
||||||
|
"kb-lib.materializer.metadata.solana_program_metadata";
|
||||||
|
/// Stable projection schema version for Solana Program Metadata outputs.
|
||||||
|
pub(crate) const MT_METADATA_SPM_PROJECTION_VERSION: u16 = 1;
|
||||||
|
/// Canonical tracing target for Solana Program Metadata materialization.
|
||||||
|
pub(crate) const TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA: &str =
|
||||||
|
"kb-lib.materializer.metadata.solana_program_metadata";
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
// file: kb-lib/src/materializer/metadata/solana_program_metadata/instruction.rs
|
||||||
|
// version: 1
|
||||||
|
|
||||||
|
//! Committed Solana Program Metadata instruction projections.
|
||||||
|
|
||||||
|
impl crate::MtApiEventMaterializer for crate::MtMetadataMaterializer {
|
||||||
|
fn identity(&self) -> crate::MtApiMaterializerIdentity {
|
||||||
|
return crate::MtApiMaterializerIdentity {
|
||||||
|
name: crate::MT_METADATA_SPM_PROCESSOR_NAME.to_string(),
|
||||||
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accepted_families(&self) -> &'static [crate::MdEventFamily] {
|
||||||
|
return crate::MT_METADATA_SPM_ACCEPTED_FAMILIES;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accepts_observation(&self, observation: &crate::DcApiDecodedObservation) -> bool {
|
||||||
|
return observation.event.event_family == crate::MdEventFamily::Metadata
|
||||||
|
&& observation.event.program_id.0
|
||||||
|
== kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID
|
||||||
|
&& observation.event.surface_code.0 == crate::DC_METADATA_SPM_SURFACE_CODE
|
||||||
|
&& payload_contract_matches(observation)
|
||||||
|
&& crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES
|
||||||
|
.iter()
|
||||||
|
.any(|(_, code)| return *code == observation.event.event_name.0.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction_policy(
|
||||||
|
&self,
|
||||||
|
_family: crate::MdEventFamily,
|
||||||
|
) -> crate::MtApiMaterializationTransactionPolicy {
|
||||||
|
return crate::MtApiMaterializationTransactionPolicy::SuccessfulCommittedOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn materialize(
|
||||||
|
&self,
|
||||||
|
observation: &crate::DcApiDecodedObservation,
|
||||||
|
) -> crate::MtApiMaterializerExecutionResult {
|
||||||
|
if !crate::MtApiEventMaterializer::accepts_observation(self, observation) {
|
||||||
|
return crate::MtApiMaterializerExecutionResult::ignored();
|
||||||
|
}
|
||||||
|
if observation.transaction_failed || !observation.observation_committed {
|
||||||
|
return crate::MtApiMaterializerExecutionResult::refused(
|
||||||
|
"failed_transaction_solana_program_metadata_refused",
|
||||||
|
"failed or uncommitted Solana Program Metadata instructions cannot create mutation outputs",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let operation = observation.event.event_name.0.as_str();
|
||||||
|
let projection = instruction_projection(operation, &observation.payload_json);
|
||||||
|
let output = crate::MtApiMaterializedOutput {
|
||||||
|
output_key: format!(
|
||||||
|
"solana_program_metadata_instruction:{}:{}:{}",
|
||||||
|
observation.event.signature.0, observation.event.instruction_path.0, operation
|
||||||
|
),
|
||||||
|
family: crate::MdMaterializedEventFamily::Metadata,
|
||||||
|
payload_json: serde_json::json!({
|
||||||
|
"projectionVersion": crate::MT_METADATA_SPM_PROJECTION_VERSION,
|
||||||
|
"domain": "solana_program_metadata",
|
||||||
|
"projectionKind": "committed_instruction_mutation",
|
||||||
|
"projectionSemantics": "committed_instruction_fact_non_authoritative_state",
|
||||||
|
"operation": operation,
|
||||||
|
"programId": observation.event.program_id.0.clone(),
|
||||||
|
"signature": observation.event.signature.0.clone(),
|
||||||
|
"slot": observation.event.slot.0,
|
||||||
|
"instructionPath": observation.event.instruction_path.0.clone(),
|
||||||
|
"sourceKind": observation.event.source_kind,
|
||||||
|
"transactionSucceeded": true,
|
||||||
|
"stateProjectionAuthority": "post_execution_account_snapshot",
|
||||||
|
"authoritativeFinalStateCaptured": false,
|
||||||
|
"projection": projection,
|
||||||
|
"decodedPayload": observation.payload_json.clone(),
|
||||||
|
"externalContentFetched": false,
|
||||||
|
"externalAccountRead": false,
|
||||||
|
"provenance": {
|
||||||
|
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
|
||||||
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
|
"decoderProof": observation.proof.clone()
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
tracing::debug!(
|
||||||
|
target: crate::TRACING_TARGET_MATERIALIZER_METADATA_SOLANA_PROGRAM_METADATA,
|
||||||
|
action = "materialize_solana_program_metadata_instruction",
|
||||||
|
operation = operation,
|
||||||
|
signature = %observation.event.signature.0,
|
||||||
|
instruction_path = %observation.event.instruction_path.0,
|
||||||
|
output_count = 1_usize,
|
||||||
|
"materialize committed Solana Program Metadata instruction"
|
||||||
|
);
|
||||||
|
return crate::MtApiMaterializerExecutionResult {
|
||||||
|
status: crate::MtApiMaterializerOutcomeStatus::Inserted,
|
||||||
|
outputs: std::vec![output],
|
||||||
|
diagnostics: std::vec::Vec::new(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn payload_contract_matches(observation: &crate::DcApiDecodedObservation) -> bool {
|
||||||
|
let instruction =
|
||||||
|
observation.payload_json.get("instruction").and_then(serde_json::Value::as_str);
|
||||||
|
let program_id = observation.payload_json.get("programId").and_then(serde_json::Value::as_str);
|
||||||
|
return instruction == std::option::Option::Some(observation.event.event_name.0.as_str())
|
||||||
|
&& program_id
|
||||||
|
== std::option::Option::Some(
|
||||||
|
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instruction_projection(operation: &str, payload: &serde_json::Value) -> serde_json::Value {
|
||||||
|
let parameters = match payload.get("parameters") {
|
||||||
|
std::option::Option::Some(value) => value.clone(),
|
||||||
|
std::option::Option::None => serde_json::Value::Null,
|
||||||
|
};
|
||||||
|
let accounts = match payload.get("accounts") {
|
||||||
|
std::option::Option::Some(value) => value.clone(),
|
||||||
|
std::option::Option::None => serde_json::Value::Null,
|
||||||
|
};
|
||||||
|
return serde_json::json!({
|
||||||
|
"stateTransition": state_transition(operation),
|
||||||
|
"targetAccount": target_account(operation, &accounts),
|
||||||
|
"authority": account_for_role(&accounts, "authority"),
|
||||||
|
"destination": account_for_role(&accounts, "destination"),
|
||||||
|
"sourceBuffer": account_for_role(&accounts, "source_buffer"),
|
||||||
|
"buffer": account_for_role(&accounts, "buffer"),
|
||||||
|
"program": account_for_role(&accounts, "program"),
|
||||||
|
"programData": account_for_role(&accounts, "program_data"),
|
||||||
|
"parameters": parameters,
|
||||||
|
"accounts": accounts,
|
||||||
|
"currentStableInstruction": true,
|
||||||
|
"deprecated": false,
|
||||||
|
"replaced": false,
|
||||||
|
"executionPolicy": "current_candidate",
|
||||||
|
"preStableAlias": if operation == "trim" {
|
||||||
|
serde_json::Value::String("withdraw_excess_lamports".to_string())
|
||||||
|
} else {
|
||||||
|
serde_json::Value::Null
|
||||||
|
},
|
||||||
|
"preStableAliasDecodedSeparately": false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_transition(operation: &str) -> &'static str {
|
||||||
|
match operation {
|
||||||
|
"write" => return "buffer_content_written",
|
||||||
|
"initialize" => return "metadata_account_initialized",
|
||||||
|
"set_authority" => return "account_authority_updated",
|
||||||
|
"set_data" => return "metadata_data_configuration_updated",
|
||||||
|
"set_immutable" => return "metadata_account_marked_immutable",
|
||||||
|
"trim" => return "account_capacity_trimmed",
|
||||||
|
"close" => return "account_closed",
|
||||||
|
"allocate" => return "buffer_account_allocated",
|
||||||
|
"extend" => return "account_capacity_extended",
|
||||||
|
_ => return "unknown",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_account(operation: &str, accounts: &serde_json::Value) -> serde_json::Value {
|
||||||
|
let roles: &[&str] = match operation {
|
||||||
|
"write" | "allocate" => &["buffer"],
|
||||||
|
"initialize" | "set_data" | "set_immutable" => &["metadata"],
|
||||||
|
"set_authority" | "trim" | "close" | "extend" => &["account"],
|
||||||
|
_ => &[],
|
||||||
|
};
|
||||||
|
for role in roles {
|
||||||
|
let value = account_for_role(accounts, role);
|
||||||
|
if !value.is_null() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serde_json::Value::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn account_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value {
|
||||||
|
let values = match accounts.as_array() {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return serde_json::Value::Null,
|
||||||
|
};
|
||||||
|
for value in values {
|
||||||
|
let candidate = value.get("role").and_then(serde_json::Value::as_str);
|
||||||
|
let present = match value.get("present").and_then(serde_json::Value::as_bool) {
|
||||||
|
std::option::Option::Some(candidate) => candidate,
|
||||||
|
std::option::Option::None => true,
|
||||||
|
};
|
||||||
|
if candidate == std::option::Option::Some(role) && present {
|
||||||
|
return match value.get("accountKey") {
|
||||||
|
std::option::Option::Some(candidate) => candidate.clone(),
|
||||||
|
std::option::Option::None => serde_json::Value::Null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serde_json::Value::Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
const MATRIX_JSON: &str = include_str!(
|
||||||
|
"../../../../../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json"
|
||||||
|
);
|
||||||
|
|
||||||
|
fn observation(operation: &str, committed: bool) -> crate::DcApiDecodedObservation {
|
||||||
|
return crate::DcApiDecodedObservation {
|
||||||
|
event_key: format!("solana_program_metadata:{operation}:0"),
|
||||||
|
event: crate::MdDecodedProtocolEvent {
|
||||||
|
signature: crate::MdSignature("signature111".to_string()),
|
||||||
|
slot: crate::MdSlot(42),
|
||||||
|
instruction_path: crate::MdInstructionPath("0".to_string()),
|
||||||
|
program_id: crate::MdProgramId(
|
||||||
|
kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID.to_string(),
|
||||||
|
),
|
||||||
|
protocol_code: crate::MdProtocolCode(
|
||||||
|
crate::DC_METADATA_SPM_SURFACE_CODE.to_string(),
|
||||||
|
),
|
||||||
|
surface_code: crate::MdSurfaceCode(crate::DC_METADATA_SPM_SURFACE_CODE.to_string()),
|
||||||
|
event_code: crate::MdEventCode(format!(
|
||||||
|
"{}.{}",
|
||||||
|
crate::DC_METADATA_SPM_SURFACE_CODE,
|
||||||
|
operation
|
||||||
|
)),
|
||||||
|
event_name: crate::MdEventName(operation.to_string()),
|
||||||
|
event_family: crate::MdEventFamily::Metadata,
|
||||||
|
source_kind: crate::MdEventSourceKind::Instruction,
|
||||||
|
confidence: crate::MdDecoderConfidence::ManualExact,
|
||||||
|
},
|
||||||
|
payload_json: serde_json::json!({
|
||||||
|
"programId": kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
|
||||||
|
"instruction": operation,
|
||||||
|
"parameters": {"length": 8},
|
||||||
|
"accounts": [
|
||||||
|
{"role":"account","accountKey":"account111","present":true},
|
||||||
|
{"role":"authority","accountKey":"authority111","present":true}
|
||||||
|
],
|
||||||
|
"wire": {"sha256":"00"}
|
||||||
|
}),
|
||||||
|
transaction_failed: !committed,
|
||||||
|
transaction_error: if committed {
|
||||||
|
std::option::Option::None
|
||||||
|
} else {
|
||||||
|
std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]}))
|
||||||
|
},
|
||||||
|
observation_committed: committed,
|
||||||
|
proof: crate::DcApiDecoderProof {
|
||||||
|
kind: crate::DcApiDecoderProofKind::Manual,
|
||||||
|
confidence: crate::MdDecoderConfidence::ManualExact,
|
||||||
|
evidence: std::vec!["wire".to_string()],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_stable_instruction_has_one_materialized_mutation_fact() {
|
||||||
|
let materializer = crate::MtMetadataMaterializer;
|
||||||
|
for (_, operation) in crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES.iter().copied() {
|
||||||
|
let value = observation(operation, true);
|
||||||
|
assert!(crate::MtApiEventMaterializer::accepts_observation(&materializer, &value));
|
||||||
|
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
|
||||||
|
assert_eq!(result.status, crate::MtApiMaterializerOutcomeStatus::Inserted);
|
||||||
|
assert_eq!(result.outputs.len(), 1);
|
||||||
|
assert_eq!(result.outputs[0].payload_json["operation"], *operation);
|
||||||
|
assert_eq!(result.outputs[0].payload_json["projection"]["deprecated"], false);
|
||||||
|
assert_eq!(result.outputs[0].payload_json["projection"]["replaced"], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn failed_instruction_is_refused_and_foreign_surface_is_ignored() {
|
||||||
|
let materializer = crate::MtMetadataMaterializer;
|
||||||
|
let failed = observation("set_data", false);
|
||||||
|
let failure = crate::MtApiEventMaterializer::materialize(&materializer, &failed);
|
||||||
|
assert_eq!(failure.status, crate::MtApiMaterializerOutcomeStatus::Refused);
|
||||||
|
let mut foreign = observation("set_data", true);
|
||||||
|
foreign.event.program_id =
|
||||||
|
crate::MdProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string());
|
||||||
|
let ignored = crate::MtApiEventMaterializer::materialize(&materializer, &foreign);
|
||||||
|
assert_eq!(ignored.status, crate::MtApiMaterializerOutcomeStatus::Ignored);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn materialization_matrix_matches_two_account_states_and_nine_instructions() {
|
||||||
|
let parsed = serde_json::from_str::<serde_json::Value>(MATRIX_JSON);
|
||||||
|
assert!(parsed.is_ok());
|
||||||
|
let matrix = match parsed {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return,
|
||||||
|
};
|
||||||
|
let accounts = match matrix.get("accountSnapshots").and_then(serde_json::Value::as_array) {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return,
|
||||||
|
};
|
||||||
|
let instructions =
|
||||||
|
match matrix.get("instructionFacts").and_then(serde_json::Value::as_array) {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => return,
|
||||||
|
};
|
||||||
|
assert_eq!(accounts.len(), 2);
|
||||||
|
assert_eq!(instructions.len(), crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES.len());
|
||||||
|
for (position, (tag, operation)) in
|
||||||
|
crate::DC_METADATA_SPM_INSTRUCTION_ENTRIES.iter().copied().enumerate()
|
||||||
|
{
|
||||||
|
assert_eq!(instructions[position]["tag"], tag);
|
||||||
|
assert_eq!(instructions[position]["entryCode"], operation);
|
||||||
|
assert_eq!(instructions[position]["deprecated"], false);
|
||||||
|
assert_eq!(instructions[position]["replaced"], false);
|
||||||
|
assert_eq!(
|
||||||
|
instructions[position]["stateTransition"],
|
||||||
|
super::state_transition(operation)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trim_keeps_prestable_alias_as_provenance_only() {
|
||||||
|
let materializer = crate::MtMetadataMaterializer;
|
||||||
|
let value = observation("trim", true);
|
||||||
|
let result = crate::MtApiEventMaterializer::materialize(&materializer, &value);
|
||||||
|
assert_eq!(
|
||||||
|
result.outputs[0].payload_json["projection"]["preStableAlias"],
|
||||||
|
"withdraw_excess_lamports"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result.outputs[0].payload_json["projection"]["preStableAliasDecodedSeparately"],
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,343 @@
|
|||||||
|
// file: kb-lib/src/materializer/metadata/solana_program_metadata/state.rs
|
||||||
|
// version: 2
|
||||||
|
|
||||||
|
//! Authoritative Solana Program Metadata account-state projections.
|
||||||
|
|
||||||
|
use base64::Engine; // rust-rules: trait-import
|
||||||
|
use sha2::Digest; // rust-rules: trait-import
|
||||||
|
|
||||||
|
/// Materializes one decoded Solana Program Metadata account snapshot.
|
||||||
|
pub fn materializer_metadata_materialize_solana_program_metadata_account_snapshot(
|
||||||
|
slot: u64,
|
||||||
|
snapshot: &crate::DcMetadataSpmAccountSnapshot,
|
||||||
|
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
|
||||||
|
match snapshot {
|
||||||
|
crate::DcMetadataSpmAccountSnapshot::Buffer(value) => {
|
||||||
|
return crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
|
||||||
|
slot,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
crate::DcMetadataSpmAccountSnapshot::Metadata(value) => {
|
||||||
|
return crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
|
||||||
|
slot,
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Materializes one bounded Solana Program Metadata Buffer account snapshot.
|
||||||
|
pub fn materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
|
||||||
|
slot: u64,
|
||||||
|
snapshot: &crate::DcMetadataSpmBufferAccountSnapshot,
|
||||||
|
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
|
||||||
|
if snapshot.account.trim().is_empty() {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata Buffer materialization requires a non-empty account"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if snapshot.program.as_ref().is_some_and(|value| return value.trim().is_empty())
|
||||||
|
|| snapshot.authority.as_ref().is_some_and(|value| return value.trim().is_empty())
|
||||||
|
{
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata Buffer optional identities must not be empty".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if snapshot.data.len() != snapshot.allocated_data_bytes {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata Buffer allocated byte count does not match decoded data"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if snapshot.allocated_data_bytes
|
||||||
|
> crate::DC_METADATA_SPM_MAX_ACCOUNT_BYTES
|
||||||
|
.saturating_sub(crate::DC_METADATA_SPM_BUFFER_HEADER_BYTES)
|
||||||
|
{
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata Buffer exceeds the bounded account-data contract".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let seed_hex = hex(snapshot.seed.as_bytes());
|
||||||
|
let data = bytes_projection(snapshot.data.as_slice());
|
||||||
|
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
|
||||||
|
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
|
||||||
|
family: crate::MdMaterializedEventFamily::Metadata,
|
||||||
|
payload_json: serde_json::json!({
|
||||||
|
"projectionVersion": crate::MT_METADATA_SPM_PROJECTION_VERSION,
|
||||||
|
"domain": "solana_program_metadata",
|
||||||
|
"projectionKind": "buffer_state",
|
||||||
|
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
|
||||||
|
"account": snapshot.account,
|
||||||
|
"slot": slot,
|
||||||
|
"programId": kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
|
||||||
|
"accountDiscriminator": "buffer",
|
||||||
|
"metadataProgram": snapshot.program,
|
||||||
|
"authority": snapshot.authority,
|
||||||
|
"mutable": snapshot.authority.is_some(),
|
||||||
|
"canonical": snapshot.canonical,
|
||||||
|
"seedHex": seed_hex,
|
||||||
|
"allocatedDataBytes": snapshot.allocated_data_bytes,
|
||||||
|
"data": data,
|
||||||
|
"metadataSource": "solana_program_metadata_owned_buffer_account",
|
||||||
|
"externalContentFetched": false,
|
||||||
|
"externalAccountRead": false,
|
||||||
|
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
|
||||||
|
"provenance": {
|
||||||
|
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
|
||||||
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
|
"source": "bounded_solana_program_metadata_account_decoder"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Materializes one bounded Solana Program Metadata Metadata account snapshot.
|
||||||
|
pub fn materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
|
||||||
|
slot: u64,
|
||||||
|
snapshot: &crate::DcMetadataSpmMetadataAccountSnapshot,
|
||||||
|
) -> std::result::Result<crate::MtApiMaterializedOutput, String> {
|
||||||
|
if snapshot.account.trim().is_empty() || snapshot.program.trim().is_empty() {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata state materialization requires account and program identities"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if snapshot.authority.as_ref().is_some_and(|value| return value.trim().is_empty()) {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata authority must not be empty when present".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if snapshot.data_source != snapshot.data.source() {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata data source does not match decoded content".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let std::result::Result::Err(error) = snapshot.data.validate() {
|
||||||
|
return std::result::Result::Err(error.to_string());
|
||||||
|
}
|
||||||
|
let logical_data_bytes = match usize::try_from(snapshot.data_length) {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata logical data length is not representable".to_string(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if logical_data_bytes != snapshot.data.wire_byte_len() {
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata logical data length does not match decoded content"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if snapshot.allocated_data_bytes
|
||||||
|
> crate::DC_METADATA_SPM_MAX_ACCOUNT_BYTES
|
||||||
|
.saturating_sub(crate::DC_METADATA_SPM_METADATA_HEADER_BYTES)
|
||||||
|
{
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata state exceeds the bounded account-data contract".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if snapshot.allocated_data_bytes
|
||||||
|
!= logical_data_bytes.saturating_add(snapshot.trailing_capacity_bytes)
|
||||||
|
{
|
||||||
|
return std::result::Result::Err(
|
||||||
|
"Solana Program Metadata allocated capacity is inconsistent".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let seed_hex = hex(snapshot.seed.as_bytes());
|
||||||
|
let data = data_projection(&snapshot.data);
|
||||||
|
return std::result::Result::Ok(crate::MtApiMaterializedOutput {
|
||||||
|
output_key: format!("solana_program_metadata_account_state:{}", snapshot.account),
|
||||||
|
family: crate::MdMaterializedEventFamily::Metadata,
|
||||||
|
payload_json: serde_json::json!({
|
||||||
|
"projectionVersion": crate::MT_METADATA_SPM_PROJECTION_VERSION,
|
||||||
|
"domain": "solana_program_metadata",
|
||||||
|
"projectionKind": "metadata_state",
|
||||||
|
"projectionSemantics": "authoritative_bounded_owned_account_snapshot",
|
||||||
|
"account": snapshot.account,
|
||||||
|
"slot": slot,
|
||||||
|
"programId": kb_program_ids::METADATA_SOLANA_PROGRAM_METADATA_PROGRAM_ID,
|
||||||
|
"accountDiscriminator": "metadata",
|
||||||
|
"bump": snapshot.bump,
|
||||||
|
"metadataProgram": snapshot.program,
|
||||||
|
"authority": snapshot.authority,
|
||||||
|
"mutable": snapshot.mutable,
|
||||||
|
"canonical": snapshot.canonical,
|
||||||
|
"seedHex": seed_hex,
|
||||||
|
"encoding": snapshot.encoding,
|
||||||
|
"compression": snapshot.compression,
|
||||||
|
"format": snapshot.format,
|
||||||
|
"dataSource": snapshot.data_source,
|
||||||
|
"dataLength": snapshot.data_length,
|
||||||
|
"allocatedDataBytes": snapshot.allocated_data_bytes,
|
||||||
|
"trailingCapacityBytes": snapshot.trailing_capacity_bytes,
|
||||||
|
"data": data,
|
||||||
|
"metadataSource": "solana_program_metadata_owned_metadata_account",
|
||||||
|
"externalContentFetched": false,
|
||||||
|
"externalAccountRead": false,
|
||||||
|
"instructionProjectionRole": "mutation_observation_only_no_duplicate_state",
|
||||||
|
"provenance": {
|
||||||
|
"processorName": crate::MT_METADATA_SPM_PROCESSOR_NAME,
|
||||||
|
"processorVersion": env!("CARGO_PKG_VERSION"),
|
||||||
|
"source": "bounded_solana_program_metadata_account_decoder"
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_projection(data: &crate::DcMetadataSpmData) -> serde_json::Value {
|
||||||
|
match data {
|
||||||
|
crate::DcMetadataSpmData::Direct(value) => {
|
||||||
|
let mut projection = bytes_projection(value.as_slice());
|
||||||
|
projection["kind"] = serde_json::json!("direct");
|
||||||
|
return projection;
|
||||||
|
},
|
||||||
|
crate::DcMetadataSpmData::Url(value) => {
|
||||||
|
return serde_json::json!({
|
||||||
|
"kind": "url",
|
||||||
|
"value": value,
|
||||||
|
"lengthBytes": value.len(),
|
||||||
|
"sha256": hash(value.as_bytes()),
|
||||||
|
"fetched": false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
crate::DcMetadataSpmData::External(value) => {
|
||||||
|
return serde_json::json!({
|
||||||
|
"kind": "external",
|
||||||
|
"address": value.address.to_string(),
|
||||||
|
"offset": value.offset,
|
||||||
|
"length": value.length.map(std::num::NonZeroU32::get),
|
||||||
|
"wireLengthBytes": crate::DC_METADATA_SPM_EXTERNAL_DATA_BYTES,
|
||||||
|
"accountRead": false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytes_projection(bytes: &[u8]) -> serde_json::Value {
|
||||||
|
return serde_json::json!({
|
||||||
|
"lengthBytes": bytes.len(),
|
||||||
|
"sha256": hash(bytes),
|
||||||
|
"base64": base64::engine::general_purpose::STANDARD.encode(bytes)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash(bytes: &[u8]) -> std::string::String {
|
||||||
|
let digest = sha2::Sha256::digest(bytes);
|
||||||
|
let mut output = std::string::String::with_capacity(64);
|
||||||
|
for byte in digest {
|
||||||
|
output.push_str(format!("{byte:02x}").as_str());
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hex(bytes: &[u8]) -> std::string::String {
|
||||||
|
let mut output = std::string::String::with_capacity(bytes.len().saturating_mul(2));
|
||||||
|
for byte in bytes {
|
||||||
|
output.push_str(format!("{byte:02x}").as_str());
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn buffer_snapshot_preserves_all_bounded_bytes_and_authority_state() {
|
||||||
|
let snapshot = crate::DcMetadataSpmBufferAccountSnapshot {
|
||||||
|
account: "buffer111".to_string(),
|
||||||
|
program: std::option::Option::Some("program111".to_string()),
|
||||||
|
authority: std::option::Option::Some("authority111".to_string()),
|
||||||
|
canonical: true,
|
||||||
|
seed: match crate::DcMetadataSpmSeed::from_wire_bytes(&[7_u8; 16]) {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => crate::DcMetadataSpmSeed::default(),
|
||||||
|
},
|
||||||
|
data: std::vec![1, 2, 3],
|
||||||
|
allocated_data_bytes: 3,
|
||||||
|
};
|
||||||
|
let output =
|
||||||
|
crate::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot(
|
||||||
|
42, &snapshot,
|
||||||
|
);
|
||||||
|
assert!(output.is_ok());
|
||||||
|
let payload = match output.as_ref() {
|
||||||
|
std::result::Result::Ok(value) => value.payload_json.clone(),
|
||||||
|
std::result::Result::Err(_) => serde_json::Value::Null,
|
||||||
|
};
|
||||||
|
assert_eq!(payload["projectionKind"], "buffer_state");
|
||||||
|
assert_eq!(payload["data"]["base64"], "AQID");
|
||||||
|
assert_eq!(payload["mutable"], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn metadata_snapshots_materialize_every_onchain_source_without_resolution() {
|
||||||
|
let direct = metadata_snapshot(crate::DcMetadataSpmData::Direct(std::vec![4, 5]));
|
||||||
|
let direct_output =
|
||||||
|
crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
|
||||||
|
9, &direct,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
direct_output
|
||||||
|
.as_ref()
|
||||||
|
.map(|value| return value.payload_json["data"]["kind"].clone()),
|
||||||
|
std::result::Result::Ok(serde_json::json!("direct"))
|
||||||
|
);
|
||||||
|
let url_value = "https://example.invalid/idl.json".to_string();
|
||||||
|
let mut url = metadata_snapshot(crate::DcMetadataSpmData::Url(url_value.clone()));
|
||||||
|
url.data_source = crate::DcMetadataSpmDataSource::Url;
|
||||||
|
url.data_length = match u32::try_from(url_value.len()) {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => 0,
|
||||||
|
};
|
||||||
|
url.allocated_data_bytes = url_value.len();
|
||||||
|
let url_output =
|
||||||
|
crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
|
||||||
|
10, &url,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
url_output
|
||||||
|
.as_ref()
|
||||||
|
.map(|value| return value.payload_json["data"]["fetched"].clone()),
|
||||||
|
std::result::Result::Ok(serde_json::json!(false))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inconsistent_snapshot_lengths_fail_closed() {
|
||||||
|
let mut snapshot = metadata_snapshot(crate::DcMetadataSpmData::Direct(std::vec![1, 2]));
|
||||||
|
snapshot.data_length = 1;
|
||||||
|
assert!(
|
||||||
|
crate::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot(
|
||||||
|
1, &snapshot
|
||||||
|
)
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn metadata_snapshot(
|
||||||
|
data: crate::DcMetadataSpmData,
|
||||||
|
) -> crate::DcMetadataSpmMetadataAccountSnapshot {
|
||||||
|
return crate::DcMetadataSpmMetadataAccountSnapshot {
|
||||||
|
account: "metadata111".to_string(),
|
||||||
|
bump: 2,
|
||||||
|
program: "program111".to_string(),
|
||||||
|
authority: std::option::Option::Some("authority111".to_string()),
|
||||||
|
mutable: true,
|
||||||
|
canonical: true,
|
||||||
|
seed: crate::DcMetadataSpmSeed::default(),
|
||||||
|
encoding: crate::DcMetadataSpmEncoding::Utf8,
|
||||||
|
compression: crate::DcMetadataSpmCompression::None,
|
||||||
|
format: crate::DcMetadataSpmFormat::Json,
|
||||||
|
data_source: crate::DcMetadataSpmDataSource::Direct,
|
||||||
|
data_length: match u32::try_from(data.wire_byte_len()) {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => 0,
|
||||||
|
},
|
||||||
|
allocated_data_bytes: data.wire_byte_len(),
|
||||||
|
trailing_capacity_bytes: 0,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-lib/tests/external_materializer_api.rs
|
// file: kb-lib/tests/external_materializer_api.rs
|
||||||
// version: 5
|
// version: 6
|
||||||
|
|
||||||
//! Downstream-style compilation contract for externally implemented materializers.
|
//! Downstream-style compilation contract for externally implemented materializers.
|
||||||
|
|
||||||
@@ -50,6 +50,7 @@ fn external_materializer_uses_only_the_public_kb_lib_contract() {
|
|||||||
assert_event_materializer::<kb_lib::MtGovernanceMaterializer>();
|
assert_event_materializer::<kb_lib::MtGovernanceMaterializer>();
|
||||||
assert_event_materializer::<kb_lib::MtLendingMaterializer>();
|
assert_event_materializer::<kb_lib::MtLendingMaterializer>();
|
||||||
assert_event_materializer::<kb_lib::MtLifecycleMaterializer>();
|
assert_event_materializer::<kb_lib::MtLifecycleMaterializer>();
|
||||||
|
assert_event_materializer::<kb_lib::MtMetadataMaterializer>();
|
||||||
assert_event_materializer::<kb_lib::MtLiquidityMaterializer>();
|
assert_event_materializer::<kb_lib::MtLiquidityMaterializer>();
|
||||||
assert_event_materializer::<kb_lib::MtNftMaterializer>();
|
assert_event_materializer::<kb_lib::MtNftMaterializer>();
|
||||||
assert_event_materializer::<kb_lib::MtOracleMaterializer>();
|
assert_event_materializer::<kb_lib::MtOracleMaterializer>();
|
||||||
@@ -83,6 +84,9 @@ fn external_materializer_uses_only_the_public_kb_lib_contract() {
|
|||||||
let _ = kb_lib::materializer_admin_materialize_elgamal_registry_state_snapshot;
|
let _ = kb_lib::materializer_admin_materialize_elgamal_registry_state_snapshot;
|
||||||
let _ = kb_lib::materializer_metadata_materialize_metaplex_account_snapshot;
|
let _ = kb_lib::materializer_metadata_materialize_metaplex_account_snapshot;
|
||||||
let _ = kb_lib::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
let _ = kb_lib::materializer_metadata_materialize_metaplex_metadata_snapshot;
|
||||||
|
let _ = kb_lib::materializer_metadata_materialize_solana_program_metadata_account_snapshot;
|
||||||
|
let _ = kb_lib::materializer_metadata_materialize_solana_program_metadata_buffer_snapshot;
|
||||||
|
let _ = kb_lib::materializer_metadata_materialize_solana_program_metadata_metadata_snapshot;
|
||||||
let _ = kb_lib::materializer_admin_materialize_token_2022_state_snapshots;
|
let _ = kb_lib::materializer_admin_materialize_token_2022_state_snapshots;
|
||||||
let _ = kb_lib::materializer_fees_materialize_token_2022_state_snapshots;
|
let _ = kb_lib::materializer_fees_materialize_token_2022_state_snapshots;
|
||||||
let _ = kb_lib::materializer_metadata_materialize_token_2022_snapshot;
|
let _ = kb_lib::materializer_metadata_materialize_token_2022_snapshot;
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"file": "test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json",
|
||||||
|
"version": 1,
|
||||||
|
"programId": "ProgM6JCCvbYkfKqJYHePx4xxSUSqJp7rh8Lyv7nk7S",
|
||||||
|
"surface": "metadata.solana_program_metadata",
|
||||||
|
"accountSnapshots": [
|
||||||
|
{
|
||||||
|
"accountKind": "buffer",
|
||||||
|
"projectionKind": "buffer_state",
|
||||||
|
"authoritative": true,
|
||||||
|
"completeDecodedContentRetained": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"accountKind": "metadata",
|
||||||
|
"projectionKind": "metadata_state",
|
||||||
|
"authoritative": true,
|
||||||
|
"completeDecodedContentRetained": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"instructionFacts": [
|
||||||
|
{"tag": 0, "entryCode": "write", "stateTransition": "buffer_content_written", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"},
|
||||||
|
{"tag": 1, "entryCode": "initialize", "stateTransition": "metadata_account_initialized", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"},
|
||||||
|
{"tag": 2, "entryCode": "set_authority", "stateTransition": "account_authority_updated", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"},
|
||||||
|
{"tag": 3, "entryCode": "set_data", "stateTransition": "metadata_data_configuration_updated", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"},
|
||||||
|
{"tag": 4, "entryCode": "set_immutable", "stateTransition": "metadata_account_marked_immutable", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"},
|
||||||
|
{"tag": 5, "entryCode": "trim", "stateTransition": "account_capacity_trimmed", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate", "preStableAlias": "withdraw_excess_lamports"},
|
||||||
|
{"tag": 6, "entryCode": "close", "stateTransition": "account_closed", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"},
|
||||||
|
{"tag": 7, "entryCode": "allocate", "stateTransition": "buffer_account_allocated", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"},
|
||||||
|
{"tag": 8, "entryCode": "extend", "stateTransition": "account_capacity_extended", "authoritative": false, "deprecated": false, "replaced": false, "executionPolicy": "current_candidate"}
|
||||||
|
],
|
||||||
|
"transactionPolicy": "successful_committed_only",
|
||||||
|
"offchainContentFetched": false,
|
||||||
|
"externalAccountsReadByMaterializer": false
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user