Skip to content

Commit

Permalink
add editions processor example
Browse files Browse the repository at this point in the history
  • Loading branch information
buffalojoec committed Oct 18, 2023
1 parent 928093f commit f22c516
Show file tree
Hide file tree
Showing 8 changed files with 520 additions and 10 deletions.
2 changes: 1 addition & 1 deletion token-group/example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ solana-program = "1.16.16"
spl-pod = { version = "0.1.0", path = "../../libraries/pod" }
spl-token-2022 = { version = "0.9.0", path = "../../token/program-2022" }
spl-token-group-interface = { version = "0.1.0", path = "../interface" }
spl-token-metadata-interface = { version = "0.2", path = "../../token-metadata/interface" }
spl-type-length-value = { version = "0.3.0", path = "../../libraries/type-length-value" }

[dev-dependencies]
solana-program-test = "1.16.16"
solana-sdk = "1.16.16"
spl-discriminator = { version = "0.1.0", path = "../../libraries/discriminator" }
spl-token-client = { version = "0.7", path = "../../token/client" }
spl-token-metadata-interface = { version = "0.2", path = "../../token-metadata/interface" }

[lib]
crate-type = ["cdylib", "lib"]
Expand Down
136 changes: 133 additions & 3 deletions token-group/example/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,26 @@ use {
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
program::invoke,
program_error::ProgramError,
program_option::COption,
pubkey::Pubkey,
},
spl_pod::optional_keys::OptionalNonZeroPubkey,
spl_token_2022::{extension::StateWithExtensions, state::Mint},
spl_token_2022::{
extension::{
metadata_pointer::MetadataPointer, BaseStateWithExtensions, StateWithExtensions,
},
state::Mint,
},
spl_token_group_interface::{
error::TokenGroupError,
instruction::{
InitializeGroup, TokenGroupInstruction, UpdateGroupAuthority, UpdateGroupMaxSize,
},
state::{TokenGroup, TokenGroupMember},
},
spl_token_metadata_interface::state::TokenMetadata,
spl_type_length_value::state::TlvStateMut,
};

Expand Down Expand Up @@ -201,6 +208,122 @@ pub fn process_initialize_collection_member(
Ok(())
}

/// Processes a [InitializeMember](enum.GroupInterfaceInstruction.html)
/// instruction for an `Edition`.
///
/// This function demonstrates using this interface for editions as well.
fn process_initialize_edition_reprint(
_program_id: &Pubkey,
accounts: &[AccountInfo],
) -> ProgramResult {
// Here we are going to assume the original has been created and
// initialized as a group, then we can use the original to print "reprints"
// from it.
// We're also assuming a mint _and_ metadata have been created for _both_
// the original and the reprint.
let account_info_iter = &mut accounts.iter();

// Accounts expected by this instruction:
//
// 0. `[w]` Reprint (Member)
// 1. `[]` Reprint (Member) Mint
// 2. `[s]` Reprint (Member) Mint authority
// 3. `[w]` Original (Group)
// 4. `[s]` Original (Group) update authority
let reprint_info = next_account_info(account_info_iter)?;
// Note this particular example _also_ requires the mint to be writable!
let reprint_mint_info = next_account_info(account_info_iter)?;
let reprint_mint_authority_info = next_account_info(account_info_iter)?;
let original_info = next_account_info(account_info_iter)?;
let original_update_authority_info = next_account_info(account_info_iter)?;

// Additional accounts expected by this instruction:
//
// 5. `[]` Original (Group) Mint
// 6. `[]` SPL Token 2022 program
let original_mint_info = next_account_info(account_info_iter)?;
let _program_2022_info = next_account_info(account_info_iter)?;

// Mint & metadata checks on the original
let original_token_metadata = {
// IMPORTANT: this example program is designed to work with any
// program that implements the SPL token interface, so there is no
// ownership check on the mint account.
let original_mint_data = original_mint_info.try_borrow_data()?;
let original_mint = StateWithExtensions::<Mint>::unpack(&original_mint_data)?;

// Make sure the metadata pointer is pointing to the mint itself
let metadata_pointer = original_mint.get_extension::<MetadataPointer>()?;
let metadata_pointer_address = Option::<Pubkey>::from(metadata_pointer.metadata_address);
if metadata_pointer_address != Some(*original_mint_info.key) {
return Err(ProgramError::InvalidAccountData);
}

// Extract the token metadata
original_mint.get_variable_len_extension::<TokenMetadata>()?
};

// Mint checks on the reprint
{
// IMPORTANT: this example program is designed to work with any
// program that implements the SPL token interface, so there is no
// ownership check on the mint account.
let reprint_mint_data = reprint_mint_info.try_borrow_data()?;
let reprint_mint = StateWithExtensions::<Mint>::unpack(&reprint_mint_data)?;

if !reprint_mint_authority_info.is_signer {
return Err(ProgramError::MissingRequiredSignature);
}
if reprint_mint.base.mint_authority.as_ref()
!= COption::Some(reprint_mint_authority_info.key)
{
return Err(TokenGroupError::IncorrectAuthority.into());
}

// Make sure the metadata pointer is pointing to the mint itself
let metadata_pointer = reprint_mint.get_extension::<MetadataPointer>()?;
let metadata_pointer_address = Option::<Pubkey>::from(metadata_pointer.metadata_address);
if metadata_pointer_address != Some(*reprint_mint_info.key) {
return Err(ProgramError::InvalidAccountData);
}
}

// Increment the size of the editions
let mut buffer = original_info.try_borrow_mut_data()?;
let mut state = TlvStateMut::unpack(&mut buffer)?;
let original = state.get_first_value_mut::<TokenGroup>()?;

check_update_authority(original_update_authority_info, &original.update_authority)?;
let reprint_number = original.increment_size()?;

// Allocate a TLV entry for the space and write it in
let mut buffer = reprint_info.try_borrow_mut_data()?;
let mut state = TlvStateMut::unpack(&mut buffer)?;
let (reprint, _) = state.init_value::<TokenGroupMember>(false)?;
*reprint = TokenGroupMember::new(*original_info.key, reprint_number);

// Use the original metadata to initialize the reprint metadata
let cpi_instruction = spl_token_metadata_interface::instruction::initialize(
&spl_token_2022::id(),
reprint_mint_info.key,
original_update_authority_info.key,
reprint_mint_info.key,
reprint_mint_authority_info.key,
original_token_metadata.name,
original_token_metadata.symbol,
original_token_metadata.uri,
);
let cpi_account_infos = &[
reprint_mint_info.clone(),
original_update_authority_info.clone(),
reprint_mint_info.clone(),
reprint_mint_authority_info.clone(),
];
invoke(&cpi_instruction, cpi_account_infos)?;

Ok(())
}

/// Processes an `SplTokenGroupInstruction`
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
let instruction = TokenGroupInstruction::unpack(input)?;
Expand All @@ -218,8 +341,15 @@ pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> P
process_update_collection_authority(program_id, accounts, data)
}
TokenGroupInstruction::InitializeMember(_) => {
msg!("Instruction: InitializeCollectionMember");
process_initialize_collection_member(program_id, accounts)
// For demonstration purposes, we'll use the number of accounts
// provided to determine which type of member to initialize.
if accounts.len() == 5 {
msg!("Instruction: InitializeCollectionMember");
process_initialize_collection_member(program_id, accounts)
} else {
msg!("Instruction: InitializeEdition");
process_initialize_edition_reprint(program_id, accounts)
}
}
}
}
8 changes: 7 additions & 1 deletion token-group/example/tests/initialize_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ async fn test_initialize_collection() {
Some(0),
payer.clone(),
);
setup_mint(&token_client, &collection_mint, &collection_mint_authority).await;
setup_mint(
&token_client,
&collection_mint,
&collection_mint_authority,
vec![],
)
.await;

let mut context = context.lock().await;

Expand Down
2 changes: 2 additions & 0 deletions token-group/example/tests/initialize_collection_member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ async fn test_initialize_collection_member() {
),
&collection_mint,
&collection_mint_authority,
vec![],
)
.await;
setup_mint(
Expand All @@ -62,6 +63,7 @@ async fn test_initialize_collection_member() {
),
&member_mint,
&member_mint_authority,
vec![],
)
.await;

Expand Down
Loading

0 comments on commit f22c516

Please sign in to comment.