v0.4.8-pre.011

This commit is contained in:
2026-08-07 07:14:49 +02:00
parent 3167297283
commit dcc1d382d9
38 changed files with 1358 additions and 129 deletions

View File

@@ -1,8 +1,21 @@
<!-- file: kb-lib/CHANGELOG.md -->
<!-- version: 30 -->
<!-- version: 35 -->
# CHANGELOG — kb-lib
## `0.4.8-pre.011` — complétude Token-2022 Token Metadata
- ajoute les intents et builders exacts `UpdateTokenMetadataAuthority` et `EmitTokenMetadata` ;
- encode lautorité nullable via le contrat officiel et borne les plages `Emit` à 1 024 octets ;
- conserve le `returnData` de simulation et expose le parseur strict de la valeur `TokenMetadata` ;
- corrige le feature-gating TS-RS du `serde_json::Value` exposé par le résultat de simulation afin que le derive `TS` et la régénération des bindings utilisent `JsonValue` ;
- refuse les plages `Emit` ouvertes avec `start` sans `end`, dont la taille ne peut pas être bornée avant simulation ;
- aligne le test de parité de la matrice dexécution sur les cinq opérations de `spl-token-metadata-interface`, en rattachant également `UpdateAuthority` et `Emit` à lentrée `token_metadata` ;
- extrait le mapping capacités → familles de matrice dans un helper testé directement, afin de rendre explicite et vérifiable le rattachement de `update_token_metadata_authority` et `emit_token_metadata` à `token_metadata` ;
- ajoute les régressions de clôture couvrant lautorité nullable `None`, la forme `Emit` complète sans plage et la plage maximale exactement égale à 1 024 octets.
- corrige dans `pre.011-fix-006` les deux attentes de nommage de ces régressions afin de vérifier les codes dopération canoniques `spl.token_2022.update_token_metadata_authority` et `spl.token_2022.emit_token_metadata`.
## 0.4.8-pre.008 — matérialiseurs metadata spécialisés
- corrige le décodeur Solana Program Metadata pour interpréter les flags `signer` et `writable` du replay comme des privilèges globaux de message : les privilèges requis sont désormais des minima, et les sur-ensembles légitimes dus au fee payer ou à dautres instructions ne sont plus rejetés ;

View File

@@ -1,5 +1,5 @@
<!-- file: kb-lib/README.md -->
<!-- version: 26 -->
<!-- version: 27 -->
# kb-lib
@@ -103,3 +103,5 @@ Le registre ElGamal nest pas déclaré validé réellement sur réseau.
- [Matrice de matérialisation Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_MATERIALIZATION_MATRIX.json)
- [Matrice dexécution Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_EXECUTOR_MATRIX.json)
- [Audit de linventaire et de lhistorique des instructions](../docs/audits/V0_4_8_PRE_005_SOLANA_PROGRAM_METADATA_INSTRUCTION_HISTORY_AUDIT.md)
- complétude dexécution des cinq instructions Token-2022 Token Metadata, y compris `UpdateAuthority` et `Emit` ;

View File

@@ -1,5 +1,5 @@
<!-- file: kb-lib/USAGE.md -->
<!-- version: 14 -->
<!-- version: 15 -->
# Utilisation de kb-lib
@@ -414,3 +414,7 @@ Pour un NFT classique créé par le runner :
- un mint dont le PDA metadata est déjà créé ne peut pas être réutilisé pour un second `Create`.
La matérialisation Metaplex est optionnelle. Lorsquelle est activée après une soumission confirmée, chaque compte déclaré dans les lectures de postcondition est relu, validé, décodé canoniquement puis projeté comme snapshot détat. Aucun contenu URI off-chain nest téléchargé par ce parcours.
## Token-2022 Token Metadata complet
Les opérations `InitializeTokenMetadata`, `UpdateTokenMetadataField`, `RemoveTokenMetadataKey`, `UpdateTokenMetadataAuthority` et `EmitTokenMetadata` sont constructibles. `EmitTokenMetadata` est en lecture seule et ne réclame pas une matérialisation post-exécution.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder.rs
// version: 16
// version: 17
//! Consolidated decoder modules.
@@ -745,6 +745,8 @@ pub(crate) use self::spl::decoder_spl_token_2022_decode;
pub(crate) use self::spl::decoder_spl_token_2022_entry_for_tag;
/// Parses one Token-2022 Mint, Account, or Multisig state.
pub use self::spl::decoder_spl_token_2022_parse_token_2022_state;
/// Parses one exact Token Metadata interface value.
pub use self::spl::decoder_spl_token_2022_parse_token_metadata_value;
/// Returns the first byte of one retained Token-2022 payload.
pub(crate) use self::spl::decoder_spl_token_2022_payload_tag;
/// Decodes one bounded classic SPL Token instruction.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/spl.rs
// version: 10
// version: 11
//! `spl` decoder family.
@@ -147,5 +147,7 @@ pub(crate) use self::token_2022::decoder_spl_token_2022_decode;
pub(crate) use self::token_2022::decoder_spl_token_2022_entry_for_tag;
/// Parses one Token-2022 Mint, Account, or Multisig state.
pub use self::token_2022::decoder_spl_token_2022_parse_token_2022_state;
/// Parses one exact Token Metadata interface value.
pub use self::token_2022::decoder_spl_token_2022_parse_token_metadata_value;
/// Returns the first byte of one retained Token-2022 payload.
pub(crate) use self::token_2022::decoder_spl_token_2022_payload_tag;

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/spl/token_2022.rs
// version: 4
// version: 5
//! Exact Token-2022 instruction and state decoder component.
@@ -18,6 +18,8 @@ pub use self::state::DcToken2022StateKind;
pub use self::state::DcToken2022TlvEntry;
/// Parses one Token-2022 Mint, Account, or Multisig state.
pub use self::state::decoder_spl_token_2022_parse_token_2022_state;
/// Parses one exact Token Metadata interface value.
pub use self::state::decoder_spl_token_2022_parse_token_metadata_value;
/// Maximum prefix retained in bounded Token-2022 diagnostics.
pub(crate) use self::constants::DC_SPL_TOKEN_2022_DIAGNOSTIC_PREFIX_BYTES;

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/spl/token_2022/state.rs
// version: 3
// version: 4
//! Strict Token-2022 base-state and TLV parsing.
@@ -429,6 +429,13 @@ fn decode_token_group_member(value: &[u8]) -> Result<serde_json::Value, String>
}));
}
/// Parses one exact Token Metadata interface value with full-consumption checks.
pub fn decoder_spl_token_2022_parse_token_metadata_value(
value: &[u8],
) -> std::result::Result<serde_json::Value, std::string::String> {
return decode_token_metadata(value);
}
fn decode_token_metadata(value: &[u8]) -> Result<serde_json::Value, String> {
if value.len() < 76 {
return Err(format!(

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor.rs
// version: 16
// version: 17
//! Consolidated executor modules.
@@ -714,6 +714,8 @@ pub use self::spl::EX_SPL_TOKEN_2022_DISABLE_REQUIRED_TRANSFER_MEMOS_OPERATION;
pub use self::spl::EX_SPL_TOKEN_2022_ELGAMAL_CIPHERTEXT_BYTES;
/// Exposes `EX_SPL_TOKEN_2022_ELGAMAL_PUBKEY_BYTES`.
pub use self::spl::EX_SPL_TOKEN_2022_ELGAMAL_PUBKEY_BYTES;
/// Exposes `EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION`.
pub use self::spl::EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION`.
pub use self::spl::EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_ENABLE_CONFIDENTIAL_CREDITS_OPERATION`.
@@ -844,6 +846,8 @@ pub use self::spl::EX_SPL_TOKEN_2022_UPDATE_METADATA_POINTER_OPERATION;
pub use self::spl::EX_SPL_TOKEN_2022_UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION`.
pub use self::spl::EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION`.
pub use self::spl::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION`.
pub use self::spl::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TRANSFER_HOOK_OPERATION`.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/api/execution.rs
// version: 8
// version: 9
//! Typed execution plans, policies and result contracts.
@@ -378,6 +378,9 @@ pub struct ExApiExecutionSimulationResult {
pub estimated_fee_lamports: std::option::Option<u64>,
/// Simulation logs in runtime order.
pub logs: std::vec::Vec<std::string::String>,
/// Program return data retained as provider-neutral JSON when reported.
#[serde(default)]
pub return_data: std::option::Option<serde_json::Value>,
/// Stable or provider error text when simulation failed.
pub error: std::option::Option<std::string::String>,
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/safety/evaluation.rs
// version: 2
// version: 3
//! Safety checks applied before simulation, signing or sending.
@@ -516,6 +516,7 @@ mod tests {
units_consumed: std::option::Option::Some(500),
estimated_fee_lamports: std::option::Option::Some(5_000),
logs: std::vec::Vec::new(),
return_data: std::option::Option::None,
error: std::option::Option::None,
};
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/solana/transaction/core.rs
// version: 9
// version: 10
//! Legacy and durable nonce Solana transaction assembly, simulation binding and signing.
@@ -750,6 +750,7 @@ mod tests {
units_consumed: std::option::Option::Some(500),
estimated_fee_lamports: std::option::Option::Some(5_000),
logs: std::vec::Vec::new(),
return_data: std::option::Option::None,
error: std::option::Option::None,
};
}
@@ -773,6 +774,7 @@ mod tests {
units_consumed: std::option::Option::Some(700),
estimated_fee_lamports: std::option::Option::Some(10_000),
logs: std::vec::Vec::new(),
return_data: std::option::Option::None,
error: std::option::Option::None,
};
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/spl.rs
// version: 7
// version: 8
//! `spl` executor family.
@@ -183,6 +183,8 @@ pub use self::token_2022::EX_SPL_TOKEN_2022_DISABLE_REQUIRED_TRANSFER_MEMOS_OPER
pub use self::token_2022::EX_SPL_TOKEN_2022_ELGAMAL_CIPHERTEXT_BYTES;
/// Exposes `EX_SPL_TOKEN_2022_ELGAMAL_PUBKEY_BYTES`.
pub use self::token_2022::EX_SPL_TOKEN_2022_ELGAMAL_PUBKEY_BYTES;
/// Exposes `EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION`.
pub use self::token_2022::EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION`.
pub use self::token_2022::EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_ENABLE_CONFIDENTIAL_CREDITS_OPERATION`.
@@ -313,6 +315,8 @@ pub use self::token_2022::EX_SPL_TOKEN_2022_UPDATE_METADATA_POINTER_OPERATION;
pub use self::token_2022::EX_SPL_TOKEN_2022_UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION`.
pub use self::token_2022::EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION`.
pub use self::token_2022::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION`.
pub use self::token_2022::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TRANSFER_HOOK_OPERATION`.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/spl/token_2022.rs
// version: 5
// version: 6
//! SPL Token-2022 executor.
@@ -86,6 +86,8 @@ pub use self::intent::EX_SPL_TOKEN_2022_DISABLE_CPI_GUARD_OPERATION;
pub use self::intent::EX_SPL_TOKEN_2022_DISABLE_NON_CONFIDENTIAL_CREDITS_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_DISABLE_REQUIRED_TRANSFER_MEMOS_OPERATION`.
pub use self::intent::EX_SPL_TOKEN_2022_DISABLE_REQUIRED_TRANSFER_MEMOS_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION`.
pub use self::intent::EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION`.
pub use self::intent::EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_ENABLE_CONFIDENTIAL_CREDITS_OPERATION`.
@@ -214,6 +216,8 @@ pub use self::intent::EX_SPL_TOKEN_2022_UPDATE_METADATA_POINTER_OPERATION;
pub use self::intent::EX_SPL_TOKEN_2022_UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION`.
pub use self::intent::EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION`.
pub use self::intent::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION`.
pub use self::intent::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TRANSFER_HOOK_OPERATION`.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/spl/token_2022/builder.rs
// version: 30
// version: 34
//! Official Token-2022 builders and conservative plan validation.
@@ -172,6 +172,41 @@ fn validate_metadata_string(value: &str, field: &str) -> kb_core::Result<()> {
return std::result::Result::Ok(());
}
fn validate_token_metadata_emit_range(
start: std::option::Option<u64>,
end: std::option::Option<u64>,
) -> kb_core::Result<()> {
if let (std::option::Option::Some(start), std::option::Option::Some(end)) = (start, end) {
if start > end {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_metadata_emit_range_invalid",
format!("Token metadata Emit start {start} exceeds end {end}"),
));
}
if end - start > 1_024 {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_metadata_emit_range_too_large",
"Token metadata Emit range exceeds the 1024-byte Solana return-data bound",
));
}
}
if let (std::option::Option::None, std::option::Option::Some(end)) = (start, end) {
if end > 1_024 {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_metadata_emit_range_too_large",
"Token metadata Emit range from zero exceeds the 1024-byte Solana return-data bound",
));
}
}
if let (std::option::Option::Some(_), std::option::Option::None) = (start, end) {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_metadata_emit_range_end_required",
"Token metadata Emit with a start offset requires an explicit end to remain bounded",
));
}
return std::result::Result::Ok(());
}
fn default_account_state(
state: crate::ExSplTokenDefaultAccountState,
) -> spl_token_2022_interface::state::AccountState {
@@ -1162,6 +1197,40 @@ fn build_single(
*idempotent,
))
},
crate::ExSplTokenSingleOperation::UpdateTokenMetadataAuthority {
metadata,
current_authority,
new_authority,
} => {
let metadata = parse_key!(metadata, "metadata");
let current_authority = parse_key!(current_authority, "current_authority");
let new_authority = parse_optional_key!(new_authority, "new_authority");
let nullable_authority = match new_authority.try_into() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_spl_token_2022_metadata_authority_invalid",
"Token metadata authority cannot be encoded as a nullable address",
));
},
};
std::result::Result::Ok(spl_token_metadata_interface::instruction::update_authority(
program_id,
&metadata,
&current_authority,
nullable_authority,
))
},
crate::ExSplTokenSingleOperation::EmitTokenMetadata { metadata, start, end } => {
match validate_token_metadata_emit_range(*start, *end) {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
let metadata = parse_key!(metadata, "metadata");
std::result::Result::Ok(spl_token_metadata_interface::instruction::emit(
program_id, &metadata, *start, *end,
))
},
crate::ExSplTokenSingleOperation::InitializeTokenGroup {
group,
mint,
@@ -3566,6 +3635,22 @@ pub(crate) mod tests {
idempotent: true,
},
),
(
255,
crate::ExSplTokenSingleOperation::UpdateTokenMetadataAuthority {
metadata: first.clone(),
current_authority: second.clone(),
new_authority: std::option::Option::Some(third.clone()),
},
),
(
255,
crate::ExSplTokenSingleOperation::EmitTokenMetadata {
metadata: first.clone(),
start: std::option::Option::Some(0),
end: std::option::Option::Some(64),
},
),
(
255,
crate::ExSplTokenSingleOperation::InitializeTokenGroup {
@@ -4706,6 +4791,7 @@ pub(crate) mod tests {
uri: std::string::String::from("https://example.invalid/token.json"),
},
4_usize,
true,
),
(
crate::ExSplTokenSingleOperation::UpdateTokenMetadataField {
@@ -4717,6 +4803,7 @@ pub(crate) mod tests {
value: std::string::String::from("https://example.invalid"),
},
2_usize,
true,
),
(
crate::ExSplTokenSingleOperation::RemoveTokenMetadataKey {
@@ -4726,6 +4813,25 @@ pub(crate) mod tests {
idempotent: true,
},
2_usize,
true,
),
(
crate::ExSplTokenSingleOperation::UpdateTokenMetadataAuthority {
metadata: first.clone(),
current_authority: second.clone(),
new_authority: std::option::Option::Some(third.clone()),
},
2_usize,
true,
),
(
crate::ExSplTokenSingleOperation::EmitTokenMetadata {
metadata: first.clone(),
start: std::option::Option::Some(8),
end: std::option::Option::Some(72),
},
1_usize,
false,
),
(
crate::ExSplTokenSingleOperation::InitializeTokenGroup {
@@ -4736,6 +4842,7 @@ pub(crate) mod tests {
max_size: 42,
},
3_usize,
true,
),
(
crate::ExSplTokenSingleOperation::UpdateTokenGroupMaxSize {
@@ -4744,6 +4851,7 @@ pub(crate) mod tests {
max_size: 84,
},
2_usize,
true,
),
(
crate::ExSplTokenSingleOperation::InitializeTokenGroupMember {
@@ -4754,9 +4862,10 @@ pub(crate) mod tests {
group_update_authority: second,
},
5_usize,
true,
),
];
for (operation, account_count) in cases {
for (operation, account_count, first_writable) in cases {
let intent = intent(crate::ExSplToken2022Operation::Instruction {
value: std::boxed::Box::new(operation),
});
@@ -4767,7 +4876,63 @@ pub(crate) mod tests {
.unwrap_or_else(|error| panic!("metadata or group plan failed: {error}"));
assert!(plan.instructions[0].data.len() >= 8);
assert_eq!(plan.instructions[0].accounts.len(), account_count);
assert!(plan.instructions[0].accounts[0].is_writable);
assert_eq!(plan.instructions[0].accounts[0].is_writable, first_writable);
}
}
#[test]
fn metadata_update_authority_accepts_nullable_none() {
let planned = planned_single_instruction(
crate::ExSplTokenSingleOperation::UpdateTokenMetadataAuthority {
metadata: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID),
current_authority: pubkey(kb_program_ids::CONFIG_PROGRAM_ID),
new_authority: std::option::Option::None,
},
);
assert_eq!(planned.operation_code, "spl.token_2022.update_token_metadata_authority");
assert_eq!(planned.accounts.len(), 2);
assert!(planned.accounts[0].is_writable);
assert!(planned.accounts[1].is_signer);
}
#[test]
fn metadata_emit_accepts_complete_and_maximum_bounded_ranges() {
for (start, end) in [
(std::option::Option::None, std::option::Option::None),
(std::option::Option::Some(0), std::option::Option::Some(1_024)),
] {
let planned =
planned_single_instruction(crate::ExSplTokenSingleOperation::EmitTokenMetadata {
metadata: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID),
start,
end,
});
assert_eq!(planned.operation_code, "spl.token_2022.emit_token_metadata");
assert_eq!(planned.accounts.len(), 1);
assert!(!planned.accounts[0].is_writable);
}
}
#[test]
fn metadata_emit_rejects_reversed_oversized_and_open_ended_ranges() {
let metadata = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID);
for (start, end) in [
(std::option::Option::Some(9), std::option::Option::Some(8)),
(std::option::Option::Some(0), std::option::Option::Some(1_025)),
(std::option::Option::Some(1), std::option::Option::None),
] {
let value = crate::ExSplTokenSingleOperation::EmitTokenMetadata {
metadata: metadata.clone(),
start,
end,
};
let result = crate::ExApiTypedInstructionExecutor::build_prepared_plan(
&crate::ExSplToken2022Executor,
&intent(crate::ExSplToken2022Operation::Instruction {
value: std::boxed::Box::new(value),
}),
);
assert!(result.is_err());
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/spl/token_2022/executor.rs
// version: 23
// version: 25
//! Exact Token-2022 capability dispatch and typed plan construction.
@@ -220,6 +220,86 @@ mod tests {
);
}
fn matrix_operation_name(name: &str) -> &str {
return match name {
"initialize_interest_bearing_mint" | "update_interest_bearing_rate" => {
"interest_bearing_mint_extension"
},
"initialize_transfer_fee_config"
| "set_transfer_fee"
| "transfer_checked_with_fee"
| "withdraw_withheld_tokens_from_mint"
| "withdraw_withheld_tokens_from_accounts"
| "harvest_withheld_tokens_to_mint" => "transfer_fee_extension",
"initialize_default_account_state" | "update_default_account_state" => {
"default_account_state_extension"
},
"enable_required_transfer_memos" | "disable_required_transfer_memos" => {
"memo_transfer_extension"
},
"enable_cpi_guard" | "disable_cpi_guard" => "cpi_guard_extension",
"initialize_transfer_hook" | "update_transfer_hook" => "transfer_hook_extension",
"initialize_metadata_pointer" | "update_metadata_pointer" => {
"metadata_pointer_extension"
},
"initialize_group_pointer" | "update_group_pointer" => "group_pointer_extension",
"initialize_group_member_pointer" | "update_group_member_pointer" => {
"group_member_pointer_extension"
},
"initialize_token_metadata"
| "update_token_metadata_field"
| "remove_token_metadata_key"
| "update_token_metadata_authority"
| "emit_token_metadata" => "token_metadata",
"initialize_token_group" | "update_token_group_max_size" => "token_group",
"initialize_token_group_member" => "token_group_member",
"initialize_scaled_ui_amount" | "update_scaled_ui_amount_multiplier" => {
"scaled_ui_amount_extension"
},
"initialize_pausable_config" | "pause" | "resume" => "pausable_extension",
"initialize_permissioned_burn"
| "permissioned_burn"
| "permissioned_burn_checked"
| "permissioned_confidential_burn" => "permissioned_burn_extension",
"initialize_confidential_transfer_mint"
| "update_confidential_transfer_mint"
| "apply_pending_confidential_balance"
| "approve_confidential_transfer_account"
| "deposit_confidential_tokens"
| "enable_confidential_credits"
| "disable_confidential_credits"
| "enable_non_confidential_credits"
| "disable_non_confidential_credits"
| "configure_confidential_transfer_account"
| "configure_confidential_transfer_account_with_registry"
| "empty_confidential_transfer_account"
| "withdraw_confidential_tokens"
| "transfer_confidential_tokens"
| "transfer_confidential_tokens_with_fee" => "confidential_transfer_extension",
"initialize_confidential_transfer_fee_config"
| "enable_confidential_transfer_fee_harvest"
| "disable_confidential_transfer_fee_harvest"
| "harvest_confidential_withheld_tokens_to_mint"
| "withdraw_confidential_withheld_tokens_from_mint"
| "withdraw_confidential_withheld_tokens_from_accounts" => {
"confidential_transfer_fee_extension"
},
"initialize_confidential_mint_burn_mint"
| "rotate_confidential_supply_elgamal_pubkey"
| "update_confidential_decryptable_supply"
| "apply_pending_confidential_burn"
| "confidential_mint"
| "confidential_burn" => "confidential_mint_burn_extension",
_ => name,
};
}
#[test]
fn token_metadata_operations_share_matrix_family() {
assert_eq!(matrix_operation_name("update_token_metadata_authority"), "token_metadata");
assert_eq!(matrix_operation_name("emit_token_metadata"), "token_metadata");
}
#[test]
fn machine_readable_matrix_matches_executor_policy() {
let matrix = serde_json::from_str::<serde_json::Value>(include_str!(
@@ -242,75 +322,7 @@ mod tests {
let name = operation_code
.strip_prefix("spl.token_2022.")
.unwrap_or_else(|| panic!("invalid operation code {operation_code}"));
let matrix_name = match name {
"initialize_interest_bearing_mint" | "update_interest_bearing_rate" => {
"interest_bearing_mint_extension"
},
"initialize_transfer_fee_config"
| "set_transfer_fee"
| "transfer_checked_with_fee"
| "withdraw_withheld_tokens_from_mint"
| "withdraw_withheld_tokens_from_accounts"
| "harvest_withheld_tokens_to_mint" => "transfer_fee_extension",
"initialize_default_account_state" | "update_default_account_state" => {
"default_account_state_extension"
},
"enable_required_transfer_memos" | "disable_required_transfer_memos" => {
"memo_transfer_extension"
},
"enable_cpi_guard" | "disable_cpi_guard" => "cpi_guard_extension",
"initialize_transfer_hook" | "update_transfer_hook" => "transfer_hook_extension",
"initialize_metadata_pointer" | "update_metadata_pointer" => {
"metadata_pointer_extension"
},
"initialize_group_pointer" | "update_group_pointer" => "group_pointer_extension",
"initialize_group_member_pointer" | "update_group_member_pointer" => {
"group_member_pointer_extension"
},
"initialize_token_metadata"
| "update_token_metadata_field"
| "remove_token_metadata_key" => "token_metadata",
"initialize_token_group" | "update_token_group_max_size" => "token_group",
"initialize_token_group_member" => "token_group_member",
"initialize_scaled_ui_amount" | "update_scaled_ui_amount_multiplier" => {
"scaled_ui_amount_extension"
},
"initialize_pausable_config" | "pause" | "resume" => "pausable_extension",
"initialize_permissioned_burn"
| "permissioned_burn"
| "permissioned_burn_checked"
| "permissioned_confidential_burn" => "permissioned_burn_extension",
"initialize_confidential_transfer_mint"
| "update_confidential_transfer_mint"
| "apply_pending_confidential_balance"
| "approve_confidential_transfer_account"
| "deposit_confidential_tokens"
| "enable_confidential_credits"
| "disable_confidential_credits"
| "enable_non_confidential_credits"
| "disable_non_confidential_credits"
| "configure_confidential_transfer_account"
| "configure_confidential_transfer_account_with_registry"
| "empty_confidential_transfer_account"
| "withdraw_confidential_tokens"
| "transfer_confidential_tokens"
| "transfer_confidential_tokens_with_fee" => "confidential_transfer_extension",
"initialize_confidential_transfer_fee_config"
| "enable_confidential_transfer_fee_harvest"
| "disable_confidential_transfer_fee_harvest"
| "harvest_confidential_withheld_tokens_to_mint"
| "withdraw_confidential_withheld_tokens_from_mint"
| "withdraw_confidential_withheld_tokens_from_accounts" => {
"confidential_transfer_fee_extension"
},
"initialize_confidential_mint_burn_mint"
| "rotate_confidential_supply_elgamal_pubkey"
| "update_confidential_decryptable_supply"
| "apply_pending_confidential_burn"
| "confidential_mint"
| "confidential_burn" => "confidential_mint_burn_extension",
_ => name,
};
let matrix_name = matrix_operation_name(name);
assert!(
matrix_names.contains(matrix_name),
"matrix is missing supported operation {name}"

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/spl/token_2022/intent.rs
// version: 26
// version: 27
//! Typed Token-2022 execution intents.
@@ -138,6 +138,12 @@ pub const EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION: &str =
/// Stable operation code for removing an embedded metadata key.
pub const EX_SPL_TOKEN_2022_REMOVE_TOKEN_METADATA_KEY_OPERATION: &str =
"spl.token_2022.remove_token_metadata_key";
/// Stable operation code for updating or clearing embedded metadata authority.
pub const EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION: &str =
"spl.token_2022.update_token_metadata_authority";
/// Stable operation code for emitting bounded embedded metadata return data.
pub const EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION: &str =
"spl.token_2022.emit_token_metadata";
/// Stable operation code for initializing an embedded token group.
pub const EX_SPL_TOKEN_2022_INITIALIZE_TOKEN_GROUP_OPERATION: &str =
"spl.token_2022.initialize_token_group";
@@ -307,6 +313,8 @@ pub const EX_SPL_TOKEN_2022_SUPPORTED_OPERATION_CODES: &[&str] = &[
EX_SPL_TOKEN_2022_INITIALIZE_TOKEN_METADATA_OPERATION,
EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION,
EX_SPL_TOKEN_2022_REMOVE_TOKEN_METADATA_KEY_OPERATION,
EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION,
EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION,
EX_SPL_TOKEN_2022_INITIALIZE_TOKEN_GROUP_OPERATION,
EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION,
EX_SPL_TOKEN_2022_INITIALIZE_TOKEN_GROUP_MEMBER_OPERATION,
@@ -919,6 +927,24 @@ pub enum ExSplTokenSingleOperation {
/// Whether a missing key is accepted.
idempotent: bool,
},
/// Update or permanently clear embedded metadata authority.
UpdateTokenMetadataAuthority {
/// Metadata account.
metadata: crate::MdPubkey,
/// Current update authority signer.
current_authority: crate::MdPubkey,
/// New authority, or `None` to make metadata immutable.
new_authority: std::option::Option<crate::MdPubkey>,
},
/// Emit all or one bounded range of embedded metadata through return data.
EmitTokenMetadata {
/// Metadata account.
metadata: crate::MdPubkey,
/// Optional inclusive byte-range start.
start: std::option::Option<u64>,
/// Optional exclusive byte-range end.
end: std::option::Option<u64>,
},
/// Initialize a Token Group entry.
InitializeTokenGroup {
/// Group state account, normally the group mint.
@@ -1454,6 +1480,10 @@ impl crate::ExSplTokenSingleOperation {
Self::InitializeTokenMetadata { .. } => crate::EX_SPL_TOKEN_2022_INITIALIZE_TOKEN_METADATA_OPERATION,
Self::UpdateTokenMetadataField { .. } => crate::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION,
Self::RemoveTokenMetadataKey { .. } => crate::EX_SPL_TOKEN_2022_REMOVE_TOKEN_METADATA_KEY_OPERATION,
Self::UpdateTokenMetadataAuthority { .. } => {
crate::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION
},
Self::EmitTokenMetadata { .. } => crate::EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION,
Self::InitializeTokenGroup { .. } => crate::EX_SPL_TOKEN_2022_INITIALIZE_TOKEN_GROUP_OPERATION,
Self::UpdateTokenGroupMaxSize { .. } => crate::EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION,
Self::InitializeTokenGroupMember { .. } => {
@@ -1566,6 +1596,7 @@ impl crate::ExSplTokenSingleOperation {
return !matches!(
self,
Self::GetAccountDataSize { .. }
| Self::EmitTokenMetadata { .. }
| Self::InitializeImmutableOwner { .. }
| Self::AmountToUiAmount { .. }
| Self::UiAmountToAmount { .. }

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/lib.rs
// version: 37
// version: 38
//! Consolidated decoder, executor, materializer and shared model library.
#![warn(missing_docs)]
@@ -401,6 +401,8 @@ pub use self::decoder::decoder_metadata_solana_program_metadata_decode_metadata_
pub use self::decoder::decoder_spl_elgamal_registry_parse_elgamal_registry_state;
/// Parses one Token-2022 Mint, Account, or Multisig state.
pub use self::decoder::decoder_spl_token_2022_parse_token_2022_state;
/// Parses one exact Token Metadata interface value.
pub use self::decoder::decoder_spl_token_2022_parse_token_metadata_value;
/// Stable Solana Program Metadata `Allocate` operation code.
pub use self::executor::EX_METADATA_SPM_ALLOCATE_OPERATION;
/// Stable Solana Program Metadata `Close` operation code.
@@ -791,6 +793,8 @@ pub use self::executor::EX_SPL_TOKEN_2022_DISABLE_REQUIRED_TRANSFER_MEMOS_OPERAT
pub use self::executor::EX_SPL_TOKEN_2022_ELGAMAL_CIPHERTEXT_BYTES;
/// Exposes `EX_SPL_TOKEN_2022_ELGAMAL_PUBKEY_BYTES`.
pub use self::executor::EX_SPL_TOKEN_2022_ELGAMAL_PUBKEY_BYTES;
/// Exposes `EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION`.
pub use self::executor::EX_SPL_TOKEN_2022_EMIT_TOKEN_METADATA_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION`.
pub use self::executor::EX_SPL_TOKEN_2022_EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_ENABLE_CONFIDENTIAL_CREDITS_OPERATION`.
@@ -921,6 +925,8 @@ pub use self::executor::EX_SPL_TOKEN_2022_UPDATE_METADATA_POINTER_OPERATION;
pub use self::executor::EX_SPL_TOKEN_2022_UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION`.
pub use self::executor::EX_SPL_TOKEN_2022_UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION`.
pub use self::executor::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_AUTHORITY_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION`.
pub use self::executor::EX_SPL_TOKEN_2022_UPDATE_TOKEN_METADATA_FIELD_OPERATION;
/// Exposes `EX_SPL_TOKEN_2022_UPDATE_TRANSFER_HOOK_OPERATION`.