v0.4.8-pre.013

This commit is contained in:
2026-08-08 22:28:17 +02:00
parent f5efce576b
commit 8b831f8692
77 changed files with 15590 additions and 1585 deletions

View File

@@ -1,8 +1,41 @@
<!-- file: kb-lib/CHANGELOG.md -->
<!-- version: 35 -->
<!-- version: 48 -->
# CHANGELOG — kb-lib
## `0.4.8-pre.013` — exactitude des builders Metaplex courants
- `pre.013-delta-fix-033` aligne la validation du décodeur `Update` sur les privilèges runtime globaux : lauthority doit signer et les comptes mutables doivent rester writable, mais un même wallet utilisé comme authority et fee payer peut apparaître writable à la position authority lors du replay Core ; le test `modern_update_variants_accept_transaction_privilege_supersets` couvre cette forme sans augmenter le nombre total de tests.
- `pre.013-delta-fix-032` corrige le contrat des comptes optionnels terminaux des instructions escrow historiques `CreateEscrowAccount` et `TransferOutOfEscrow` : lorsque `authority=None`, aucun placeholder Metaplex nest ajouté en fin de liste, conformément aux builders officiels et au handler qui déduit la présence de lautorité à partir du nombre de comptes ; les comptes optionnels positionnels des instructions courantes conservent leurs placeholders. Le test de builder existant couvre désormais les formes escrow sans authority et avec authority explicite.
- `pre.013-delta-fix-029` aligne le décodeur courant `Use` sur les privilèges minimaux du builder `mpl-token-metadata 5.1.1` : lautorité et le payer doivent signer, Metadata doit être writable, et les comptes optionnels réellement présents (`delegate_record`, `token`, `edition`) restent writable ; les privilèges globaux supplémentaires hérités du fee payer ne provoquent plus de faux rejet.
- `pre.013-delta-fix-027` normalise la projection JSON canonique des comptes `TokenRecord` : `delegate` et `locked_transfer` sont désormais exposés en chaînes base58, comme les champs typés du snapshot, au lieu de dépendre de la représentation Serde interne de `solana_pubkey::Pubkey` ; la régression existante verrouille explicitement ces deux projections et `delegate_role`.
- `pre.013-delta-fix-026` corrige le replay des opérations pNFT `Delegate`/`Revoke` et `Lock`/`Unlock` sur la frontière des privilèges globaux de transaction : le décodeur vérifie désormais uniquement les minima signer/writable réellement requis au lieu de rejeter les sur-ensembles hérités lorsquun même compte occupe aussi le rôle de fee payer ;
- conserve lexigence writable des comptes optionnels réellement mutés (`delegate_record`, `token_record`, `token`) lorsquils ne sont pas le placeholder positionnel du programme Token Metadata ;
- ajoute une régression reproduisant `Delegate/Revoke` avec lautorité également fee payer et `Lock/Unlock` avec le token owner également fee payer, sans relâcher les refus de signer/writable obligatoires manquants.
- `pre.013-delta-fix-025` corrige la lecture stateful des Master Edition pNFT compactes : les deux octets finaux de lallocation 20 octets sont des champs sémantiques Metaplex (`fee flag`, puis `TokenStandard`) et ne sont plus traités comme du padding nul ;
- conserve une validation fermée : les octets réservés intermédiaires doivent rester nuls, le fee flag est borné à `0/1`, et le trailer Master Edition naccepte que `NonFungible(0)` ou `ProgrammableNonFungible(4)` ;
- ajoute une régression reproduisant une Master Edition V2 compacte `supply=0,max_supply=0` avec trailer pNFT, ainsi que les refus dun fee flag, dun token standard ou dun octet réservé invalides.
- `pre.013-delta-fix-022` corrige la régression `modern_print_accepts_transaction_privilege_supersets` : un `edition_token_record` réellement fourni reste writable conformément au builder `Print` courant ; le test continue d'exercer les privilèges globaux supplémentaires sans retirer un minimum obligatoire ;
- aucun assouplissement du décodeur `Print` n'est introduit par ce correctif.
- `pre.013-delta-fix-021` corrige le décodage des comptes `Edition` réellement alloués par le programme courant : `Edition::LEN` décrit 41 octets sérialisés, tandis que les allocations Metaplex connues sont 42 octets en forme compacte et 241 octets en forme historique ;
- accepte uniquement la sérialisation exacte ou ces allocations connues avec padding nul, refuse les longueurs/paddings inconnus et verrouille aussi les allocations connues de Master Edition après lélargissement de la borne nécessaire aux anciennes Edition ;
- ajoute une régression couvrant Edition compacte/historique, Master Edition compacte/historique, longueur inconnue et padding non nul.
- `pre.013-delta-fix-018` corrige `Verify`/`Unverify` sur la même frontière de privilèges globaux déjà rencontrée pour `Create`/`Mint` : l'autorité peut être writable dans le message parce qu'elle est aussi fee payer, sans que l'AccountMeta Metaplex exige ce privilège ; le décodeur vérifie désormais uniquement les minima signer/writable réellement requis ;
- aligne `Unverify` sur les 7 positions du wrapper courant `mpl-token-metadata 5.1.1` au lieu d'exiger artificiellement 8 comptes, et ajoute une régression couvrant `Verify` et `Unverify` lorsque l'autorité fee payer hérite du privilège writable global ;
- `pre.013-delta-fix-009` corrige le décodeur des opérations courantes `Create` et `Mint` pour interpréter les flags signer/writable comme des privilèges globaux de transaction : seuls les privilèges requis constituent désormais des minima, sans rejeter les sur-ensembles légitimes dus au fee payer ou à un même wallet présent sous plusieurs rôles ;
- accepte explicitement `Create.mint_as_signer = false` pour un mint préexistant et ne force plus `update_authority_as_signer = true`, conformément aux booléens exposés par le SDK courant ;
- conserve lexigence writable pour un master edition/token record réellement fourni, tout en distinguant ces comptes des placeholders Metaplex positionnels ;
- ajoute deux régressions couvrant le `Create` à mint préexistant et le `Mint` où token owner, authority et payer héritent de privilèges transactionnels supérieurs.
- `pre.013-delta-fix-004` ferme laudit des comptes positionnels réellement optionnels du SDK courant pour `CreateEscrowAccount`, `TransferOutOfEscrow`, `Mint`, `Migrate`, `Use`, `Print` et `Resize` ;
- expose ces optionalités dans les intents publics et encode une absence avec le placeholder readonly du programme Metaplex, conformément au contrat Kinobi déjà attendu par le décodeur et la matrice ;
- conserve explicitement requis les comptes système/programmes qui ne sont que defaultables par le builder mais restent présents dans la struct dinstruction courante ;
- ajoute des régressions sur les positions optionnelles de `Mint`, `Migrate`, `Use`, `Print` et `Resize`, ainsi que sur lautorité optionnelle descrow ;
- intègre les deux `return` ajoutés lors de la validation locale de `fix-003` dans les closures de génération de pubkeys de test, afin que larchive reproduise létat Clippy réellement validé ;
- migre `Print` et `Resize` depuis les account metas figées de lIDL historique vers les builders officiels `mpl-token-metadata 5.1.1` ;
- rétablit le contrat signer courant de `Print` : `edition_mint`, `edition_mint_authority`, `payer` et `master_token_account_owner` sont désormais déclarés signataires conformément au SDK officiel ;
- rétablit le contrat signer courant de `Resize` : `payer` est writable + signer, tandis que lautorité fournie reste signer ;
- ajoute des régressions directes sur lordre, le discriminator et les flags signer/writable de ces deux opérations avant toute campagne Devnet associée.
## `0.4.8-pre.011` — complétude Token-2022 Token Metadata
- ajoute les intents et builders exacts `UpdateTokenMetadataAuthority` et `EmitTokenMetadata` ;

View File

@@ -1,5 +1,5 @@
<!-- file: kb-lib/USAGE.md -->
<!-- version: 15 -->
<!-- version: 16 -->
# Utilisation de kb-lib
@@ -307,6 +307,8 @@ La surface publique repose sur :
- les constantes `EX_METAPLEX_TOKEN_METADATA_*_OPERATION` ;
- `EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES` et `EX_METAPLEX_TOKEN_METADATA_DEPRECATED_OPERATION_CODES`.
Les comptes `Edition` Token Metadata doivent être décodés selon leur contenu Borsh **et** leur allocation on-chain. Le SDK `mpl-token-metadata 5.1.1` expose `Edition::LEN = 41`, tandis que le programme courant peut allouer 42 octets et les comptes historiques 241 octets. Le décodeur accepte seulement ces formes connues avec padding nul ; une longueur inconnue ou un padding non nul est refusé.
Le consommateur construit un intent typé, puis appelle `ExApiTypedInstructionExecutor::build_prepared_plan`. Le plan retourné contient les instructions, comptes et signers requis, mais neffectue ni simulation ni envoi.
Les opérations courantes utilisent leur wrapper canonique final. Les versions remplacées ne possèdent pas de builder public distinct. Les opérations obsolètes encore constructibles exigent que lintent autorise explicitement lexécution dépréciée ; leurs variantes Rust sont marquées `#[deprecated]`.

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/account.rs
// version: 12
// version: 15
//! Bounded decoding and PDA validation for Metaplex Token Metadata accounts.
@@ -103,6 +103,10 @@ const MAX_NAME_BYTES: usize = 32;
const MAX_SYMBOL_BYTES: usize = 10;
const MAX_URI_BYTES: usize = 200;
const MAX_CREATORS: usize = 5;
const COMPACT_MASTER_EDITION_ACCOUNT_BYTES: usize = 20;
const LEGACY_MASTER_EDITION_ACCOUNT_BYTES: usize = 282;
const COMPACT_EDITION_ACCOUNT_BYTES: usize = 42;
const LEGACY_EDITION_ACCOUNT_BYTES: usize = 241;
const EDITION_MARKER_V1_BITS: u64 = 248;
const MAX_EDITION_MARKER_V2_LEDGER_BYTES: usize = 1_048_576;
@@ -325,6 +329,75 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_metadata_account(
});
}
fn validate_known_account_allocation(
data: &[u8],
serialized_len: usize,
padded_account_lengths: &[usize],
) -> std::result::Result<(), DcMetadataMtmMetadataAccountDecodeError> {
if data.len() == serialized_len {
return std::result::Result::Ok(());
}
if data.len() < serialized_len || !padded_account_lengths.contains(&data.len()) {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidDataLength,
);
}
if data[serialized_len..].iter().any(|byte| return *byte != 0) {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout,
);
}
return std::result::Result::Ok(());
}
fn validate_master_edition_allocation(
data: &[u8],
serialized_len: usize,
padded_account_lengths: &[usize],
) -> std::result::Result<(), DcMetadataMtmMetadataAccountDecodeError> {
if data.len() == serialized_len {
return std::result::Result::Ok(());
}
if data.len() < serialized_len || !padded_account_lengths.contains(&data.len()) {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidDataLength,
);
}
let trailer_start = match data.len().checked_sub(2) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidDataLength,
);
},
};
if trailer_start < serialized_len {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidDataLength,
);
}
if data[serialized_len..trailer_start].iter().any(|byte| return *byte != 0) {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout,
);
}
let fee_flag = data[trailer_start];
if fee_flag > 1 {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout,
);
}
let token_standard = data[trailer_start + 1];
if token_standard != mpl_token_metadata::types::TokenStandard::NonFungible as u8
&& token_standard != mpl_token_metadata::types::TokenStandard::ProgrammableNonFungible as u8
{
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout,
);
}
return std::result::Result::Ok(());
}
/// Supported edition account layout.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DcMetadataMtmEditionAccountKind {
@@ -396,7 +469,7 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_edition_account(
if owner_key != expected_owner {
return std::result::Result::Err(DcMetadataMtmMetadataAccountDecodeError::ForeignOwner);
}
if data.is_empty() || data.len() > 128 {
if data.is_empty() || data.len() > LEGACY_MASTER_EDITION_ACCOUNT_BYTES {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidDataLength,
);
@@ -427,6 +500,14 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_edition_account(
);
},
};
let serialized_len = if value.max_supply.is_some() { 82 } else { 74 };
if let std::result::Result::Err(error) = validate_master_edition_allocation(
data,
serialized_len,
&[LEGACY_MASTER_EDITION_ACCOUNT_BYTES],
) {
return std::result::Result::Err(error);
}
let payload_json = match serde_json::to_value(&value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
@@ -460,6 +541,14 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_edition_account(
);
},
};
let serialized_len = if value.max_supply.is_some() { 18 } else { 10 };
if let std::result::Result::Err(error) = validate_master_edition_allocation(
data,
serialized_len,
&[COMPACT_MASTER_EDITION_ACCOUNT_BYTES, LEGACY_MASTER_EDITION_ACCOUNT_BYTES],
) {
return std::result::Result::Err(error);
}
let payload_json = match serde_json::to_value(&value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
@@ -483,11 +572,6 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_edition_account(
});
}
if key == mpl_token_metadata::types::Key::EditionV1 as u8 {
if data.len() != mpl_token_metadata::accounts::Edition::LEN {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::InvalidDataLength,
);
}
let value = match mpl_token_metadata::accounts::Edition::from_bytes(data) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
@@ -496,6 +580,13 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_edition_account(
);
},
};
if let std::result::Result::Err(error) = validate_known_account_allocation(
data,
mpl_token_metadata::accounts::Edition::LEN,
&[COMPACT_EDITION_ACCOUNT_BYTES, LEGACY_EDITION_ACCOUNT_BYTES],
) {
return std::result::Result::Err(error);
}
let payload_json = match serde_json::to_value(&value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
@@ -851,17 +942,21 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_token_record_account(
DcMetadataMtmMetadataAccountDecodeError::InvalidTokenRecordPda,
);
}
let payload_json = match serde_json::to_value(&value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(
DcMetadataMtmMetadataAccountDecodeError::ProjectionFailed,
);
},
};
let state = format!("{:?}", value.state);
let delegate = value.delegate.map(|delegate| return delegate.to_string());
let delegate_role = value.delegate_role.as_ref().map(|role| return format!("{role:?}"));
let locked_transfer =
value.locked_transfer.map(|locked_transfer| return locked_transfer.to_string());
let locked = value.state == mpl_token_metadata::types::TokenState::Locked;
let payload_json = serde_json::json!({
"key": "TokenRecord",
"bump": value.bump,
"state": &state,
"rule_set_revision": value.rule_set_revision,
"delegate": &delegate,
"delegate_role": &delegate_role,
"locked_transfer": &locked_transfer,
});
return std::result::Result::Ok(DcMetadataMtmTokenRecordAccountSnapshot {
account: account_key.to_string(),
bump,
@@ -870,11 +965,9 @@ pub fn decoder_metadata_metaplex_token_metadata_decode_token_record_account(
stored_bump: value.bump,
state,
rule_set_revision: value.rule_set_revision,
delegate: value.delegate.map(|delegate| return delegate.to_string()),
delegate,
delegate_role,
locked_transfer: value
.locked_transfer
.map(|locked_transfer| return locked_transfer.to_string()),
locked_transfer,
locked,
payload_json,
});
@@ -1975,6 +2068,179 @@ mod tests {
assert_eq!(edition_snapshot.edition, std::option::Option::Some(u64::MAX));
}
#[test]
fn edition_and_master_edition_accept_only_known_allocations() {
let mint = mpl_token_metadata::ID;
let (account, _) = mpl_token_metadata::accounts::MasterEdition::find_pda(&mint);
let master = mpl_token_metadata::accounts::MasterEdition {
key: mpl_token_metadata::types::Key::MasterEditionV2,
supply: 1,
max_supply: std::option::Option::Some(1),
};
let master_data = match borsh_0_10::to_vec(&master) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("master edition serialization failed: {error}")
},
};
for allocated_len in [
super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES,
super::LEGACY_MASTER_EDITION_ACCOUNT_BYTES,
] {
let mut padded = master_data.clone();
padded.resize(allocated_len, 0);
assert!(
crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
padded.as_slice(),
)
.is_ok()
);
}
let edition = mpl_token_metadata::accounts::Edition {
key: mpl_token_metadata::types::Key::EditionV1,
parent: account,
edition: 1,
};
let edition_data = match borsh_0_10::to_vec(&edition) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("edition serialization failed: {error}"),
};
for allocated_len in
[super::COMPACT_EDITION_ACCOUNT_BYTES, super::LEGACY_EDITION_ACCOUNT_BYTES]
{
let mut padded = edition_data.clone();
padded.resize(allocated_len, 0);
assert!(
crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
padded.as_slice(),
)
.is_ok()
);
}
let mut unexpected_len = edition_data.clone();
unexpected_len.resize(super::COMPACT_EDITION_ACCOUNT_BYTES + 1, 0);
assert_eq!(
crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
unexpected_len.as_slice(),
),
std::result::Result::Err(
crate::DcMetadataMtmMetadataAccountDecodeError::InvalidDataLength
),
);
let mut non_zero_padding = edition_data;
non_zero_padding.resize(super::COMPACT_EDITION_ACCOUNT_BYTES, 1);
assert_eq!(
crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
non_zero_padding.as_slice(),
),
std::result::Result::Err(
crate::DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout
),
);
}
#[test]
fn compact_master_edition_trailer_accepts_pnft_and_rejects_invalid_flags() {
let mint = mpl_token_metadata::ID;
let (account, _) = mpl_token_metadata::accounts::MasterEdition::find_pda(&mint);
let master = mpl_token_metadata::accounts::MasterEdition {
key: mpl_token_metadata::types::Key::MasterEditionV2,
supply: 0,
max_supply: std::option::Option::Some(0),
};
let serialized = match borsh_0_10::to_vec(&master) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("master edition serialization failed: {error}")
},
};
let mut pnft = serialized.clone();
pnft.resize(super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES, 0);
pnft[super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES - 2] = 1;
pnft[super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES - 1] =
mpl_token_metadata::types::TokenStandard::ProgrammableNonFungible as u8;
let snapshot = match crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
pnft.as_slice(),
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("compact pNFT master edition decode failed: {error}")
},
};
assert_eq!(snapshot.kind, crate::DcMetadataMtmEditionAccountKind::MasterEditionV2);
assert_eq!(snapshot.supply, std::option::Option::Some(0));
assert_eq!(snapshot.max_supply, std::option::Option::Some(0));
let mut invalid_fee_flag = pnft.clone();
invalid_fee_flag[super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES - 2] = 2;
assert_eq!(
crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
invalid_fee_flag.as_slice(),
),
std::result::Result::Err(
crate::DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout
),
);
let mut invalid_token_standard = pnft;
invalid_token_standard[super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES - 1] =
mpl_token_metadata::types::TokenStandard::Fungible as u8;
assert_eq!(
crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
invalid_token_standard.as_slice(),
),
std::result::Result::Err(
crate::DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout
),
);
let master_without_limit = mpl_token_metadata::accounts::MasterEdition {
key: mpl_token_metadata::types::Key::MasterEditionV2,
supply: 0,
max_supply: std::option::Option::None,
};
let mut invalid_reserved = match borsh_0_10::to_vec(&master_without_limit) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("master edition serialization failed: {error}")
},
};
let serialized_len = invalid_reserved.len();
invalid_reserved.resize(super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES, 0);
invalid_reserved[serialized_len] = 1;
invalid_reserved[super::COMPACT_MASTER_EDITION_ACCOUNT_BYTES - 1] =
mpl_token_metadata::types::TokenStandard::ProgrammableNonFungible as u8;
assert_eq!(
crate::decoder_metadata_metaplex_token_metadata_decode_edition_account(
&account.to_string(),
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
&mint.to_string(),
invalid_reserved.as_slice(),
),
std::result::Result::Err(
crate::DcMetadataMtmMetadataAccountDecodeError::InvalidMetadataLayout
),
);
}
#[test]
fn historical_master_edition_v1_and_invalid_boundaries_are_explicit() {
let mint = mpl_token_metadata::ID;
@@ -2218,6 +2484,15 @@ mod tests {
snapshot.locked_transfer,
std::option::Option::Some(mpl_token_metadata::ID.to_string())
);
assert_eq!(
snapshot.payload_json["delegate"],
serde_json::json!(mpl_token_metadata::ID.to_string())
);
assert_eq!(snapshot.payload_json["delegate_role"], serde_json::json!("Transfer"));
assert_eq!(
snapshot.payload_json["locked_transfer"],
serde_json::json!(mpl_token_metadata::ID.to_string())
);
}
#[test]

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/decoder.rs
// version: 32
// version: 39
//! Exact contextual dispatch for Metaplex Token Metadata.
@@ -633,6 +633,69 @@ mod tests {
);
}
#[test]
fn modern_create_accepts_preexisting_mint_and_transaction_privilege_supersets() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
let result = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
modern_create_wire().as_slice(),
&[
(false, true),
(false, true),
(false, true),
(true, true),
(true, true),
(true, true),
(false, false),
(false, false),
(false, false),
],
false,
"modern_create_preexisting_mint",
),
);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Decoded);
assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some("create"));
}
#[test]
fn modern_mint_accepts_authority_owner_payer_privilege_supersets() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
let args = mpl_token_metadata::types::MintArgs::V1 {
amount: 1,
authorization_data: std::option::Option::None,
};
let wire = programmable_wire(crate::DC_METADATA_MTM_MINT_DISCRIMINATOR, &args);
let result = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
wire.as_slice(),
&[
(false, true),
(true, true),
(false, false),
(false, true),
(false, true),
(false, true),
(true, true),
(false, false),
(true, true),
(false, false),
(false, false),
(false, false),
(false, false),
(false, false),
(false, false),
],
false,
"modern_mint_privilege_superset",
),
);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Decoded);
assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some("mint"));
}
#[test]
fn modern_create_rejects_truncated_invalid_and_suffix_wires() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
@@ -661,12 +724,12 @@ mod tests {
&input_with_flags(
modern_create_wire().as_slice(),
&[
(false, true),
(false, false),
(false, true),
(false, true),
(true, false),
(true, true),
(true, false),
(false, false),
(false, false),
(false, false),
(false, false),
@@ -852,6 +915,53 @@ mod tests {
assert!(!v2.observations[0].observation_committed);
}
#[test]
fn modern_print_accepts_transaction_privilege_supersets() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
let flags = [
(false, true),
(false, true),
(true, true),
(true, true),
(false, true),
(true, true),
(false, true),
(false, true),
(false, true),
(true, true),
(true, true),
(false, false),
(false, false),
(true, true),
(false, false),
(false, false),
(false, false),
(false, false),
];
let decoded = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
modern_print_wire_v1(1).as_slice(),
&flags,
false,
"modern_print_privilege_superset",
),
);
assert_eq!(decoded.status, crate::DcApiDecoderOutcomeStatus::Decoded);
let mut missing_mint_signature = flags;
missing_mint_signature[2] = (false, true);
let rejected = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
modern_print_wire_v1(1).as_slice(),
&missing_mint_signature,
false,
"modern_print_missing_mint_signature",
),
);
assert_eq!(rejected.status, crate::DcApiDecoderOutcomeStatus::Failed);
}
#[test]
fn modern_print_rejects_truncated_invalid_and_suffix_wires() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
@@ -915,10 +1025,10 @@ mod tests {
}
#[test]
fn modern_update_variants_decode_exact_contract() {
fn modern_update_variants_accept_transaction_privilege_supersets() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
let flags = [
(true, false),
(true, true),
(false, false),
(false, false),
(false, false),
@@ -1012,13 +1122,13 @@ mod tests {
fn modern_use_verify_and_unverify_decode_exact_contracts() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
let use_flags = [
(true, false),
(true, true),
(false, true),
(false, true),
(false, false),
(false, true),
(false, true),
(true, false),
(true, true),
(false, false),
(false, false),
(false, false),
@@ -1081,6 +1191,74 @@ mod tests {
assert!(!unverify.observations[0].observation_committed);
}
#[test]
fn modern_verify_and_unverify_accept_fee_payer_privilege_supersets() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
let verify_flags = [
(true, true),
(false, false),
(false, true),
(false, false),
(false, true),
(false, false),
(false, false),
(false, false),
];
let verify = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
modern_verification_wire(
crate::DC_METADATA_MTM_VERIFY_DISCRIMINATOR,
mpl_token_metadata::types::VerificationArgs::CollectionV1,
)
.as_slice(),
&verify_flags,
false,
"modern_verify_fee_payer",
),
);
assert_eq!(verify.status, crate::DcApiDecoderOutcomeStatus::Decoded);
let unverify_flags = [
(true, true),
(false, false),
(false, true),
(false, false),
(false, true),
(false, false),
(false, false),
];
let unverify = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
modern_verification_wire(
crate::DC_METADATA_MTM_UNVERIFY_DISCRIMINATOR,
mpl_token_metadata::types::VerificationArgs::CollectionV1,
)
.as_slice(),
&unverify_flags,
false,
"modern_unverify_fee_payer",
),
);
assert_eq!(unverify.status, crate::DcApiDecoderOutcomeStatus::Decoded);
let mut missing_authority_signature = verify_flags;
missing_authority_signature[0] = (false, true);
let invalid = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
modern_verification_wire(
crate::DC_METADATA_MTM_VERIFY_DISCRIMINATOR,
mpl_token_metadata::types::VerificationArgs::CollectionV1,
)
.as_slice(),
&missing_authority_signature,
false,
"modern_verify_missing_authority_signature",
),
);
assert_eq!(invalid.status, crate::DcApiDecoderOutcomeStatus::Failed);
}
#[test]
fn modern_use_verify_and_unverify_reject_invalid_wires() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
@@ -1120,6 +1298,18 @@ mod tests {
),
);
assert_eq!(invalid.status, crate::DcApiDecoderOutcomeStatus::Failed);
let mut readonly_token = use_flags;
readonly_token[2] = (false, false);
let invalid = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
modern_use_wire().as_slice(),
&readonly_token,
false,
"modern_use_readonly_token",
),
);
assert_eq!(invalid.status, crate::DcApiDecoderOutcomeStatus::Failed);
let verify_flags = [
(true, false),
(false, false),
@@ -2261,6 +2451,93 @@ mod tests {
assert_eq!(revoke.observations[0].payload_json["parameters"], "StandardV1");
}
#[test]
fn programmable_delegate_revoke_lock_and_unlock_accept_transaction_privilege_supersets() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;
let delegate_flags = &[
(false, true),
(false, false),
(false, true),
(false, false),
(false, true),
(false, false),
(false, true),
(true, true),
(true, true),
(false, false),
(false, false),
(false, false),
(false, false),
(false, false),
];
for (wire, path) in [
(
delegate_wire(&mpl_token_metadata::types::DelegateArgs::StakingV1 {
amount: 1,
authorization_data: std::option::Option::None,
}),
"delegate_privilege_superset",
),
(
revoke_wire(&mpl_token_metadata::types::RevokeArgs::StakingV1),
"revoke_privilege_superset",
),
] {
let result = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(wire.as_slice(), delegate_flags, false, path),
);
assert_eq!(result.status, crate::DcApiDecoderOutcomeStatus::Decoded);
}
let lock_flags = &[
(true, false),
(true, true),
(false, true),
(false, false),
(false, true),
(false, false),
(false, true),
(true, true),
(false, false),
(false, false),
(false, false),
(false, false),
(false, false),
];
let lock = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
programmable_wire(
crate::DC_METADATA_MTM_LOCK_DISCRIMINATOR,
&mpl_token_metadata::types::LockArgs::V1 {
authorization_data: std::option::Option::None,
},
)
.as_slice(),
lock_flags,
false,
"lock_privilege_superset",
),
);
assert_eq!(lock.status, crate::DcApiDecoderOutcomeStatus::Decoded);
let unlock = crate::DcApiInstructionDecoder::decode(
&decoder,
&input_with_flags(
programmable_wire(
crate::DC_METADATA_MTM_UNLOCK_DISCRIMINATOR,
&mpl_token_metadata::types::UnlockArgs::V1 {
authorization_data: std::option::Option::None,
},
)
.as_slice(),
lock_flags,
false,
"unlock_privilege_superset",
),
);
assert_eq!(unlock.status, crate::DcApiDecoderOutcomeStatus::Decoded);
}
#[test]
fn programmable_delegate_and_revoke_reject_truncated_invalid_and_suffix_wires() {
let decoder = crate::DcMetadataMetaplexTokenMetadataDecoder;

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/decoder/metadata/metaplex_token_metadata/instruction.rs
// version: 28
// version: 34
//! Bounded Metaplex Token Metadata instruction wire decoding.
@@ -1159,26 +1159,18 @@ fn validate_accounts(
accounts.len()
));
}
let mandatory: &[(usize, bool, bool)] = &[
(0, false, true),
(2, true, true),
(3, true, false),
(4, true, true),
(5, true, false),
(6, false, false),
(7, false, false),
(8, false, false),
];
for (index, signer, writable) in mandatory {
let required_privileges: &[(usize, bool, bool)] =
&[(0, false, true), (2, false, true), (3, true, false), (4, true, true)];
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!("create is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"create account {index} has invalid signer/writable flags"
"create account {index} lacks required signer/writable privileges"
));
}
}
@@ -1190,9 +1182,12 @@ fn validate_accounts(
);
},
};
if master_edition.signer {
if master_edition.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !master_edition.writable
{
return std::result::Result::Err(
"create master edition account must not sign".to_string(),
"create master edition account lacks required writable privilege".to_string(),
);
}
return std::result::Result::Ok(());
@@ -1204,31 +1199,22 @@ fn validate_accounts(
accounts.len()
));
}
let mandatory: &[(usize, bool, bool)] = &[
(0, false, true),
(2, false, false),
(5, false, true),
(6, true, false),
(8, true, true),
(9, false, false),
(10, false, false),
(11, false, false),
(12, false, false),
];
for (index, signer, writable) in mandatory {
let required_privileges: &[(usize, bool, bool)] =
&[(0, false, true), (5, false, true), (6, true, false), (8, true, true)];
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!("mint is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"mint account {index} has invalid signer/writable flags"
"mint account {index} lacks required signer/writable privileges"
));
}
}
for index in [1_usize, 3_usize, 4_usize, 7_usize, 13_usize, 14_usize] {
for index in [3_usize, 4_usize] {
let account = match accounts.get(index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
@@ -1237,9 +1223,12 @@ fn validate_accounts(
));
},
};
if account.signer {
if account.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !account.writable
{
return std::result::Result::Err(format!(
"mint optional account {index} must not sign"
"mint optional account {index} lacks required writable privilege"
));
}
}
@@ -1252,35 +1241,27 @@ fn validate_accounts(
accounts.len()
));
}
let mandatory: &[(usize, bool, bool)] = &[
let required_privileges: &[(usize, bool, bool)] = &[
(0, false, true),
(1, false, true),
(2, true, true),
(3, false, false),
(4, false, true),
(5, true, false),
(7, false, true),
(8, false, true),
(9, true, true),
(10, true, false),
(11, false, false),
(12, false, false),
(13, false, false),
(14, false, false),
(15, false, false),
(16, false, false),
(17, false, false),
];
for (index, signer, writable) in mandatory {
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!("print is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"print account {index} has invalid signer/writable flags"
"print account {index} lacks required signer/writable privileges"
));
}
}
@@ -1297,6 +1278,14 @@ fn validate_accounts(
"print edition token record must not sign".to_string(),
);
}
if edition_token_record.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !edition_token_record.writable
{
return std::result::Result::Err(
"print edition token record lacks required writable privilege".to_string(),
);
}
return std::result::Result::Ok(());
}
if code == "update" {
@@ -1321,9 +1310,9 @@ fn validate_accounts(
return std::result::Result::Err(format!("update is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"update account {index} has invalid signer/writable flags"
"update account {index} lacks required signer/writable privileges"
));
}
}
@@ -1503,42 +1492,36 @@ fn validate_accounts(
accounts.len()
));
}
let mandatory: &[(usize, bool, bool)] = &[
(0, true, false),
(2, false, true),
(3, false, false),
(4, false, true),
(7, true, true),
(8, false, false),
(9, false, false),
];
for (index, signer, writable) in mandatory {
let required_privileges: &[(usize, bool, bool)] =
&[(0, true, false), (2, false, true), (4, false, true), (7, true, true)];
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!("{code} is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"{code} account {index} has invalid signer/writable flags"
"{code} account {index} lacks required signer/writable privileges"
));
}
}
for index in [1_usize, 5_usize, 6_usize, 10_usize, 11_usize, 12_usize] {
let account = match accounts.get(index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!(
"{code} is missing optional placeholder account {index}"
));
},
};
if account.signer {
let token_record = match accounts.get(6) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!(
"{code} optional account {index} must not sign"
"{code} is missing token record placeholder"
));
}
},
};
if token_record.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !token_record.writable
{
return std::result::Result::Err(format!(
"{code} token record lacks required writable privilege"
));
}
return std::result::Result::Ok(());
}
@@ -1549,27 +1532,18 @@ fn validate_accounts(
accounts.len()
));
}
let mandatory: &[(usize, bool, bool)] = &[
(0, true, false),
(3, false, false),
(4, false, true),
(6, true, false),
(7, false, false),
(8, false, false),
(9, false, false),
(10, false, false),
(11, false, false),
];
for (index, signer, writable) in mandatory {
let required_privileges: &[(usize, bool, bool)] =
&[(0, true, false), (4, false, true), (6, true, false)];
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!("use is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"use account {index} has invalid signer/writable flags"
"use account {index} lacks required signer/writable privileges"
));
}
}
@@ -1587,46 +1561,95 @@ fn validate_accounts(
"use optional account {index} must not sign"
));
}
if account.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !account.writable
{
return std::result::Result::Err(format!(
"use optional account {index} lacks required writable privilege"
));
}
}
return std::result::Result::Ok(());
}
if code == "verify" || code == "unverify" {
if code == "verify" {
if accounts.len() < 8 {
return std::result::Result::Err(format!(
"{code} requires 8 positional accounts, received {}",
"verify requires 8 positional accounts, received {}",
accounts.len()
));
}
let mandatory: &[(usize, bool, bool)] =
&[(0, true, false), (2, false, true), (6, false, false), (7, false, false)];
for (index, signer, writable) in mandatory {
let required_privileges: &[(usize, bool, bool)] = &[(0, true, false), (2, false, true)];
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!("{code} is missing account {index}"));
return std::result::Result::Err(format!("verify is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"{code} account {index} has invalid signer/writable flags"
"verify account {index} lacks required signer/writable privileges"
));
}
}
for index in [1_usize, 3_usize, 4_usize, 5_usize] {
let account = match accounts.get(index) {
let collection_metadata = match accounts.get(4) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
"verify is missing collection metadata placeholder".to_string(),
);
},
};
if collection_metadata.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !collection_metadata.writable
{
return std::result::Result::Err(
"verify collection metadata lacks required writable privilege".to_string(),
);
}
return std::result::Result::Ok(());
}
if code == "unverify" {
if accounts.len() < 7 {
return std::result::Result::Err(format!(
"unverify requires 7 positional accounts, received {}",
accounts.len()
));
}
let required_privileges: &[(usize, bool, bool)] = &[(0, true, false), (2, false, true)];
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!(
"{code} is missing optional placeholder account {index}"
"unverify is missing account {index}"
));
},
};
if account.signer {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"{code} optional account {index} must not sign"
"unverify account {index} lacks required signer/writable privileges"
));
}
}
let collection_metadata = match accounts.get(4) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(
"unverify is missing collection metadata placeholder".to_string(),
);
},
};
if collection_metadata.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !collection_metadata.writable
{
return std::result::Result::Err(
"unverify collection metadata lacks required writable privilege".to_string(),
);
}
return std::result::Result::Ok(());
}
if code == "burn" {
@@ -1682,29 +1705,18 @@ fn validate_accounts(
accounts.len()
));
}
let mandatory: &[(usize, bool, bool)] = &[
(1, false, false),
(2, false, true),
(3, false, false),
(5, false, false),
(7, true, false),
(8, true, true),
(9, false, false),
(10, false, false),
(11, false, false),
(12, false, false),
(13, false, false),
];
for (index, signer, writable) in mandatory {
let required_privileges: &[(usize, bool, bool)] =
&[(2, false, true), (7, true, false), (8, true, true)];
for (index, signer, writable) in required_privileges {
let account = match accounts.get(*index) {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(format!("{code} is missing account {index}"));
},
};
if account.signer != *signer || account.writable != *writable {
if (*signer && !account.signer) || (*writable && !account.writable) {
return std::result::Result::Err(format!(
"{code} account {index} has invalid signer/writable flags"
"{code} account {index} lacks required signer/writable privileges"
));
}
}
@@ -1717,9 +1729,12 @@ fn validate_accounts(
));
},
};
if account.signer {
if account.account_key.as_str()
!= kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID
&& !account.writable
{
return std::result::Result::Err(format!(
"{code} optional account {index} must not sign"
"{code} optional account {index} lacks required writable privilege"
));
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/metadata/metaplex_token_metadata/builder.rs
// version: 8
// version: 11
//! Official Metaplex builders and conservative plan validation.
@@ -181,15 +181,15 @@ pub(crate) fn executor_metaplex_token_metadata_build_prepared_plan(
crate::ExMetaplexTokenMetadataOperation::Utilize { metadata, token_account, mint, use_authority, owner, token_program, ata_program, system_program, rent, use_authority_record, burner, args } => build_idl_instruction_with_args(19, &[(metadata, false, true), (token_account, false, true), (mint, false, true), (use_authority, true, true), (owner, false, false), (token_program, false, false), (ata_program, false, false), (system_program, false, false), (rent, false, false), (use_authority_record, false, true), (burner, false, false)], args),
crate::ExMetaplexTokenMetadataOperation::ApproveUseAuthority { use_authority_record, owner, payer, user, owner_token_account, metadata, mint, burner, token_program, system_program, rent, args } => build_idl_instruction_with_args(20, &[(use_authority_record, false, true), (owner, true, true), (payer, true, true), (user, false, false), (owner_token_account, false, true), (metadata, false, false), (mint, false, false), (burner, false, false), (token_program, false, false), (system_program, false, false), (rent, false, false)], args),
crate::ExMetaplexTokenMetadataOperation::RevokeUseAuthority { use_authority_record, owner, user, owner_token_account, mint, metadata, token_program, system_program, rent } => build_idl_instruction_no_args(21, &[(use_authority_record, false, true), (owner, true, true), (user, false, false), (owner_token_account, false, true), (mint, false, false), (metadata, false, false), (token_program, false, false), (system_program, false, false), (rent, false, false)]),
crate::ExMetaplexTokenMetadataOperation::CreateEscrowAccount { escrow, metadata, mint, token_account, edition, payer, system_program, sysvar_instructions, authority } => build_idl_instruction_no_args(38, &[(escrow, false, true), (metadata, false, true), (mint, false, false), (token_account, false, false), (edition, false, false), (payer, true, true), (system_program, false, false), (sysvar_instructions, false, false), (authority, true, false)]),
crate::ExMetaplexTokenMetadataOperation::CreateEscrowAccount { escrow, metadata, mint, token_account, edition, payer, system_program, sysvar_instructions, authority } => build_idl_instruction_trailing_optional_no_args(38, &[(escrow, false, true), (metadata, false, true), (mint, false, false), (token_account, false, false), (edition, false, false), (payer, true, true), (system_program, false, false), (sysvar_instructions, false, false)], authority.as_ref().map(|value| return (value, true, false))),
crate::ExMetaplexTokenMetadataOperation::CloseEscrowAccount { escrow, metadata, mint, token_account, edition, payer, system_program, sysvar_instructions } => build_idl_instruction_no_args(39, &[(escrow, false, true), (metadata, false, true), (mint, false, false), (token_account, false, false), (edition, false, false), (payer, true, true), (system_program, false, false), (sysvar_instructions, false, false)]),
crate::ExMetaplexTokenMetadataOperation::TransferOutOfEscrow { escrow, metadata, payer, attribute_mint, attribute_src, attribute_dst, escrow_mint, escrow_account, system_program, ata_program, token_program, sysvar_instructions, authority, args } => build_idl_instruction_with_args(40, &[(escrow, false, false), (metadata, false, true), (payer, true, true), (attribute_mint, false, false), (attribute_src, false, true), (attribute_dst, false, true), (escrow_mint, false, false), (escrow_account, false, false), (system_program, false, false), (ata_program, false, false), (token_program, false, false), (sysvar_instructions, false, false), (authority, true, false)], args),
crate::ExMetaplexTokenMetadataOperation::Mint { token, token_owner, metadata, master_edition, token_record, mint, authority, delegate_record, payer, system_program, sysvar_instructions, spl_token_program, spl_ata_program, authorization_rules_program, authorization_rules, args } => build_idl_instruction_with_args(43, &[(token, false, true), (token_owner, false, false), (metadata, false, false), (master_edition, false, true), (token_record, false, true), (mint, false, true), (authority, true, false), (delegate_record, false, false), (payer, true, true), (system_program, false, false), (sysvar_instructions, false, false), (spl_token_program, false, false), (spl_ata_program, false, false), (authorization_rules_program, false, false), (authorization_rules, false, false)], args),
crate::ExMetaplexTokenMetadataOperation::Migrate { metadata, edition, token, token_owner, mint, payer, authority, collection_metadata, delegate_record, token_record, system_program, sysvar_instructions, spl_token_program, authorization_rules_program, authorization_rules } => build_idl_instruction_no_args(48, &[(metadata, false, true), (edition, false, true), (token, false, true), (token_owner, false, false), (mint, false, false), (payer, true, true), (authority, true, false), (collection_metadata, false, false), (delegate_record, false, false), (token_record, false, true), (system_program, false, false), (sysvar_instructions, false, false), (spl_token_program, false, false), (authorization_rules_program, false, false), (authorization_rules, false, false)]),
crate::ExMetaplexTokenMetadataOperation::Use { authority, delegate_record, token, mint, metadata, edition, payer, system_program, sysvar_instructions, spl_token_program, authorization_rules_program, authorization_rules, args } => build_idl_instruction_with_args(51, &[(authority, true, false), (delegate_record, false, true), (token, false, true), (mint, false, false), (metadata, false, true), (edition, false, true), (payer, true, false), (system_program, false, false), (sysvar_instructions, false, false), (spl_token_program, false, false), (authorization_rules_program, false, false), (authorization_rules, false, false)], args),
crate::ExMetaplexTokenMetadataOperation::TransferOutOfEscrow { escrow, metadata, payer, attribute_mint, attribute_src, attribute_dst, escrow_mint, escrow_account, system_program, ata_program, token_program, sysvar_instructions, authority, args } => build_idl_instruction_trailing_optional_with_args(40, &[(escrow, false, false), (metadata, false, true), (payer, true, true), (attribute_mint, false, false), (attribute_src, false, true), (attribute_dst, false, true), (escrow_mint, false, false), (escrow_account, false, false), (system_program, false, false), (ata_program, false, false), (token_program, false, false), (sysvar_instructions, false, false)], authority.as_ref().map(|value| return (value, true, false)), args),
crate::ExMetaplexTokenMetadataOperation::Mint { token, token_owner, metadata, master_edition, token_record, mint, authority, delegate_record, payer, system_program, sysvar_instructions, spl_token_program, spl_ata_program, authorization_rules_program, authorization_rules, args } => build_idl_instruction_optional_with_args(43, &[(std::option::Option::Some(token), false, true), (token_owner.as_ref(), false, false), (std::option::Option::Some(metadata), false, false), (master_edition.as_ref(), false, true), (token_record.as_ref(), false, true), (std::option::Option::Some(mint), false, true), (std::option::Option::Some(authority), true, false), (delegate_record.as_ref(), false, false), (std::option::Option::Some(payer), true, true), (std::option::Option::Some(system_program), false, false), (std::option::Option::Some(sysvar_instructions), false, false), (std::option::Option::Some(spl_token_program), false, false), (std::option::Option::Some(spl_ata_program), false, false), (authorization_rules_program.as_ref(), false, false), (authorization_rules.as_ref(), false, false)], args),
crate::ExMetaplexTokenMetadataOperation::Migrate { metadata, edition, token, token_owner, mint, payer, authority, collection_metadata, delegate_record, token_record, system_program, sysvar_instructions, spl_token_program, authorization_rules_program, authorization_rules } => build_idl_instruction_optional_no_args(48, &[(std::option::Option::Some(metadata), false, true), (std::option::Option::Some(edition), false, true), (std::option::Option::Some(token), false, true), (std::option::Option::Some(token_owner), false, false), (std::option::Option::Some(mint), false, false), (std::option::Option::Some(payer), true, true), (std::option::Option::Some(authority), true, false), (std::option::Option::Some(collection_metadata), false, false), (std::option::Option::Some(delegate_record), false, false), (std::option::Option::Some(token_record), false, true), (std::option::Option::Some(system_program), false, false), (std::option::Option::Some(sysvar_instructions), false, false), (std::option::Option::Some(spl_token_program), false, false), (authorization_rules_program.as_ref(), false, false), (authorization_rules.as_ref(), false, false)]),
crate::ExMetaplexTokenMetadataOperation::Use { authority, delegate_record, token, mint, metadata, edition, payer, system_program, sysvar_instructions, spl_token_program, authorization_rules_program, authorization_rules, args } => build_idl_instruction_optional_with_args(51, &[(std::option::Option::Some(authority), true, false), (delegate_record.as_ref(), false, true), (token.as_ref(), false, true), (std::option::Option::Some(mint), false, false), (std::option::Option::Some(metadata), false, true), (edition.as_ref(), false, true), (std::option::Option::Some(payer), true, false), (std::option::Option::Some(system_program), false, false), (std::option::Option::Some(sysvar_instructions), false, false), (spl_token_program.as_ref(), false, false), (authorization_rules_program.as_ref(), false, false), (authorization_rules.as_ref(), false, false)], args),
crate::ExMetaplexTokenMetadataOperation::Collect { authority, recipient } => build_idl_instruction_no_args(54, &[(authority, true, false), (recipient, false, false)]),
crate::ExMetaplexTokenMetadataOperation::Print { edition_metadata, edition, edition_mint, edition_token_account_owner, edition_token_account, edition_mint_authority, edition_token_record, master_edition, edition_marker_pda, payer, master_token_account_owner, master_token_account, master_metadata, update_authority, spl_token_program, spl_ata_program, sysvar_instructions, system_program, args } => build_idl_instruction_with_args(55, &[(edition_metadata, false, true), (edition, false, true), (edition_mint, false, true), (edition_token_account_owner, false, false), (edition_token_account, false, true), (edition_mint_authority, true, false), (edition_token_record, false, true), (master_edition, false, true), (edition_marker_pda, false, true), (payer, true, true), (master_token_account_owner, false, false), (master_token_account, false, false), (master_metadata, false, false), (update_authority, false, false), (spl_token_program, false, false), (spl_ata_program, false, false), (sysvar_instructions, false, false), (system_program, false, false)], args),
crate::ExMetaplexTokenMetadataOperation::Resize { metadata, edition, mint, payer, authority, token, system_program } => build_idl_instruction_no_args(56, &[(metadata, false, true), (edition, false, true), (mint, false, false), (payer, false, true), (authority, true, false), (token, false, false), (system_program, false, false)]),
crate::ExMetaplexTokenMetadataOperation::Print { edition_metadata, edition, edition_mint, edition_token_account_owner, edition_token_account, edition_mint_authority, edition_token_record, master_edition, edition_marker_pda, payer, master_token_account_owner, master_token_account, master_metadata, update_authority, spl_token_program, spl_ata_program, sysvar_instructions, system_program, args } => build_print(edition_metadata, edition, edition_mint, edition_token_account_owner, edition_token_account, edition_mint_authority, edition_token_record.as_ref(), master_edition, edition_marker_pda, payer, master_token_account_owner, master_token_account, master_metadata, update_authority, spl_token_program, spl_ata_program, sysvar_instructions, system_program, args),
crate::ExMetaplexTokenMetadataOperation::Resize { metadata, edition, mint, payer, authority, token, system_program } => build_resize(metadata, edition, mint, payer, authority.as_ref(), token.as_ref(), system_program),
};
let instruction = match instruction_result {
std::result::Result::Ok(value) => value,
@@ -1207,6 +1207,192 @@ fn build_close_accounts(
return std::result::Result::Ok(convert_official_instruction!(official));
}
#[allow(clippy::too_many_arguments)]
fn build_print(
edition_metadata: &crate::MdPubkey,
edition: &crate::MdPubkey,
edition_mint: &crate::MdPubkey,
edition_token_account_owner: &crate::MdPubkey,
edition_token_account: &crate::MdPubkey,
edition_mint_authority: &crate::MdPubkey,
edition_token_record: std::option::Option<&crate::MdPubkey>,
master_edition: &crate::MdPubkey,
edition_marker_pda: &crate::MdPubkey,
payer: &crate::MdPubkey,
master_token_account_owner: &crate::MdPubkey,
master_token_account: &crate::MdPubkey,
master_metadata: &crate::MdPubkey,
update_authority: &crate::MdPubkey,
spl_token_program: &crate::MdPubkey,
spl_ata_program: &crate::MdPubkey,
sysvar_instructions: &crate::MdPubkey,
system_program: &crate::MdPubkey,
args: &mpl_token_metadata::types::PrintArgs,
) -> kb_core::Result<solana_instruction::Instruction> {
let mut builder = mpl_token_metadata::instructions::PrintBuilder::new();
builder
.edition_metadata(parse_official_pubkey!(&edition_metadata.0, "edition_metadata"))
.edition(parse_official_pubkey!(&edition.0, "edition"))
.edition_mint(parse_official_pubkey!(&edition_mint.0, "edition_mint"), true)
.edition_token_account_owner(parse_official_pubkey!(
&edition_token_account_owner.0,
"edition_token_account_owner"
))
.edition_token_account(parse_official_pubkey!(
&edition_token_account.0,
"edition_token_account"
))
.edition_mint_authority(parse_official_pubkey!(
&edition_mint_authority.0,
"edition_mint_authority"
))
.edition_token_record(parse_optional_official_pubkey!(
edition_token_record,
"edition_token_record"
))
.master_edition(parse_official_pubkey!(&master_edition.0, "master_edition"))
.edition_marker_pda(parse_official_pubkey!(&edition_marker_pda.0, "edition_marker_pda"))
.payer(parse_official_pubkey!(&payer.0, "payer"))
.master_token_account_owner(
parse_official_pubkey!(&master_token_account_owner.0, "master_token_account_owner"),
true,
)
.master_token_account(parse_official_pubkey!(
&master_token_account.0,
"master_token_account"
))
.master_metadata(parse_official_pubkey!(&master_metadata.0, "master_metadata"))
.update_authority(parse_official_pubkey!(&update_authority.0, "update_authority"))
.spl_token_program(parse_official_pubkey!(&spl_token_program.0, "spl_token_program"))
.spl_ata_program(parse_official_pubkey!(&spl_ata_program.0, "spl_ata_program"))
.sysvar_instructions(parse_official_pubkey!(&sysvar_instructions.0, "sysvar_instructions"))
.system_program(parse_official_pubkey!(&system_program.0, "system_program"))
.print_args(args.clone());
let official = builder.instruction();
return std::result::Result::Ok(convert_official_instruction!(official));
}
fn build_resize(
metadata: &crate::MdPubkey,
edition: &crate::MdPubkey,
mint: &crate::MdPubkey,
payer: &crate::MdPubkey,
authority: std::option::Option<&crate::MdPubkey>,
token: std::option::Option<&crate::MdPubkey>,
system_program: &crate::MdPubkey,
) -> kb_core::Result<solana_instruction::Instruction> {
let mut builder = mpl_token_metadata::instructions::ResizeBuilder::new();
builder
.metadata(parse_official_pubkey!(&metadata.0, "metadata"))
.edition(parse_official_pubkey!(&edition.0, "edition"))
.mint(parse_official_pubkey!(&mint.0, "mint"))
.payer(parse_official_pubkey!(&payer.0, "payer"), true)
.authority(parse_optional_official_pubkey!(authority, "authority"))
.token(parse_optional_official_pubkey!(token, "token"))
.system_program(parse_official_pubkey!(&system_program.0, "system_program"));
let official = builder.instruction();
return std::result::Result::Ok(convert_official_instruction!(official));
}
fn build_idl_instruction_trailing_optional_no_args(
discriminator: u8,
accounts: &[(&crate::MdPubkey, bool, bool)],
trailing_account: std::option::Option<(&crate::MdPubkey, bool, bool)>,
) -> kb_core::Result<solana_instruction::Instruction> {
let mut exact_accounts = accounts.to_vec();
if let std::option::Option::Some(value) = trailing_account {
exact_accounts.push(value);
}
return build_idl_instruction_no_args(discriminator, exact_accounts.as_slice());
}
fn build_idl_instruction_trailing_optional_with_args<T: borsh_0_10::BorshSerialize>(
discriminator: u8,
accounts: &[(&crate::MdPubkey, bool, bool)],
trailing_account: std::option::Option<(&crate::MdPubkey, bool, bool)>,
args: &T,
) -> kb_core::Result<solana_instruction::Instruction> {
let mut exact_accounts = accounts.to_vec();
if let std::option::Option::Some(value) = trailing_account {
exact_accounts.push(value);
}
return build_idl_instruction_with_args(discriminator, exact_accounts.as_slice(), args);
}
fn build_idl_instruction_optional_no_args(
discriminator: u8,
accounts: &[(std::option::Option<&crate::MdPubkey>, bool, bool)],
) -> kb_core::Result<solana_instruction::Instruction> {
return build_idl_instruction_optional(discriminator, accounts, std::option::Option::None);
}
fn build_idl_instruction_optional_with_args<T: borsh_0_10::BorshSerialize>(
discriminator: u8,
accounts: &[(std::option::Option<&crate::MdPubkey>, bool, bool)],
args: &T,
) -> kb_core::Result<solana_instruction::Instruction> {
let encoded = match borsh_0_10::BorshSerialize::try_to_vec(args) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(kb_core::Error::new(
"execution_metaplex_arguments_serialize_failed",
error.to_string(),
));
},
};
return build_idl_instruction_optional(
discriminator,
accounts,
std::option::Option::Some(encoded),
);
}
fn build_idl_instruction_optional(
discriminator: u8,
accounts: &[(std::option::Option<&crate::MdPubkey>, bool, bool)],
encoded_args: std::option::Option<std::vec::Vec<u8>>,
) -> kb_core::Result<solana_instruction::Instruction> {
let program_id = match parse_str_pubkey(
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
"program_id",
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut metas = std::vec::Vec::with_capacity(accounts.len());
for (value, is_signer, is_writable) in accounts {
match value {
std::option::Option::Some(value) => {
let pubkey = match parse_pubkey(value, "instruction_account") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
metas.push(solana_instruction::AccountMeta {
pubkey,
is_signer: *is_signer,
is_writable: *is_writable,
});
},
std::option::Option::None => {
metas.push(solana_instruction::AccountMeta {
pubkey: program_id,
is_signer: false,
is_writable: false,
});
},
}
}
let mut data = vec![discriminator];
if let std::option::Option::Some(mut value) = encoded_args {
data.append(&mut value);
}
return std::result::Result::Ok(solana_instruction::Instruction {
program_id,
accounts: metas,
data,
});
}
fn build_idl_instruction_no_args(
discriminator: u8,
accounts: &[(&crate::MdPubkey, bool, bool)],
@@ -1447,6 +1633,316 @@ mod tests {
assert!(instruction.accounts[3].is_signer);
}
#[test]
fn print_uses_current_official_signer_contract() {
let accounts: std::vec::Vec<crate::MdPubkey> = (0..18)
.map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string()))
.collect();
let result = super::build_print(
&accounts[0],
&accounts[1],
&accounts[2],
&accounts[3],
&accounts[4],
&accounts[5],
std::option::Option::Some(&accounts[6]),
&accounts[7],
&accounts[8],
&accounts[9],
&accounts[10],
&accounts[11],
&accounts[12],
&accounts[13],
&accounts[14],
&accounts[15],
&accounts[16],
&accounts[17],
&mpl_token_metadata::types::PrintArgs::V1 { edition: 1 },
);
assert!(result.is_ok());
let instruction = match result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(instruction.data.first().copied(), std::option::Option::Some(55));
assert_eq!(instruction.accounts.len(), 18);
assert!(instruction.accounts[2].is_signer);
assert!(instruction.accounts[2].is_writable);
assert!(instruction.accounts[5].is_signer);
assert!(!instruction.accounts[5].is_writable);
assert!(instruction.accounts[9].is_signer);
assert!(instruction.accounts[9].is_writable);
assert!(instruction.accounts[10].is_signer);
assert!(!instruction.accounts[10].is_writable);
}
#[test]
fn resize_uses_current_official_payer_signer_contract() {
let accounts: std::vec::Vec<crate::MdPubkey> = (0..7)
.map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string()))
.collect();
let result = super::build_resize(
&accounts[0],
&accounts[1],
&accounts[2],
&accounts[3],
std::option::Option::Some(&accounts[4]),
std::option::Option::Some(&accounts[5]),
&accounts[6],
);
assert!(result.is_ok());
let instruction = match result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(instruction.data, vec![56]);
assert_eq!(instruction.accounts.len(), 7);
assert!(instruction.accounts[3].is_signer);
assert!(instruction.accounts[3].is_writable);
assert!(instruction.accounts[4].is_signer);
assert!(!instruction.accounts[4].is_writable);
}
#[test]
fn positional_optional_accounts_use_placeholders_but_escrow_tail_is_omitted() {
let accounts: std::vec::Vec<crate::MdPubkey> = (0..15)
.map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string()))
.collect();
let program_id = match super::parse_str_pubkey(
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
"program_id",
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let create = super::build_idl_instruction_trailing_optional_no_args(
38,
&[
(&accounts[0], false, true),
(&accounts[1], false, true),
(&accounts[2], false, false),
(&accounts[3], false, false),
(&accounts[4], false, false),
(&accounts[5], true, true),
(&accounts[6], false, false),
(&accounts[7], false, false),
],
std::option::Option::None,
);
assert!(create.is_ok());
let create = match create {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(create.accounts.len(), 8);
let create_with_authority = super::build_idl_instruction_trailing_optional_no_args(
38,
&[
(&accounts[0], false, true),
(&accounts[1], false, true),
(&accounts[2], false, false),
(&accounts[3], false, false),
(&accounts[4], false, false),
(&accounts[5], true, true),
(&accounts[6], false, false),
(&accounts[7], false, false),
],
std::option::Option::Some((&accounts[8], true, false)),
);
assert!(create_with_authority.is_ok());
let create_with_authority = match create_with_authority {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(create_with_authority.accounts.len(), 9);
assert_eq!(create_with_authority.accounts[8].pubkey.to_string(), accounts[8].0);
assert!(create_with_authority.accounts[8].is_signer);
assert!(!create_with_authority.accounts[8].is_writable);
let transfer_out = super::build_idl_instruction_trailing_optional_with_args(
40,
&[
(&accounts[0], false, false),
(&accounts[1], false, true),
(&accounts[2], true, true),
(&accounts[3], false, false),
(&accounts[4], false, true),
(&accounts[5], false, true),
(&accounts[6], false, false),
(&accounts[7], false, false),
(&accounts[8], false, false),
(&accounts[9], false, false),
(&accounts[10], false, false),
(&accounts[11], false, false),
],
std::option::Option::None,
&mpl_token_metadata::instructions::TransferOutOfEscrowInstructionArgs { amount: 1 },
);
assert!(transfer_out.is_ok());
let transfer_out = match transfer_out {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(transfer_out.accounts.len(), 12);
let mint = super::build_idl_instruction_optional_with_args(
43,
&[
(std::option::Option::Some(&accounts[0]), false, true),
(std::option::Option::None, false, false),
(std::option::Option::Some(&accounts[2]), false, false),
(std::option::Option::None, false, true),
(std::option::Option::None, false, true),
(std::option::Option::Some(&accounts[5]), false, true),
(std::option::Option::Some(&accounts[6]), true, false),
(std::option::Option::None, false, false),
(std::option::Option::Some(&accounts[8]), true, true),
(std::option::Option::Some(&accounts[9]), false, false),
(std::option::Option::Some(&accounts[10]), false, false),
(std::option::Option::Some(&accounts[11]), false, false),
(std::option::Option::Some(&accounts[12]), false, false),
(std::option::Option::None, false, false),
(std::option::Option::None, false, false),
],
&mpl_token_metadata::types::MintArgs::V1 {
amount: 1,
authorization_data: std::option::Option::None,
},
);
assert!(mint.is_ok());
let mint = match mint {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(mint.accounts.len(), 15);
for index in [1_usize, 3, 4, 7, 13, 14] {
assert_eq!(mint.accounts[index].pubkey, program_id);
assert!(!mint.accounts[index].is_signer);
assert!(!mint.accounts[index].is_writable);
}
let migrate = super::build_idl_instruction_optional_no_args(
48,
&[
(std::option::Option::Some(&accounts[0]), false, true),
(std::option::Option::Some(&accounts[1]), false, true),
(std::option::Option::Some(&accounts[2]), false, true),
(std::option::Option::Some(&accounts[3]), false, false),
(std::option::Option::Some(&accounts[4]), false, false),
(std::option::Option::Some(&accounts[5]), true, true),
(std::option::Option::Some(&accounts[6]), true, false),
(std::option::Option::Some(&accounts[7]), false, false),
(std::option::Option::Some(&accounts[8]), false, false),
(std::option::Option::Some(&accounts[9]), false, true),
(std::option::Option::Some(&accounts[10]), false, false),
(std::option::Option::Some(&accounts[11]), false, false),
(std::option::Option::Some(&accounts[12]), false, false),
(std::option::Option::None, false, false),
(std::option::Option::None, false, false),
],
);
assert!(migrate.is_ok());
let migrate = match migrate {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(migrate.accounts[13].pubkey, program_id);
assert_eq!(migrate.accounts[14].pubkey, program_id);
let use_instruction = super::build_idl_instruction_optional_with_args(
51,
&[
(std::option::Option::Some(&accounts[0]), true, false),
(std::option::Option::None, false, true),
(std::option::Option::None, false, true),
(std::option::Option::Some(&accounts[3]), false, false),
(std::option::Option::Some(&accounts[4]), false, true),
(std::option::Option::None, false, true),
(std::option::Option::Some(&accounts[6]), true, false),
(std::option::Option::Some(&accounts[7]), false, false),
(std::option::Option::Some(&accounts[8]), false, false),
(std::option::Option::None, false, false),
(std::option::Option::None, false, false),
(std::option::Option::None, false, false),
],
&mpl_token_metadata::types::UseArgs::V1 {
authorization_data: std::option::Option::None,
},
);
assert!(use_instruction.is_ok());
let use_instruction = match use_instruction {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(use_instruction.accounts.len(), 12);
for index in [1_usize, 2, 5, 9, 10, 11] {
assert_eq!(use_instruction.accounts[index].pubkey, program_id);
assert!(!use_instruction.accounts[index].is_signer);
assert!(!use_instruction.accounts[index].is_writable);
}
}
#[test]
fn print_and_resize_accept_absent_current_optional_accounts() {
let accounts: std::vec::Vec<crate::MdPubkey> = (0..18)
.map(|_| return crate::MdPubkey(solana_pubkey::Pubkey::new_unique().to_string()))
.collect();
let program_id = match super::parse_str_pubkey(
kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID,
"program_id",
) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let print = super::build_print(
&accounts[0],
&accounts[1],
&accounts[2],
&accounts[3],
&accounts[4],
&accounts[5],
std::option::Option::None,
&accounts[7],
&accounts[8],
&accounts[9],
&accounts[10],
&accounts[11],
&accounts[12],
&accounts[13],
&accounts[14],
&accounts[15],
&accounts[16],
&accounts[17],
&mpl_token_metadata::types::PrintArgs::V1 { edition: 1 },
);
assert!(print.is_ok());
let print = match print {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(print.accounts.len(), 18);
assert_eq!(print.accounts[6].pubkey, program_id);
assert!(!print.accounts[6].is_signer);
assert!(!print.accounts[6].is_writable);
let resize = super::build_resize(
&accounts[0],
&accounts[1],
&accounts[2],
&accounts[3],
std::option::Option::None,
std::option::Option::None,
&accounts[6],
);
assert!(resize.is_ok());
let resize = match resize {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(resize.accounts.len(), 7);
assert_eq!(resize.accounts[4].pubkey, program_id);
assert_eq!(resize.accounts[5].pubkey, program_id);
assert!(!resize.accounts[4].is_signer);
assert!(!resize.accounts[4].is_writable);
assert!(!resize.accounts[5].is_signer);
assert!(!resize.accounts[5].is_writable);
}
#[test]
fn collect_builds_exact_discriminator_and_signer_contract() {
let authority = crate::MdPubkey("11111111111111111111111111111111".to_string());

View File

@@ -1,5 +1,5 @@
// file: kb-lib/src/executor/metadata/metaplex_token_metadata/intent.rs
// version: 8
// version: 9
//! Typed current and deprecated Metaplex Token Metadata execution intents.
@@ -729,8 +729,8 @@ pub enum ExMetaplexTokenMetadataOperation {
system_program: crate::MdPubkey,
/// Account `sysvarInstructions` from the official IDL contract.
sysvar_instructions: crate::MdPubkey,
/// Account `authority` from the official IDL contract.
authority: crate::MdPubkey,
/// Optional authority from the current SDK contract.
authority: std::option::Option<crate::MdPubkey>,
},
/// Build the current CloseEscrowAccount instruction.
CloseEscrowAccount {
@@ -777,30 +777,30 @@ pub enum ExMetaplexTokenMetadataOperation {
token_program: crate::MdPubkey,
/// Account `sysvarInstructions` from the official IDL contract.
sysvar_instructions: crate::MdPubkey,
/// Account `authority` from the official IDL contract.
authority: crate::MdPubkey,
/// Optional authority from the current SDK contract.
authority: std::option::Option<crate::MdPubkey>,
/// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::instructions::TransferOutOfEscrowInstructionArgs,
},
/// Build the current Mint instruction.
Mint {
/// Account `token` from the official IDL contract.
/// Account `token` from the current SDK contract.
token: crate::MdPubkey,
/// Account `tokenOwner` from the official IDL contract.
token_owner: crate::MdPubkey,
/// Optional owner of the token account in the current SDK contract.
token_owner: std::option::Option<crate::MdPubkey>,
/// Account `metadata` from the official IDL contract.
metadata: crate::MdPubkey,
/// Account `masterEdition` from the official IDL contract.
master_edition: crate::MdPubkey,
/// Account `tokenRecord` from the official IDL contract.
token_record: crate::MdPubkey,
/// Optional Master Edition account in the current SDK contract.
master_edition: std::option::Option<crate::MdPubkey>,
/// Optional token record account in the current SDK contract.
token_record: std::option::Option<crate::MdPubkey>,
/// Account `mint` from the official IDL contract.
mint: crate::MdPubkey,
/// Account `authority` from the official IDL contract.
authority: crate::MdPubkey,
/// Account `delegateRecord` from the official IDL contract.
delegate_record: crate::MdPubkey,
/// Optional delegate record account in the current SDK contract.
delegate_record: std::option::Option<crate::MdPubkey>,
/// Account `payer` from the official IDL contract.
payer: crate::MdPubkey,
/// Account `systemProgram` from the official IDL contract.
@@ -811,10 +811,10 @@ pub enum ExMetaplexTokenMetadataOperation {
spl_token_program: crate::MdPubkey,
/// Account `splAtaProgram` from the official IDL contract.
spl_ata_program: crate::MdPubkey,
/// Account `authorizationRulesProgram` from the official IDL contract.
authorization_rules_program: crate::MdPubkey,
/// Account `authorizationRules` from the official IDL contract.
authorization_rules: crate::MdPubkey,
/// Optional Token Authorization Rules program in the current SDK contract.
authorization_rules_program: std::option::Option<crate::MdPubkey>,
/// Optional Token Authorization Rules account in the current SDK contract.
authorization_rules: std::option::Option<crate::MdPubkey>,
/// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::types::MintArgs,
@@ -847,25 +847,25 @@ pub enum ExMetaplexTokenMetadataOperation {
sysvar_instructions: crate::MdPubkey,
/// Account `splTokenProgram` from the official IDL contract.
spl_token_program: crate::MdPubkey,
/// Account `authorizationRulesProgram` from the official IDL contract.
authorization_rules_program: crate::MdPubkey,
/// Account `authorizationRules` from the official IDL contract.
authorization_rules: crate::MdPubkey,
/// Optional Token Authorization Rules program in the current SDK contract.
authorization_rules_program: std::option::Option<crate::MdPubkey>,
/// Optional Token Authorization Rules account in the current SDK contract.
authorization_rules: std::option::Option<crate::MdPubkey>,
},
/// Build the current Use instruction.
Use {
/// Account `authority` from the official IDL contract.
authority: crate::MdPubkey,
/// Account `delegateRecord` from the official IDL contract.
delegate_record: crate::MdPubkey,
/// Optional delegate record account in the current SDK contract.
delegate_record: std::option::Option<crate::MdPubkey>,
/// Account `token` from the official IDL contract.
token: crate::MdPubkey,
token: std::option::Option<crate::MdPubkey>,
/// Account `mint` from the official IDL contract.
mint: crate::MdPubkey,
/// Account `metadata` from the official IDL contract.
metadata: crate::MdPubkey,
/// Account `edition` from the official IDL contract.
edition: crate::MdPubkey,
edition: std::option::Option<crate::MdPubkey>,
/// Account `payer` from the official IDL contract.
payer: crate::MdPubkey,
/// Account `systemProgram` from the official IDL contract.
@@ -873,11 +873,11 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Account `sysvarInstructions` from the official IDL contract.
sysvar_instructions: crate::MdPubkey,
/// Account `splTokenProgram` from the official IDL contract.
spl_token_program: crate::MdPubkey,
/// Account `authorizationRulesProgram` from the official IDL contract.
authorization_rules_program: crate::MdPubkey,
/// Account `authorizationRules` from the official IDL contract.
authorization_rules: crate::MdPubkey,
spl_token_program: std::option::Option<crate::MdPubkey>,
/// Optional Token Authorization Rules program in the current SDK contract.
authorization_rules_program: std::option::Option<crate::MdPubkey>,
/// Optional Token Authorization Rules account in the current SDK contract.
authorization_rules: std::option::Option<crate::MdPubkey>,
/// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::types::UseArgs,
@@ -903,8 +903,8 @@ pub enum ExMetaplexTokenMetadataOperation {
edition_token_account: crate::MdPubkey,
/// Account `editionMintAuthority` from the official IDL contract.
edition_mint_authority: crate::MdPubkey,
/// Account `editionTokenRecord` from the official IDL contract.
edition_token_record: crate::MdPubkey,
/// Optional edition token record account in the current SDK contract.
edition_token_record: std::option::Option<crate::MdPubkey>,
/// Account `masterEdition` from the official IDL contract.
master_edition: crate::MdPubkey,
/// Account `editionMarkerPda` from the official IDL contract.
@@ -941,10 +941,10 @@ pub enum ExMetaplexTokenMetadataOperation {
mint: crate::MdPubkey,
/// Account `payer` from the official IDL contract.
payer: crate::MdPubkey,
/// Account `authority` from the official IDL contract.
authority: crate::MdPubkey,
/// Account `token` from the official IDL contract.
token: crate::MdPubkey,
/// Optional authority from the current SDK contract.
authority: std::option::Option<crate::MdPubkey>,
/// Optional token account from the current SDK contract.
token: std::option::Option<crate::MdPubkey>,
/// Account `systemProgram` from the official IDL contract.
system_program: crate::MdPubkey,
},