v0.4.8-pre.011
This commit is contained in:
@@ -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 l’autorité 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 d’exécution sur les cinq opérations de `spl-token-metadata-interface`, en rattachant également `UpdateAuthority` et `Emit` à l’entré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 l’autorité 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 d’opé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 à d’autres instructions ne sont plus rejetés ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: kb-lib/README.md -->
|
||||
<!-- version: 26 -->
|
||||
<!-- version: 27 -->
|
||||
|
||||
# kb-lib
|
||||
|
||||
@@ -103,3 +103,5 @@ Le registre ElGamal n’est 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 d’exécution Solana Program Metadata](../test-fixtures/contract-matrices/SOLANA_PROGRAM_METADATA_EXECUTOR_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)
|
||||
|
||||
- complétude d’exécution des cinq instructions Token-2022 Token Metadata, y compris `UpdateAuthority` et `Emit` ;
|
||||
|
||||
@@ -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. Lorsqu’elle 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 n’est 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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,
|
||||
¤t_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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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 { .. }
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user