v0.4.8-pre.013
This commit is contained in:
@@ -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 : l’authority 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 n’est ajouté en fin de liste, conformément aux builders officiels et au handler qui déduit la présence de l’autorité à 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` : l’autorité 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 lorsqu’un même compte occupe aussi le rôle de fee payer ;
|
||||
- conserve l’exigence writable des comptes optionnels réellement mutés (`delegate_record`, `token_record`, `token`) lorsqu’ils ne sont pas le placeholder positionnel du programme Token Metadata ;
|
||||
- ajoute une régression reproduisant `Delegate/Revoke` avec l’autorité é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 l’allocation 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 n’accepte 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 d’un fee flag, d’un token standard ou d’un 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 l’exigence 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 l’audit 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 d’instruction courante ;
|
||||
- ajoute des régressions sur les positions optionnelles de `Mint`, `Migrate`, `Use`, `Print` et `Resize`, ainsi que sur l’autorité optionnelle d’escrow ;
|
||||
- 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 l’archive reproduise l’état Clippy réellement validé ;
|
||||
- migre `Print` et `Resize` depuis les account metas figées de l’IDL 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 l’autorité fournie reste signer ;
|
||||
- ajoute des régressions directes sur l’ordre, 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` ;
|
||||
|
||||
@@ -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 n’effectue 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 l’intent autorise explicitement l’exécution dépréciée ; leurs variantes Rust sont marquées `#[deprecated]`.
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user