use borsh::BorshDeserialize;
use mpl_bubblegum::instructions::{
BurnInstructionArgs, CreateTreeConfigInstructionArgs, CreateTreeConfigV2InstructionArgs,
DelegateInstructionArgs, MintToCollectionV1InstructionArgs, MintV1InstructionArgs,
MintV2InstructionArgs, TransferInstructionArgs, UpdateMetadataInstructionArgs,
UpdateMetadataV2InstructionArgs,
};
use mpl_bubblegum::types::{MetadataArgs, MetadataArgsV2, UpdateArgs};
use thiserror::Error;
use tidepool_core::Creator;
use super::leaf_event::LeafSchemaEventDecoded;
use super::types::{CnftEvent, MintMetadata, NoopOverride};
pub const BUBBLEGUM_PROGRAM_ID: &str = "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY";
pub const CREATE_TREE_CONFIG_DISC: [u8; 8] = [165, 83, 136, 142, 89, 202, 47, 220];
pub const MINT_V1_DISC: [u8; 8] = [145, 98, 192, 118, 184, 147, 118, 104];
pub const MINT_TO_COLLECTION_V1_DISC: [u8; 8] = [153, 18, 178, 47, 197, 158, 86, 15];
pub const TRANSFER_DISC: [u8; 8] = [163, 52, 200, 231, 140, 3, 69, 186];
pub const BURN_DISC: [u8; 8] = [116, 110, 29, 56, 107, 219, 42, 93];
pub const DELEGATE_DISC: [u8; 8] = [90, 147, 75, 178, 85, 88, 4, 137];
pub const VERIFY_CREATOR_DISC: [u8; 8] = [52, 17, 96, 132, 71, 4, 85, 194];
pub const UNVERIFY_CREATOR_DISC: [u8; 8] = [107, 178, 57, 39, 105, 115, 112, 152];
pub const VERIFY_COLLECTION_DISC: [u8; 8] = [56, 113, 101, 253, 79, 55, 122, 169];
pub const UNVERIFY_COLLECTION_DISC: [u8; 8] = [250, 251, 42, 106, 41, 137, 186, 168];
pub const SET_AND_VERIFY_COLLECTION_DISC: [u8; 8] = [235, 242, 121, 216, 158, 234, 180, 234];
pub const UPDATE_METADATA_DISC: [u8; 8] = [170, 182, 43, 239, 97, 78, 225, 186];
pub const CREATE_TREE_CONFIG_V2_DISC: [u8; 8] = [55, 99, 95, 215, 142, 203, 227, 205];
pub const MINT_V2_DISC: [u8; 8] = [120, 121, 23, 146, 173, 110, 199, 205];
pub const TRANSFER_V2_DISC: [u8; 8] = [119, 40, 6, 235, 234, 221, 248, 49];
pub const BURN_V2_DISC: [u8; 8] = [115, 210, 34, 240, 232, 143, 183, 16];
pub const DELEGATE_V2_DISC: [u8; 8] = [95, 87, 125, 140, 181, 131, 128, 227];
pub const VERIFY_CREATOR_V2_DISC: [u8; 8] = [85, 138, 140, 42, 22, 241, 118, 102];
pub const UNVERIFY_CREATOR_V2_DISC: [u8; 8] = [174, 112, 29, 142, 230, 100, 239, 7];
pub const UPDATE_METADATA_V2_DISC: [u8; 8] = [43, 103, 89, 42, 121, 242, 62, 72];
pub const SET_COLLECTION_V2_DISC: [u8; 8] = [229, 35, 61, 91, 15, 14, 99, 160];
mod pos {
pub mod create_tree {
pub const MERKLE_TREE: usize = 1;
pub const MIN: usize = 7;
}
pub mod mint_v1 {
pub const LEAF_OWNER: usize = 1;
pub const LEAF_DELEGATE: usize = 2;
pub const MERKLE_TREE: usize = 3;
pub const MIN: usize = 9;
}
pub mod mint_to_collection {
pub const LEAF_OWNER: usize = 1;
pub const LEAF_DELEGATE: usize = 2;
pub const MERKLE_TREE: usize = 3;
pub const COLLECTION_MINT: usize = 8;
pub const MIN: usize = 16;
}
pub mod transfer {
pub const NEW_LEAF_OWNER: usize = 3;
pub const MERKLE_TREE: usize = 4;
pub const MIN: usize = 8;
}
pub mod burn {
pub const MERKLE_TREE: usize = 3;
pub const MIN: usize = 7;
}
pub mod delegate {
pub const NEW_LEAF_DELEGATE: usize = 3;
pub const MERKLE_TREE: usize = 4;
pub const MIN: usize = 8;
}
pub mod verify_creator {
pub const MERKLE_TREE: usize = 3;
pub const CREATOR: usize = 5;
pub const MIN: usize = 9;
}
pub mod verify_collection {
pub const MERKLE_TREE: usize = 3;
pub const COLLECTION_MINT: usize = 8;
pub const MIN: usize = 16;
}
pub mod update_metadata {
pub const MERKLE_TREE: usize = 8;
pub const MIN: usize = 13;
}
pub mod create_tree_v2 {
pub const MERKLE_TREE: usize = 1;
pub const MIN: usize = 7;
}
pub mod mint_v2 {
pub const LEAF_OWNER: usize = 4;
pub const LEAF_DELEGATE: usize = 5;
pub const MERKLE_TREE: usize = 6;
pub const CORE_COLLECTION: usize = 7;
pub const MIN: usize = 13;
}
pub mod transfer_v2 {
pub const NEW_LEAF_OWNER: usize = 5;
pub const MERKLE_TREE: usize = 6;
pub const MIN: usize = 11;
}
pub mod burn_v2 {
pub const MERKLE_TREE: usize = 5;
pub const MIN: usize = 12;
}
pub mod delegate_v2 {
pub const NEW_LEAF_DELEGATE: usize = 4;
pub const MERKLE_TREE: usize = 5;
pub const MIN: usize = 9;
}
pub mod verify_creator_v2 {
pub const CREATOR: usize = 2;
pub const MERKLE_TREE: usize = 5;
pub const MIN: usize = 9;
}
pub mod update_metadata_v2 {
pub const MERKLE_TREE: usize = 5;
pub const MIN: usize = 10;
}
pub mod set_collection_v2 {
pub const MERKLE_TREE: usize = 6;
pub const NEW_CORE_COLLECTION: usize = 8;
pub const MIN: usize = 14;
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum ParseError {
#[error("ix data truncated: expected at least {expected}, got {actual}")]
TruncatedData { expected: usize, actual: usize },
#[error("unknown discriminator: {discriminator:?}")]
UnknownDiscriminator { discriminator: [u8; 8] },
#[error("ix needs at least {expected} accounts, got {actual}")]
InsufficientAccounts { expected: usize, actual: usize },
#[error("ix args failed to decode: {0}")]
DecoderError(String),
#[error("unsupported: {0}")]
Unsupported(String),
}
pub fn parse_bubblegum_instruction(
data: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<Option<CnftEvent>, ParseError> {
if data.len() < 8 {
return Err(ParseError::TruncatedData {
expected: 8,
actual: data.len(),
});
}
let (disc, body) = data.split_at(8);
let disc: [u8; 8] = disc.try_into().expect("split_at(8) yields 8 bytes");
match disc {
CREATE_TREE_CONFIG_DISC => parse_create_tree(body, accounts).map(Some),
MINT_V1_DISC => parse_mint_v1(body, accounts, noop_event).map(Some),
MINT_TO_COLLECTION_V1_DISC => {
parse_mint_to_collection_v1(body, accounts, noop_event).map(Some)
}
TRANSFER_DISC => parse_transfer(body, accounts, noop_event).map(Some),
BURN_DISC => parse_burn(body, accounts, noop_event).map(Some),
DELEGATE_DISC => parse_delegate(body, accounts, noop_event).map(Some),
VERIFY_CREATOR_DISC => parse_verify_creator(accounts, noop_event).map(Some),
UNVERIFY_CREATOR_DISC => parse_unverify_creator(accounts, noop_event).map(Some),
VERIFY_COLLECTION_DISC => parse_verify_collection(accounts, noop_event).map(Some),
UNVERIFY_COLLECTION_DISC => parse_unverify_collection(accounts, noop_event).map(Some),
SET_AND_VERIFY_COLLECTION_DISC => {
parse_set_and_verify_collection(accounts, noop_event).map(Some)
}
UPDATE_METADATA_DISC => parse_update_metadata(body, accounts, noop_event).map(Some),
CREATE_TREE_CONFIG_V2_DISC => parse_create_tree_v2(body, accounts).map(Some),
MINT_V2_DISC => parse_mint_v2(body, accounts, noop_event).map(Some),
TRANSFER_V2_DISC => parse_transfer_v2(accounts, noop_event).map(Some),
BURN_V2_DISC => parse_burn_v2(accounts, noop_event).map(Some),
DELEGATE_V2_DISC => parse_delegate_v2(accounts, noop_event).map(Some),
VERIFY_CREATOR_V2_DISC => parse_verify_creator_v2(accounts, noop_event).map(Some),
UNVERIFY_CREATOR_V2_DISC => parse_unverify_creator_v2(accounts, noop_event).map(Some),
UPDATE_METADATA_V2_DISC => parse_update_metadata_v2(body, accounts, noop_event).map(Some),
SET_COLLECTION_V2_DISC => parse_set_collection_v2(accounts, noop_event).map(Some),
_ => Err(ParseError::UnknownDiscriminator {
discriminator: disc,
}),
}
}
fn parse_create_tree(body: &[u8], accounts: &[[u8; 32]]) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::create_tree::MIN)?;
let args = CreateTreeConfigInstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
Ok(CnftEvent::CreateTree {
tree: accounts[pos::create_tree::MERKLE_TREE],
depth: u8::try_from(args.max_depth).unwrap_or(u8::MAX),
max_buffer_size: args.max_buffer_size,
})
}
fn parse_mint_v1(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::mint_v1::MIN)?;
let args = MintV1InstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
let metadata = to_mint_metadata(&args.metadata, body);
Ok(CnftEvent::Mint {
tree: accounts[pos::mint_v1::MERKLE_TREE],
owner: accounts[pos::mint_v1::LEAF_OWNER],
delegate: accounts[pos::mint_v1::LEAF_DELEGATE],
metadata,
verify_collection: None,
noop: noop_to_override(noop_event),
})
}
fn parse_mint_to_collection_v1(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::mint_to_collection::MIN)?;
let args = MintToCollectionV1InstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
let collection_mint = accounts[pos::mint_to_collection::COLLECTION_MINT];
let mut metadata = to_mint_metadata(&args.metadata, body);
metadata.collection = Some((collection_mint, true));
Ok(CnftEvent::Mint {
tree: accounts[pos::mint_to_collection::MERKLE_TREE],
owner: accounts[pos::mint_to_collection::LEAF_OWNER],
delegate: accounts[pos::mint_to_collection::LEAF_DELEGATE],
metadata,
verify_collection: Some(collection_mint),
noop: noop_to_override(noop_event),
})
}
fn parse_transfer(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::transfer::MIN)?;
let args = TransferInstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
let new_owner = accounts[pos::transfer::NEW_LEAF_OWNER];
Ok(CnftEvent::Transfer {
tree: accounts[pos::transfer::MERKLE_TREE],
leaf_index: u64::from(args.index),
nonce: args.nonce,
new_owner,
new_delegate: new_owner,
data_hash: args.data_hash,
creator_hash: args.creator_hash,
noop: noop_to_override(noop_event),
})
}
fn parse_burn(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::burn::MIN)?;
let args = BurnInstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
Ok(CnftEvent::Burn {
tree: accounts[pos::burn::MERKLE_TREE],
leaf_index: u64::from(args.index),
nonce: args.nonce,
noop: noop_to_override(noop_event),
})
}
fn parse_delegate(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::delegate::MIN)?;
let args = DelegateInstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
Ok(CnftEvent::Delegate {
tree: accounts[pos::delegate::MERKLE_TREE],
leaf_index: u64::from(args.index),
nonce: args.nonce,
new_delegate: accounts[pos::delegate::NEW_LEAF_DELEGATE],
data_hash: args.data_hash,
creator_hash: args.creator_hash,
noop: noop_to_override(noop_event),
})
}
fn parse_verify_creator(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::verify_creator::MIN)?;
let noop = require_noop(noop_event, "verifyCreator")?;
Ok(CnftEvent::VerifyCreator {
tree: accounts[pos::verify_creator::MERKLE_TREE],
creator: accounts[pos::verify_creator::CREATOR],
noop,
})
}
fn parse_unverify_creator(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::verify_creator::MIN)?;
let noop = require_noop(noop_event, "unverifyCreator")?;
Ok(CnftEvent::UnverifyCreator {
tree: accounts[pos::verify_creator::MERKLE_TREE],
creator: accounts[pos::verify_creator::CREATOR],
noop,
})
}
fn parse_verify_collection(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::verify_collection::MIN)?;
let noop = require_noop(noop_event, "verifyCollection")?;
Ok(CnftEvent::VerifyCollection {
tree: accounts[pos::verify_collection::MERKLE_TREE],
collection: accounts[pos::verify_collection::COLLECTION_MINT],
noop,
})
}
fn parse_unverify_collection(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::verify_collection::MIN)?;
let noop = require_noop(noop_event, "unverifyCollection")?;
Ok(CnftEvent::UnverifyCollection {
tree: accounts[pos::verify_collection::MERKLE_TREE],
collection: accounts[pos::verify_collection::COLLECTION_MINT],
noop,
})
}
fn parse_set_and_verify_collection(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::verify_collection::MIN)?;
let noop = require_noop(noop_event, "setAndVerifyCollection")?;
Ok(CnftEvent::SetAndVerifyCollection {
tree: accounts[pos::verify_collection::MERKLE_TREE],
collection: accounts[pos::verify_collection::COLLECTION_MINT],
noop,
})
}
fn parse_update_metadata(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::update_metadata::MIN)?;
let args = UpdateMetadataInstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
let noop = require_noop(noop_event, "updateMetadata")?;
let new_metadata = update_args_to_mint_metadata(&args.update_args, body);
Ok(CnftEvent::UpdateMetadata {
tree: accounts[pos::update_metadata::MERKLE_TREE],
new_metadata,
noop,
})
}
fn parse_create_tree_v2(body: &[u8], accounts: &[[u8; 32]]) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::create_tree_v2::MIN)?;
let args = CreateTreeConfigV2InstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
Ok(CnftEvent::CreateTree {
tree: accounts[pos::create_tree_v2::MERKLE_TREE],
depth: u8::try_from(args.max_depth).unwrap_or(u8::MAX),
max_buffer_size: args.max_buffer_size,
})
}
fn parse_mint_v2(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::mint_v2::MIN)?;
let noop = require_noop(noop_event, "mintV2")?;
let args = MintV2InstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
let leaf_owner = accounts[pos::mint_v2::LEAF_OWNER];
let leaf_delegate_slot = accounts[pos::mint_v2::LEAF_DELEGATE];
let leaf_delegate = if is_bubblegum_placeholder(&leaf_delegate_slot) {
leaf_owner
} else {
leaf_delegate_slot
};
let core_collection_slot = accounts[pos::mint_v2::CORE_COLLECTION];
let verify_collection = if is_bubblegum_placeholder(&core_collection_slot) {
None
} else {
Some(core_collection_slot)
};
let metadata = v2_metadata_to_mint_metadata(&args.metadata, body);
Ok(CnftEvent::Mint {
tree: accounts[pos::mint_v2::MERKLE_TREE],
owner: leaf_owner,
delegate: leaf_delegate,
metadata,
verify_collection,
noop: Some(noop),
})
}
fn parse_transfer_v2(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::transfer_v2::MIN)?;
let noop = require_noop(noop_event, "transferV2")?;
let new_owner = accounts[pos::transfer_v2::NEW_LEAF_OWNER];
Ok(CnftEvent::Transfer {
tree: accounts[pos::transfer_v2::MERKLE_TREE],
leaf_index: noop.leaf_index,
nonce: noop.nonce,
new_owner,
new_delegate: new_owner,
data_hash: noop.data_hash,
creator_hash: noop.creator_hash,
noop: Some(noop),
})
}
fn parse_burn_v2(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::burn_v2::MIN)?;
let noop = require_noop(noop_event, "burnV2")?;
Ok(CnftEvent::Burn {
tree: accounts[pos::burn_v2::MERKLE_TREE],
leaf_index: noop.leaf_index,
nonce: noop.nonce,
noop: Some(noop),
})
}
fn parse_delegate_v2(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::delegate_v2::MIN)?;
let noop = require_noop(noop_event, "delegateV2")?;
Ok(CnftEvent::Delegate {
tree: accounts[pos::delegate_v2::MERKLE_TREE],
leaf_index: noop.leaf_index,
nonce: noop.nonce,
new_delegate: accounts[pos::delegate_v2::NEW_LEAF_DELEGATE],
data_hash: noop.data_hash,
creator_hash: noop.creator_hash,
noop: Some(noop),
})
}
fn parse_verify_creator_v2(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::verify_creator_v2::MIN)?;
let noop = require_noop(noop_event, "verifyCreatorV2")?;
Ok(CnftEvent::VerifyCreator {
tree: accounts[pos::verify_creator_v2::MERKLE_TREE],
creator: accounts[pos::verify_creator_v2::CREATOR],
noop,
})
}
fn parse_unverify_creator_v2(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::verify_creator_v2::MIN)?;
let noop = require_noop(noop_event, "unverifyCreatorV2")?;
Ok(CnftEvent::UnverifyCreator {
tree: accounts[pos::verify_creator_v2::MERKLE_TREE],
creator: accounts[pos::verify_creator_v2::CREATOR],
noop,
})
}
fn parse_update_metadata_v2(
body: &[u8],
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::update_metadata_v2::MIN)?;
let noop = require_noop(noop_event, "updateMetadataV2")?;
let args = UpdateMetadataV2InstructionArgs::try_from_slice(body)
.map_err(|e| ParseError::DecoderError(e.to_string()))?;
let new_metadata = update_args_to_mint_metadata(&args.update_args, body);
Ok(CnftEvent::UpdateMetadata {
tree: accounts[pos::update_metadata_v2::MERKLE_TREE],
new_metadata,
noop,
})
}
fn parse_set_collection_v2(
accounts: &[[u8; 32]],
noop_event: Option<&LeafSchemaEventDecoded>,
) -> Result<CnftEvent, ParseError> {
require_accounts(accounts.len(), pos::set_collection_v2::MIN)?;
let noop = require_noop(noop_event, "setCollectionV2")?;
let new_collection = accounts[pos::set_collection_v2::NEW_CORE_COLLECTION];
if is_bubblegum_placeholder(&new_collection) {
return Err(ParseError::Unsupported(
"setCollectionV2 without a new core_collection slot isn't supported".into(),
));
}
Ok(CnftEvent::SetAndVerifyCollection {
tree: accounts[pos::set_collection_v2::MERKLE_TREE],
collection: new_collection,
noop,
})
}
fn is_bubblegum_placeholder(pk: &[u8; 32]) -> bool {
static PLACEHOLDER: std::sync::OnceLock<[u8; 32]> = std::sync::OnceLock::new();
let placeholder = PLACEHOLDER.get_or_init(|| {
let p: solana_program::pubkey::Pubkey = BUBBLEGUM_PROGRAM_ID
.parse()
.expect("BUBBLEGUM_PROGRAM_ID is a valid base58 pubkey");
p.to_bytes()
});
pk == placeholder
}
fn v2_metadata_to_mint_metadata(m: &MetadataArgsV2, ix_body_after_disc: &[u8]) -> MintMetadata {
let creators = m
.creators
.iter()
.map(|c| Creator {
address: c.address.to_bytes(),
verified: c.verified,
share: c.share,
})
.collect();
let collection = m.collection.as_ref().map(|key| (key.to_bytes(), true));
MintMetadata {
name: m.name.clone(),
symbol: m.symbol.clone(),
uri: m.uri.clone(),
seller_fee_basis_points: m.seller_fee_basis_points,
primary_sale_happened: m.primary_sale_happened,
is_mutable: m.is_mutable,
creators,
collection,
data_hash_input: ix_body_after_disc.to_vec(),
}
}
fn require_accounts(actual: usize, expected: usize) -> Result<(), ParseError> {
if actual < expected {
return Err(ParseError::InsufficientAccounts { expected, actual });
}
Ok(())
}
fn require_noop(
noop_event: Option<&LeafSchemaEventDecoded>,
ix_name: &'static str,
) -> Result<NoopOverride, ParseError> {
let event = noop_event.ok_or_else(|| {
ParseError::Unsupported(format!(
"{ix_name} requires a paired noop LeafSchemaEvent to resolve new state; none found"
))
})?;
Ok(event.as_override())
}
fn noop_to_override(noop_event: Option<&LeafSchemaEventDecoded>) -> Option<NoopOverride> {
noop_event.map(LeafSchemaEventDecoded::as_override)
}
fn to_mint_metadata(m: &MetadataArgs, ix_body_after_disc: &[u8]) -> MintMetadata {
let creators = m
.creators
.iter()
.map(|c| Creator {
address: c.address.to_bytes(),
verified: c.verified,
share: c.share,
})
.collect();
let collection = m
.collection
.as_ref()
.map(|c| (c.key.to_bytes(), c.verified));
MintMetadata {
name: m.name.clone(),
symbol: m.symbol.clone(),
uri: m.uri.clone(),
seller_fee_basis_points: m.seller_fee_basis_points,
primary_sale_happened: m.primary_sale_happened,
is_mutable: m.is_mutable,
creators,
collection,
data_hash_input: ix_body_after_disc.to_vec(),
}
}
fn update_args_to_mint_metadata(u: &UpdateArgs, ix_body_after_disc: &[u8]) -> MintMetadata {
let creators: Vec<Creator> = u
.creators
.as_ref()
.map(|list| {
list.iter()
.map(|c| Creator {
address: c.address.to_bytes(),
verified: c.verified,
share: c.share,
})
.collect()
})
.unwrap_or_default();
MintMetadata {
name: u.name.clone().unwrap_or_default(),
symbol: u.symbol.clone().unwrap_or_default(),
uri: u.uri.clone().unwrap_or_default(),
seller_fee_basis_points: u.seller_fee_basis_points.unwrap_or(0),
primary_sale_happened: u.primary_sale_happened.unwrap_or(false),
is_mutable: u.is_mutable.unwrap_or(true),
creators,
collection: None,
data_hash_input: ix_body_after_disc.to_vec(),
}
}