v0.4.7-pre.014

This commit is contained in:
2026-08-04 19:43:03 +02:00
parent 22228149a3
commit b358b12f3b
6 changed files with 249 additions and 39 deletions

View File

@@ -1,5 +1,5 @@
# file: Cargo.toml # file: Cargo.toml
# version: 20 # version: 21
[workspace] [workspace]
resolver = "3" resolver = "3"
@@ -18,7 +18,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.4.7-pre.13" version = "0.4.7-pre.14"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3" repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"

View File

@@ -0,0 +1,42 @@
<!-- file: docs/validation/V0_4_7_PRE_014_METAPLEX_COHERENT_DEVNET_JOURNEYS.md -->
<!-- version: 1 -->
# Validation `0.4.7-pre.014` — parcours Metaplex Devnet cohérents
## Objet
Cette prérelease étend le runner Metadata depuis le seul parcours NFT vers cinq parcours Devnet préparables : NFT, SFT, fungible, collection parent et pNFT sans rule set.
Chaque parcours expose d'abord un cycle borné et comparable :
1. création native d'un mint SPL classique par le workspace ;
2. préparation contextuelle de `Create` ;
3. simulation exacte ;
4. soumission et confirmation explicites ;
5. relecture et matérialisation optionnelle des comptes applicables ;
6. préparation de `UpdateAsUpdateAuthorityV2` depuis l'état créé ;
7. simulation, soumission, confirmation et postconditions.
## Familles et contrats Create
| Parcours | `token_standard` | Edition relue | Particularité |
|------------|---------------------------|--------------:|-----------------------------------------|
| NFT | `NonFungible` | oui | `print_supply = Zero` |
| SFT | `FungibleAsset` | non | `decimals = 0`, pas de master edition |
| Fungible | `Fungible` | non | `decimals = 0`, pas de master edition |
| Collection | `NonFungible` | oui | `collection_details = V1(size=0)` |
| pNFT | `ProgrammableNonFungible` | oui | aucun rule set pour ce premier parcours |
## Limite volontaire
Cette prérelease ne déclare pas encore validées les opérations spécialisées `mint`, `verify`, `unverify`, `delegate`, `lock`, `unlock`, `transfer`, `revoke`, `burn`, `resize` et apparentées. Elles doivent être ajoutées comme étapes dépendantes d'un contexte confirmé, pas comme modèles JSON isolés.
## Validation attendue
- `cargo fmt --all` ;
- `cargo check --workspace` ;
- `cargo clippy --all-targets` ;
- `python3 scripts/audit_rust_workspace_rules.py` ;
- `cargo test -p kb-pipeline-demo-scenarios` ;
- `cargo test -p kb-app-demo-desktop` ;
- pour chaque parcours : préparer `Create`, simuler, soumettre, confirmer, puis préparer et valider `Update`.

View File

@@ -1,8 +1,13 @@
<!-- file: kb-app-demo-desktop/CHANGELOG.md --> <!-- file: kb-app-demo-desktop/CHANGELOG.md -->
<!-- version: 21 --> <!-- version: 22 -->
# CHANGELOG — kb-app-demo-desktop # CHANGELOG — kb-app-demo-desktop
## 0.4.7-pre.014
- Afficher les cinq parcours Metaplex Devnet dans le runner Metadata.
- Adapter automatiquement le JSON `Create`, les lectures de postcondition et les cibles de matérialisation à la famille sélectionnée.
## 0.4.7-pre.013-fix-015 ## 0.4.7-pre.013-fix-015
- aligne les tests desktop sur le nombre réel de scénarios synthétiques et Devnet exposés par les registres réutilisables ; - aligne les tests desktop sur le nombre réel de scénarios synthétiques et Devnet exposés par les registres réutilisables ;

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_execution_metadata.rs // file: kb-app-demo-desktop/src/demo_execution_metadata.rs
// version: 12 // version: 13
//! Thin desktop adapters for metadata execution demo scenarios. //! Thin desktop adapters for metadata execution demo scenarios.
@@ -323,6 +323,84 @@ pub(crate) async fn demo_execution_metadata_prepare_create_fixture(
}); });
} }
fn adapt_create_operation_json(
operation_json: &str,
asset_family: kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily,
) -> std::result::Result<std::string::String, std::string::String> {
let mut value: serde_json::Value = match serde_json::from_str(operation_json) {
std::result::Result::Ok(parsed) => parsed,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"invalid generated Metaplex Create JSON: {error}"
));
},
};
if matches!(
asset_family,
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Sft
| kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Fungible
) {
value["master_edition"] = serde_json::Value::Null;
}
let create = match value
.get_mut("create_args")
.and_then(|args| return args.get_mut("V1"))
.and_then(serde_json::Value::as_object_mut)
{
std::option::Option::Some(found) => found,
std::option::Option::None => {
return std::result::Result::Err(
"generated Metaplex Create JSON has no create_args.V1 object".to_string(),
);
},
};
match asset_family {
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Nft => {
create.insert("name".to_string(), serde_json::json!("Khadhroony Devnet NFT"));
create.insert("symbol".to_string(), serde_json::json!("KHNFT"));
create.insert("token_standard".to_string(), serde_json::json!("NonFungible"));
create.insert("collection_details".to_string(), serde_json::Value::Null);
create.insert("decimals".to_string(), serde_json::Value::Null);
create.insert("print_supply".to_string(), serde_json::json!("Zero"));
},
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Sft => {
create.insert("name".to_string(), serde_json::json!("Khadhroony Devnet SFT"));
create.insert("symbol".to_string(), serde_json::json!("KHSFT"));
create.insert("token_standard".to_string(), serde_json::json!("FungibleAsset"));
create.insert("collection_details".to_string(), serde_json::Value::Null);
create.insert("decimals".to_string(), serde_json::json!(0));
create.insert("print_supply".to_string(), serde_json::Value::Null);
},
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Fungible => {
create.insert("name".to_string(), serde_json::json!("Khadhroony Devnet Fungible"));
create.insert("symbol".to_string(), serde_json::json!("KHFUN"));
create.insert("token_standard".to_string(), serde_json::json!("Fungible"));
create.insert("collection_details".to_string(), serde_json::Value::Null);
create.insert("decimals".to_string(), serde_json::json!(0));
create.insert("print_supply".to_string(), serde_json::Value::Null);
},
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Collection => {
create.insert("name".to_string(), serde_json::json!("Khadhroony Devnet Collection"));
create.insert("symbol".to_string(), serde_json::json!("KHCOL"));
create.insert("token_standard".to_string(), serde_json::json!("NonFungible"));
create.insert("collection_details".to_string(), serde_json::json!({"V1": {"size": 0}}));
create.insert("decimals".to_string(), serde_json::Value::Null);
create.insert("print_supply".to_string(), serde_json::json!("Zero"));
},
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::ProgrammableNft => {
create.insert("name".to_string(), serde_json::json!("Khadhroony Devnet pNFT"));
create.insert("symbol".to_string(), serde_json::json!("KHPNFT"));
create
.insert("token_standard".to_string(), serde_json::json!("ProgrammableNonFungible"));
create.insert("collection_details".to_string(), serde_json::Value::Null);
create.insert("rule_set".to_string(), serde_json::Value::Null);
create.insert("decimals".to_string(), serde_json::Value::Null);
create.insert("print_supply".to_string(), serde_json::json!("Zero"));
},
}
return std::result::Result::Ok(crate::pretty_json(&value));
}
/// Prepares one coherent scenario step and all of its stateful reads. /// Prepares one coherent scenario step and all of its stateful reads.
pub(crate) async fn demo_execution_metadata_prepare_step( pub(crate) async fn demo_execution_metadata_prepare_step(
state: tauri::State<'_, crate::AppState>, state: tauri::State<'_, crate::AppState>,
@@ -374,11 +452,18 @@ pub(crate) async fn demo_execution_metadata_prepare_step(
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error), std::result::Result::Err(error) => return std::result::Result::Err(error),
}; };
let operation_json = match adapt_create_operation_json(
fixture.operation_json.as_str(),
scenario.asset_family,
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
( (
fixture.mint, fixture.mint,
fixture.metadata, fixture.metadata,
fixture.master_edition, fixture.master_edition,
fixture.operation_json, operation_json,
if fixture.mint_created { if fixture.mint_created {
"mint_prepared".to_string() "mint_prepared".to_string()
} else { } else {
@@ -480,7 +565,13 @@ pub(crate) async fn demo_execution_metadata_prepare_step(
} else { } else {
serde_json::json!([metadata_read.clone()]) serde_json::json!([metadata_read.clone()])
}; };
let postcondition_reads = if operation == "create" { let create_reads_edition = matches!(
scenario.asset_family,
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Nft
| kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Collection
| kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::ProgrammableNft
);
let postcondition_reads = if operation == "create" && create_reads_edition {
serde_json::json!([metadata_read, edition_read]) serde_json::json!([metadata_read, edition_read])
} else { } else {
serde_json::json!([metadata_read]) serde_json::json!([metadata_read])
@@ -675,7 +766,7 @@ mod tests {
.iter() .iter()
.filter(|payload| return payload.mode == "network_simulation") .filter(|payload| return payload.mode == "network_simulation")
.count(), .count(),
1 5
); );
} }

View File

@@ -1,8 +1,13 @@
<!-- file: kb-pipeline-demo-scenarios/CHANGELOG.md --> <!-- file: kb-pipeline-demo-scenarios/CHANGELOG.md -->
<!-- version: 24 --> <!-- version: 25 -->
# CHANGELOG — kb-pipeline-demo-scenarios # CHANGELOG — kb-pipeline-demo-scenarios
## 0.4.7-pre.014
- Expose cinq parcours Metaplex Devnet préparables : NFT, SFT, fungible, collection parent et pNFT sans rule set.
- Conserver un cycle borné `Create -> UpdateAsUpdateAuthorityV2` pour chaque famille avant d'étendre les opérations spécialisées.
## 0.4.7-pre.013-fix-014 ## 0.4.7-pre.013-fix-014
- crée un nouveau mint natif pour chaque nouvelle préparation `Create`, afin de ne jamais réutiliser un PDA metadata déjà créé ; - crée un nouveau mint natif pour chaque nouvelle préparation `Create`, afin de ne jamais réutiliser un PDA metadata déjà créé ;

View File

@@ -1,5 +1,5 @@
// file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_scenarios.rs // file: kb-pipeline-demo-scenarios/src/solana_metaplex_token_metadata_scenarios.rs
// version: 4 // version: 5
//! Coherent Metaplex Token Metadata scenario journeys for tests and Devnet campaigns. //! Coherent Metaplex Token Metadata scenario journeys for tests and Devnet campaigns.
@@ -224,23 +224,85 @@ pub fn metaplex_token_metadata_synthetic_scenarios()
/// Returns the ordered Metaplex journeys intended for opt-in Devnet simulation. /// Returns the ordered Metaplex journeys intended for opt-in Devnet simulation.
pub fn metaplex_token_metadata_devnet_scenarios() pub fn metaplex_token_metadata_devnet_scenarios()
-> std::vec::Vec<crate::MetaplexTokenMetadataScenario> { -> std::vec::Vec<crate::MetaplexTokenMetadataScenario> {
let mut scenario = scenario( let definitions = [
(
"metaplex_nft_create_update_devnet", "metaplex_nft_create_update_devnet",
"NFT classique : création confirmée puis mise à jour", "NFT classique : création confirmée puis mise à jour",
crate::MetaplexTokenMetadataAssetFamily::Nft, crate::MetaplexTokenMetadataAssetFamily::Nft,
crate::MetaplexTokenMetadataFixtureKind::ClassicNftMint, crate::MetaplexTokenMetadataFixtureKind::ClassicNftMint,
false,
false,
&["metadata_state", "edition_state"][..],
),
(
"metaplex_sft_create_update_devnet",
"SFT : création confirmée puis mise à jour",
crate::MetaplexTokenMetadataAssetFamily::Sft,
crate::MetaplexTokenMetadataFixtureKind::SemiFungibleMint,
false,
false,
&["metadata_state"][..],
),
(
"metaplex_fungible_create_update_devnet",
"Fungible : création confirmée puis mise à jour",
crate::MetaplexTokenMetadataAssetFamily::Fungible,
crate::MetaplexTokenMetadataFixtureKind::FungibleMint,
false,
false,
&["metadata_state"][..],
),
(
"metaplex_collection_create_update_devnet",
"Collection parent : création confirmée puis mise à jour",
crate::MetaplexTokenMetadataAssetFamily::Collection,
crate::MetaplexTokenMetadataFixtureKind::CollectionParentAndMember,
true,
false,
&["metadata_state", "edition_state"][..],
),
(
"metaplex_pnft_create_update_devnet",
"pNFT sans rule set : création confirmée puis mise à jour",
crate::MetaplexTokenMetadataAssetFamily::ProgrammableNft,
crate::MetaplexTokenMetadataFixtureKind::ProgrammableNftTokenRecord,
false,
false,
&["metadata_state", "edition_state"][..],
),
];
return definitions
.into_iter()
.map(
|(
id,
label,
asset_family,
fixture_kind,
requires_collection,
requires_programmable_rules,
materialization_targets,
)| {
let mut value = scenario(
id,
label,
asset_family,
fixture_kind,
crate::MetaplexTokenMetadataFixtureState::MintPrepared, crate::MetaplexTokenMetadataFixtureState::MintPrepared,
crate::MetaplexTokenMetadataFixtureState::MetadataCreated, crate::MetaplexTokenMetadataFixtureState::MetadataCreated,
vec![ vec![
kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(), kb_lib::EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION.to_string(),
kb_lib::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION.to_string(), kb_lib::EX_METAPLEX_TOKEN_METADATA_UPDATE_OPERATION.to_string(),
], ],
false, requires_collection,
false, requires_programmable_rules,
&["metadata_state", "edition_state"], materialization_targets,
); );
scenario.mode = crate::MetaplexTokenMetadataScenarioMode::NetworkSimulation; value.mode = crate::MetaplexTokenMetadataScenarioMode::NetworkSimulation;
return vec![scenario]; return value;
},
)
.collect();
} }
#[cfg(test)] #[cfg(test)]
@@ -279,9 +341,13 @@ mod tests {
#[test] #[test]
fn devnet_inventory_exposes_only_fully_preparable_journeys() { fn devnet_inventory_exposes_only_fully_preparable_journeys() {
let devnet = crate::metaplex_token_metadata_devnet_scenarios(); let devnet = crate::metaplex_token_metadata_devnet_scenarios();
assert_eq!(devnet.len(), 1); assert_eq!(devnet.len(), 5);
let scenario = &devnet[0]; let families = devnet
assert_eq!(scenario.asset_family, crate::MetaplexTokenMetadataAssetFamily::Nft); .iter()
.map(|scenario| return scenario.asset_family)
.collect::<std::collections::BTreeSet<_>>();
assert_eq!(families.len(), 5);
for scenario in devnet {
assert_eq!(scenario.operation_codes.len(), 2); assert_eq!(scenario.operation_codes.len(), 2);
assert_eq!( assert_eq!(
scenario.operation_codes[0], scenario.operation_codes[0],
@@ -294,3 +360,4 @@ mod tests {
assert_eq!(scenario.mode, crate::MetaplexTokenMetadataScenarioMode::NetworkSimulation); assert_eq!(scenario.mode, crate::MetaplexTokenMetadataScenarioMode::NetworkSimulation);
} }
} }
}