|
|
|
|
@@ -1,5 +1,5 @@
|
|
|
|
|
// file: kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs
|
|
|
|
|
// version: 1
|
|
|
|
|
// version: 4
|
|
|
|
|
|
|
|
|
|
//! Desktop adapters for Metaplex Token Metadata execution demo scenarios.
|
|
|
|
|
|
|
|
|
|
@@ -190,6 +190,13 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_execute(
|
|
|
|
|
std::result::Result::Ok(value) => value,
|
|
|
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
|
|
|
|
};
|
|
|
|
|
let store = match crate::connect_postgres_store(&profile).await {
|
|
|
|
|
std::result::Result::Ok(value) => value,
|
|
|
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
|
|
|
|
};
|
|
|
|
|
if let std::result::Result::Err(error) = store.initialize_store_schema().await {
|
|
|
|
|
return std::result::Result::Err(error.to_string());
|
|
|
|
|
}
|
|
|
|
|
let mut execution_request = match kb_pipeline_demo_scenarios::DevnetMetaplexTokenMetadataExecutionRequest::from_operation_json(
|
|
|
|
|
request.intent_id,
|
|
|
|
|
request.operation_json.as_str(),
|
|
|
|
|
@@ -210,9 +217,20 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_execute(
|
|
|
|
|
execution_request.submit = request.submit;
|
|
|
|
|
execution_request.operator_confirmed = request.operator_confirmed;
|
|
|
|
|
execution_request.materialize_after_confirmation = request.materialize_after_confirmation;
|
|
|
|
|
execution_request.post_validation_max_retries = 20;
|
|
|
|
|
execution_request.force_post_validation_replay = false;
|
|
|
|
|
if let std::result::Result::Err(error) = execution_request.validate() {
|
|
|
|
|
return std::result::Result::Err(error.to_string());
|
|
|
|
|
}
|
|
|
|
|
let decoders: std::vec::Vec<std::sync::Arc<dyn kb_lib::DcApiInstructionDecoder>> =
|
|
|
|
|
std::vec![std::sync::Arc::new(kb_lib::DcMetadataMetaplexTokenMetadataDecoder)];
|
|
|
|
|
let materializers: std::vec::Vec<std::sync::Arc<dyn kb_lib::MtApiEventMaterializer>> = std::vec![
|
|
|
|
|
std::sync::Arc::new(kb_lib::MtAdminMaterializer),
|
|
|
|
|
std::sync::Arc::new(kb_lib::MtComplianceAuditMaterializer),
|
|
|
|
|
std::sync::Arc::new(kb_lib::MtLifecycleMaterializer),
|
|
|
|
|
std::sync::Arc::new(kb_lib::MtMetadataMetaplexTokenMetadataMaterializer),
|
|
|
|
|
std::sync::Arc::new(kb_lib::MtRiskMaterializer),
|
|
|
|
|
];
|
|
|
|
|
let observer = crate::DemoExecutionMetadataObserver {
|
|
|
|
|
app_handle,
|
|
|
|
|
cancel_requested: state.demo_execution_solana_core_cancel_requested(),
|
|
|
|
|
@@ -220,9 +238,12 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_execute(
|
|
|
|
|
let workspace_root = crate::workspace_root_dir();
|
|
|
|
|
let summary = match kb_pipeline_demo_scenarios::execute_devnet_metaplex_token_metadata(
|
|
|
|
|
&http_pool,
|
|
|
|
|
&store,
|
|
|
|
|
&profile,
|
|
|
|
|
workspace_root.as_path(),
|
|
|
|
|
&execution_request,
|
|
|
|
|
decoders.as_slice(),
|
|
|
|
|
materializers.as_slice(),
|
|
|
|
|
&observer,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
@@ -241,7 +262,13 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_execute(
|
|
|
|
|
"confirmation": &summary.confirmation,
|
|
|
|
|
"after": &summary.after,
|
|
|
|
|
"materializationRequested": summary.materialization_requested,
|
|
|
|
|
"materializedSnapshots": &summary.materialized_snapshots
|
|
|
|
|
"materializedSnapshots": &summary.materialized_snapshots,
|
|
|
|
|
"backfill": summary.backfill.as_ref().map(backfill_summary_json),
|
|
|
|
|
"coreExtraction": summary.core_extraction.as_ref().map(core_extraction_summary_json),
|
|
|
|
|
"decodeReplay": summary.decode_replay.as_ref().map(decode_replay_summary_json),
|
|
|
|
|
"idempotenceReplay": summary.idempotence_replay.as_ref().map(decode_replay_summary_json),
|
|
|
|
|
"instructionMaterializations": &summary.materializations,
|
|
|
|
|
"postExecution": &summary.post_execution
|
|
|
|
|
}));
|
|
|
|
|
return std::result::Result::Ok(
|
|
|
|
|
crate::DemoExecutionMetadataMetaplexTokenMetadataSummaryPayload {
|
|
|
|
|
@@ -263,6 +290,88 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_execute(
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn backfill_summary_json(summary: &kb_pipeline::BackfillSummary) -> serde_json::Value {
|
|
|
|
|
return serde_json::json!({
|
|
|
|
|
"captureSessionId": summary.capture_session_id,
|
|
|
|
|
"filterCode": summary.filter_code,
|
|
|
|
|
"role": summary.role,
|
|
|
|
|
"provider": summary.provider,
|
|
|
|
|
"endpointCode": summary.endpoint_code,
|
|
|
|
|
"pagesFetched": summary.pages_fetched,
|
|
|
|
|
"candidatesSelected": summary.candidates_selected,
|
|
|
|
|
"candidatesStarted": summary.candidates_started,
|
|
|
|
|
"candidatesCompleted": summary.candidates_completed,
|
|
|
|
|
"candidatesCancelled": summary.candidates_cancelled,
|
|
|
|
|
"candidatesNotStarted": summary.candidates_not_started,
|
|
|
|
|
"transactionsReceived": summary.transactions_received,
|
|
|
|
|
"canonicalInserted": summary.canonical_inserted,
|
|
|
|
|
"canonicalSkipped": summary.canonical_skipped,
|
|
|
|
|
"existingSkipped": summary.existing_skipped,
|
|
|
|
|
"missing": summary.missing,
|
|
|
|
|
"failed": summary.failed,
|
|
|
|
|
"observationsInserted": summary.observations_inserted,
|
|
|
|
|
"attempts": summary.attempts,
|
|
|
|
|
"cancelled": summary.cancelled,
|
|
|
|
|
"resumeBeforeSignature": summary.resume_before_signature,
|
|
|
|
|
"startedAt": summary.started_at,
|
|
|
|
|
"finishedAt": summary.finished_at
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn core_extraction_summary_json(summary: &kb_pipeline::CoreExtractionSummary) -> serde_json::Value {
|
|
|
|
|
return serde_json::json!({
|
|
|
|
|
"processorVersion": summary.processor_version,
|
|
|
|
|
"selected": summary.selected,
|
|
|
|
|
"started": summary.started,
|
|
|
|
|
"completed": summary.completed,
|
|
|
|
|
"skipped": summary.skipped,
|
|
|
|
|
"extracted": summary.extracted,
|
|
|
|
|
"failed": summary.failed,
|
|
|
|
|
"cancelledCandidates": summary.cancelled_candidates,
|
|
|
|
|
"notStarted": summary.not_started,
|
|
|
|
|
"cancelled": summary.cancelled,
|
|
|
|
|
"startedAt": summary.started_at,
|
|
|
|
|
"finishedAt": summary.finished_at
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn decode_replay_summary_json(summary: &kb_pipeline::DecodeReplaySummary) -> serde_json::Value {
|
|
|
|
|
let processors = summary
|
|
|
|
|
.processors
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|processor| {
|
|
|
|
|
return serde_json::json!({
|
|
|
|
|
"name": processor.processor_name,
|
|
|
|
|
"version": processor.processor_version,
|
|
|
|
|
"dispatched": processor.dispatched,
|
|
|
|
|
"skipped": processor.skipped,
|
|
|
|
|
"decoded": processor.decoded,
|
|
|
|
|
"ignored": processor.ignored,
|
|
|
|
|
"unsupported": processor.unsupported,
|
|
|
|
|
"failed": processor.failed,
|
|
|
|
|
"processingErrors": processor.processing_errors,
|
|
|
|
|
"materializedOutputs": processor.materialized_outputs,
|
|
|
|
|
"materializationRefused": processor.materialization_refused
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.collect::<std::vec::Vec<serde_json::Value>>();
|
|
|
|
|
return serde_json::json!({
|
|
|
|
|
"campaignId": summary.campaign_id,
|
|
|
|
|
"pipelineVersion": summary.pipeline_version,
|
|
|
|
|
"selected": summary.selected,
|
|
|
|
|
"started": summary.started,
|
|
|
|
|
"completed": summary.completed,
|
|
|
|
|
"unmatched": summary.unmatched,
|
|
|
|
|
"notStarted": summary.not_started,
|
|
|
|
|
"failedInputs": summary.failed_inputs,
|
|
|
|
|
"processingErrorInputs": summary.processing_error_inputs,
|
|
|
|
|
"cancelled": summary.cancelled,
|
|
|
|
|
"processors": processors,
|
|
|
|
|
"startedAt": summary.started_at,
|
|
|
|
|
"finishedAt": summary.finished_at
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_stateful_reads(
|
|
|
|
|
value: &str,
|
|
|
|
|
) -> std::result::Result<
|
|
|
|
|
@@ -280,13 +389,29 @@ fn parse_stateful_reads(
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Creates or reuses the Devnet mint fixture used by the named Create scenario.
|
|
|
|
|
/// Creates or reuses the Devnet NFT mint fixture used by the standalone Create action.
|
|
|
|
|
pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_prepare_create_fixture(
|
|
|
|
|
state: tauri::State<'_, crate::AppState>,
|
|
|
|
|
profile_name: std::string::String,
|
|
|
|
|
) -> std::result::Result<
|
|
|
|
|
crate::DemoExecutionMetadataMetaplexTokenMetadataCreateFixturePayload,
|
|
|
|
|
std::string::String,
|
|
|
|
|
> {
|
|
|
|
|
return prepare_create_fixture_for_family(
|
|
|
|
|
state,
|
|
|
|
|
profile_name,
|
|
|
|
|
kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily::Nft,
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn prepare_create_fixture_for_family(
|
|
|
|
|
state: tauri::State<'_, crate::AppState>,
|
|
|
|
|
profile_name: std::string::String,
|
|
|
|
|
asset_family: kb_pipeline_demo_scenarios::MetaplexTokenMetadataAssetFamily,
|
|
|
|
|
) -> std::result::Result<
|
|
|
|
|
crate::DemoExecutionMetadataMetaplexTokenMetadataCreateFixturePayload,
|
|
|
|
|
std::string::String,
|
|
|
|
|
> {
|
|
|
|
|
let profile = match crate::select_devnet_profile(state.app_config(), profile_name.as_str()) {
|
|
|
|
|
std::result::Result::Ok(value) => value,
|
|
|
|
|
@@ -303,11 +428,14 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_prepare_crea
|
|
|
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
|
|
|
|
|
};
|
|
|
|
|
let workspace_root = crate::workspace_root_dir();
|
|
|
|
|
let options =
|
|
|
|
|
kb_pipeline_demo_scenarios::MetaplexCreateFixturePreparationOptions::new(wallet_dir)
|
|
|
|
|
.with_asset_family(asset_family);
|
|
|
|
|
let summary = match kb_pipeline_demo_scenarios::prepare_metaplex_create_fixture(
|
|
|
|
|
&http_pool,
|
|
|
|
|
&profile,
|
|
|
|
|
workspace_root.as_path(),
|
|
|
|
|
&kb_pipeline_demo_scenarios::MetaplexCreateFixturePreparationOptions::new(wallet_dir),
|
|
|
|
|
&options,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
@@ -326,84 +454,6 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_prepare_crea
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_prepare_step(
|
|
|
|
|
state: tauri::State<'_, crate::AppState>,
|
|
|
|
|
@@ -452,27 +502,17 @@ pub(crate) async fn demo_execution_metadata_metaplex_token_metadata_prepare_step
|
|
|
|
|
let (fixture_mint, fixture_metadata, fixture_master_edition, operation_json, fixture_state) =
|
|
|
|
|
if operation == "create" {
|
|
|
|
|
let fixture =
|
|
|
|
|
match crate::demo_execution_metadata_metaplex_token_metadata_prepare_create_fixture(
|
|
|
|
|
state,
|
|
|
|
|
profile_name,
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
match prepare_create_fixture_for_family(state, profile_name, scenario.asset_family)
|
|
|
|
|
.await
|
|
|
|
|
{
|
|
|
|
|
std::result::Result::Ok(value) => value,
|
|
|
|
|
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.metadata,
|
|
|
|
|
fixture.master_edition,
|
|
|
|
|
operation_json,
|
|
|
|
|
fixture.operation_json,
|
|
|
|
|
if fixture.mint_created {
|
|
|
|
|
"mint_prepared".to_string()
|
|
|
|
|
} else {
|
|
|
|
|
|