0.4.7-pre.002

This commit is contained in:
2026-08-02 01:26:37 +02:00
parent aa3f58db30
commit 7a50ddd531
8 changed files with 5454 additions and 169 deletions

View File

@@ -1,8 +1,16 @@
<!-- file: kb-lib/CHANGELOG.md -->
<!-- version: 6 -->
<!-- version: 7 -->
# CHANGELOG — kb-lib
## 0.4.7-pre.002 — contrat Metaplex fermé
- classification exhaustive des 58 discriminants Metaplex Token Metadata ;
- comptes positionnels et signers importés de lIDL archivée `V1_14_0` ;
- propriété unique des faits metadata, admin, lifecycle et risk/compliance ;
- exposition directe des créateurs, collection, uses, token standard et programmable config dans le snapshot metadata autoritatif ;
- tests contractuels dexhaustivité, dunicité et de séparation avec Token-2022.
## 0.4.6
- alignement de la crate sur la version fonctionnelle bot3 `0.4.6` ;

View File

@@ -1,5 +1,5 @@
<!-- file: kb-lib/README.md -->
<!-- version: 14 -->
<!-- version: 15 -->
# kb-lib
@@ -56,7 +56,7 @@ Les matrices actives sont maintenues sous [`../test-fixtures/contract-matrices/`
## Statut et limites
La migration des surfaces historiques jusquà un périmètre proche de bot2 `0.4.6` est largement réalisée. Le décodeur et les matérialisations Metaplex Token Metadata sont présents, mais lexécuteur et la clôture fonctionnelle de cette surface restent à réaliser. Le registre ElGamal nest pas déclaré validé sur Devnet ou Mainnet.
La migration des surfaces historiques jusquà un périmètre proche de bot2 `0.4.6` est largement réalisée. Le décodeur Metaplex Token Metadata couvre les 58 discriminants et les matérialisations possèdent désormais un contrat fermé de propriété des faits. Lexécuteur et la clôture fonctionnelle de cette surface restent à réaliser entre `0.4.7-pre.003` et `pre.006`. Le registre ElGamal nest pas déclaré validé sur Devnet ou Mainnet.
## Documents

View File

@@ -1,12 +1,12 @@
<!-- file: kb-lib/TODO.md -->
<!-- version: 3 -->
<!-- version: 4 -->
# TODO — kb-lib
## Entre `0.4.6` et `0.4.7` — Metaplex Token Metadata
- [ ] Matérialisation - vérifier la couverture exacte migrée depuis bot2.
- [ ] Matérialisation - compléter uniquement les projections réellement manquantes.
- [x] Matérialisation - vérifier la couverture exacte migrée depuis bot2.
- [x] Matérialisation - compléter uniquement les projections réellement manquantes et fermer leur propriété unique.
- [ ] Exécution - implémenter lexécuteur à partir des contrats officiels vérifiés.
- [ ] Tests - ajouter les tests unitaires et contractuels de lexécuteur.
- [ ] Intégration - préparer les contrats nécessaires au pipeline et aux démonstrations.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/decoder.rs
// version: 30
// version: 31
//! Exact contextual dispatch for Metaplex Token Metadata.
@@ -3051,6 +3051,80 @@ mod tests {
}
}
#[test]
fn pre_002_contract_closes_all_instruction_statuses_and_idl_accounts() {
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
};
let entries = match matrix
.get("instruction_execution_contract")
.and_then(serde_json::Value::as_array)
{
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("instruction_execution_contract missing"),
};
assert_eq!(entries.len(), 58);
let mut discriminators = std::collections::BTreeSet::new();
for entry in entries {
let discriminator = match entry
.get("discriminator")
.and_then(serde_json::Value::as_u64)
{
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("instruction discriminator missing"),
};
assert!(discriminators.insert(discriminator));
assert!(entry.get("accounts").is_some_and(serde_json::Value::is_array));
assert!(entry.get("signers").is_some_and(serde_json::Value::is_array));
assert!(entry
.get("coverage_status")
.and_then(serde_json::Value::as_str)
.is_some_and(|value| return value != "pending" && !value.is_empty()));
assert!(entry
.get("execute")
.and_then(serde_json::Value::as_str)
.is_some_and(|value| return value != "pending" && !value.is_empty()));
}
assert_eq!(discriminators, (0u64..58u64).collect());
}
#[test]
fn pre_002_fact_ownership_is_unique_and_keeps_metadata_domains_separate() {
let matrix: serde_json::Value = match serde_json::from_str(AUDIT_MATRIX_JSON) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("matrix parse failed: {error}"),
};
let facts = match matrix
.get("fact_ownership_contract")
.and_then(serde_json::Value::as_array)
{
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("fact_ownership_contract missing"),
};
let mut names = std::collections::BTreeSet::new();
for entry in facts {
let fact = match entry.get("fact").and_then(serde_json::Value::as_str) {
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("fact name missing"),
};
assert!(names.insert(fact));
assert!(entry
.get("owner")
.and_then(serde_json::Value::as_str)
.is_some_and(|value| return !value.is_empty()));
}
let forbidden = match matrix
.pointer("/fact_ownership_proof/cross_domain_merge_forbidden")
.and_then(serde_json::Value::as_array)
{
std::option::Option::Some(value) => value,
std::option::Option::None => panic!("cross-domain proof missing"),
};
assert!(forbidden.contains(&serde_json::json!("metaplex_token_metadata")));
assert!(forbidden.contains(&serde_json::json!("spl_token_2022_embedded_metadata")));
}
#[test]
fn omitted_historical_discriminators_reject_invalid_wires() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/materializer/metadata/core.rs
// version: 5
// version: 6
//! Stable Token-2022 and Metaplex metadata snapshot materializer.
@@ -134,6 +134,11 @@ pub fn materializer_metadata_materialize_metaplex_metadata_snapshot(
"symbol": metadata.symbol,
"uri": metadata.uri,
"sellerFeeBasisPoints": metadata.seller_fee_basis_points,
"creators": metadata.payload_json.get("creators").cloned().unwrap_or(serde_json::Value::Null),
"collection": metadata.payload_json.get("collection").cloned().unwrap_or(serde_json::Value::Null),
"uses": metadata.payload_json.get("uses").cloned().unwrap_or(serde_json::Value::Null),
"tokenStandard": metadata.payload_json.get("tokenStandard").cloned().unwrap_or(serde_json::Value::Null),
"programmableConfig": metadata.payload_json.get("programmableConfig").cloned().unwrap_or(serde_json::Value::Null),
"primarySaleHappened": metadata.primary_sale_happened,
"isMutable": metadata.is_mutable,
"layout": snapshot.identity.layout,
@@ -218,6 +223,18 @@ mod tests_metaplex_metadata_state {
first.as_ref().map(|value| return value.payload_json["metadataSource"].clone()),
std::result::Result::Ok(serde_json::json!("metaplex_owned_account"))
);
assert_eq!(
first.as_ref().map(|value| return value.payload_json["creators"].clone()),
std::result::Result::Ok(
serde_json::json!([{"address":"creator111","verified":true,"share":100}])
)
);
assert_eq!(
first
.as_ref()
.map(|value| return value.payload_json["programmableConfig"].clone()),
std::result::Result::Ok(serde_json::json!({"ruleSet":"rules111"}))
);
assert_eq!(
first
.as_ref()