Token-Gruppen und Mitglieder
Wie man die GroupPointerExtension und GroupMemberPointerExtension aktiviert
Die
GroupPointerExtension
kennzeichnet einen mint account als Gruppen-Mint (wie z.B. ein Collection NFT),
indem sie auf ein Konto verweist, das
Gruppenkonfigurationen
wie Update-Berechtigung und maximale Größe enthält.
Die
GroupMemberPointerExtension
kennzeichnet einen mint account als Mitglieds-Mint (wie z.B. ein NFT in einer
Kollektion), indem sie auf ein Konto verweist, das
Mitgliedschaftsdaten
wie Gruppenadresse und Mitgliedsnummer enthält.
Das Token Extensions Program implementiert die Token Group Interface direkt, wodurch sowohl Gruppen- als auch Mitgliedsdaten auf dem mint account selbst gespeichert werden können. Wenn die Pointer-Extensions auf den Mint selbst verweisen, können die TokenGroup und TokenGroupMember Extensions verwendet werden, um alle Sammlungs- und Mitgliedschaftsdaten im mint account zu speichern.
Typescript
import { getCreateAccountInstruction } from "@solana-program/system";import {extension,getInitializeAccountInstruction,getInitializeMintInstruction,getInitializeGroupPointerInstruction,getMintSize,TOKEN_2022_PROGRAM_ADDRESS,getInitializeTokenGroupInstruction,getInitializeGroupMemberPointerInstruction,getInitializeTokenGroupMemberInstruction} from "@solana-program/token-2022";import {airdropFactory,appendTransactionMessageInstructions,createSolanaRpc,createSolanaRpcSubscriptions,createTransactionMessage,generateKeyPairSigner,getSignatureFromTransaction,lamports,pipe,sendAndConfirmTransactionFactory,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,signTransactionMessageWithSigners,some} from "@solana/kit";// Create Connection, local validator in this exampleconst rpc = createSolanaRpc("http://localhost:8899");const rpcSubscriptions = createSolanaRpcSubscriptions("ws://localhost:8900");// Generate the authority for the mint (also acts as fee payer)const authority = await generateKeyPairSigner();// Fund authority/fee payerawait airdropFactory({ rpc, rpcSubscriptions })({recipientAddress: authority.address,lamports: lamports(5_000_000_000n), // 5 SOLcommitment: "confirmed"});// ===== GROUP MINT CREATION =====// Generate keypair to use as address of group mintconst groupMint = await generateKeyPairSigner();// Group pointer extension (points to external group account)const groupPointerExtension = extension("GroupPointer", {authority: authority.address,groupAddress: groupMint.address // Points to the group mint itself});// Group extension (stores group data directly on mint)const groupExtension = extension("TokenGroup", {updateAuthority: some(authority.address),mint: groupMint.address,size: 1, // Will be incremented when members are addedmaxSize: 10});// Get mint account size with group pointer extensionconst spaceWithGroupPointerExtensions = BigInt(getMintSize([groupPointerExtension]));// Get mint account size with group pointer and group extensionconst spaceWithGroupAndGroupPointerExtensions = BigInt(getMintSize([groupPointerExtension, groupExtension]));// Get rent exempt balance for group mintconst groupMintRent = await rpc.getMinimumBalanceForRentExemption(spaceWithGroupAndGroupPointerExtensions).send();// Get latest blockhashconst { value: latestBlockhash } = await rpc.getLatestBlockhash().send();// GROUP MINT INSTRUCTIONSconst createGroupMintAccountInstruction = getCreateAccountInstruction({payer: authority,newAccount: groupMint,lamports: groupMintRent,space: spaceWithGroupPointerExtensions,programAddress: TOKEN_2022_PROGRAM_ADDRESS});const initializeGroupPointerInstruction = getInitializeGroupPointerInstruction({mint: groupMint.address,authority: authority.address,groupAddress: groupMint.address});const initializeGroupMintInstruction = getInitializeMintInstruction({mint: groupMint.address,decimals: 0, // NFT-stylemintAuthority: authority.address,freezeAuthority: authority.address});const initializeGroupInstruction = getInitializeTokenGroupInstruction({group: groupMint.address,mint: groupMint.address,mintAuthority: authority,updateAuthority: authority.address,maxSize: 10});// Build group mint transactionconst groupInstructions = [createGroupMintAccountInstruction,initializeGroupPointerInstruction,initializeGroupMintInstruction,initializeGroupInstruction];const groupTransactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),(tx) => appendTransactionMessageInstructions(groupInstructions, tx));const signedGroupTransaction = await signTransactionMessageWithSigners(groupTransactionMessage);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedGroupTransaction,{ commitment: "confirmed", skipPreflight: true });const groupTransactionSignature = getSignatureFromTransaction(signedGroupTransaction);console.log("Group Mint Address:", groupMint.address.toString());console.log("Group Transaction Signature:", groupTransactionSignature);// ===== MEMBER MINT CREATION =====// Generate keypair for member mintconst memberMint = await generateKeyPairSigner();// Member pointer extension (points to external member account)const memberPointerExtension = extension("GroupMemberPointer", {authority: authority.address,memberAddress: memberMint.address // Points to the member mint itself});// Member extension (stores membership data directly on mint)const memberExtension = extension("TokenGroupMember", {mint: memberMint.address,group: groupMint.address, // References the group mint we created abovememberNumber: 1 // First member of the group});// Get mint account size with group pointer extensionconst spaceWithMemberPointerExtension = BigInt(getMintSize([groupPointerExtension]));// Get mint account size with group pointer and group extensionconst spaceWithMemberAndMemberPointerExtensions = BigInt(getMintSize([groupPointerExtension, groupExtension]));// Get rent exempt balance for member mintconst memberMintRent = await rpc.getMinimumBalanceForRentExemption(spaceWithMemberAndMemberPointerExtensions).send();// Get fresh blockhash for member mint transactionconst { value: memberLatestBlockhash } = await rpc.getLatestBlockhash().send();// MEMBER MINT INSTRUCTIONSconst createMemberMintAccountInstruction = getCreateAccountInstruction({payer: authority,newAccount: memberMint,lamports: memberMintRent,space: spaceWithMemberPointerExtension, // Only pointer extension space for account creationprogramAddress: TOKEN_2022_PROGRAM_ADDRESS});const initializeMemberPointerInstruction =getInitializeGroupMemberPointerInstruction({mint: memberMint.address,authority: authority.address,memberAddress: memberMint.address});const initializeMemberMintInstruction = getInitializeMintInstruction({mint: memberMint.address,decimals: 0, // NFT-stylemintAuthority: authority.address,freezeAuthority: authority.address});// Initialize member extension (added post-mint initialization)const initializeMemberInstruction = getInitializeTokenGroupMemberInstruction({member: memberMint.address,memberMint: memberMint.address,memberMintAuthority: authority,group: groupMint.address, // References the group mintgroupUpdateAuthority: authority});// Build member mint transactionconst memberInstructions = [createMemberMintAccountInstruction,initializeMemberPointerInstruction,initializeMemberMintInstruction,initializeMemberInstruction];const memberTransactionMessage = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(authority, tx),(tx) =>setTransactionMessageLifetimeUsingBlockhash(memberLatestBlockhash, tx),(tx) => appendTransactionMessageInstructions(memberInstructions, tx));const signedMemberTransaction = await signTransactionMessageWithSigners(memberTransactionMessage);await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedMemberTransaction,{ commitment: "confirmed", skipPreflight: true });const memberTransactionSignature = getSignatureFromTransaction(signedMemberTransaction);console.log("Member Mint Address:", memberMint.address.toString());console.log("Member Transaction Signature:", memberTransactionSignature);console.log("\n===== SUMMARY =====");console.log("Group Mint:", groupMint.address.toString());console.log("Member Mint:", memberMint.address.toString());console.log("Member belongs to Group:", groupMint.address.toString());
Rust
use anyhow::Result;use solana_client::nonblocking::rpc_client::RpcClient;use solana_sdk::{commitment_config::CommitmentConfig,signature::{Keypair, Signer},system_instruction::create_account,transaction::Transaction,};use spl_token_2022::{ID as TOKEN_2022_PROGRAM_ID,extension::{ExtensionType,group_member_pointer::instruction::initialize as initialize_group_member_pointer,group_pointer::instruction::initialize as initialize_group_pointer,},instruction::initialize_mint,state::Mint,};use spl_token_group_interface::instruction::{initialize_group, initialize_member}; // < 0.6.0#[tokio::main]async fn main() -> Result<()> {// Create connection to local validatorlet client = RpcClient::new_with_commitment(String::from("http://localhost:8899"),CommitmentConfig::confirmed(),);let latest_blockhash = client.get_latest_blockhash().await?;// Generate the authority for the mint (also acts as fee payer)let authority = Keypair::new();// Airdrop 5 SOL to authority/fee payerlet airdrop_signature = client.request_airdrop(&authority.pubkey(), 5_000_000_000).await?;client.confirm_transaction(&airdrop_signature).await?;loop {let confirmed = client.confirm_transaction(&airdrop_signature).await?;if confirmed {break;}}// ===== GROUP MINT CREATION =====// Generate keypair to use as address of group mintlet group_mint = Keypair::new();// Calculate space for mint with group pointer extension only (for account creation)let group_mint_space =ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::GroupPointer])?;// Calculate space for mint with group pointer and group extension (for rent calculation)let group_mint_space_with_data = ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::GroupPointer,ExtensionType::TokenGroup,])?;// Get rent exempt balance for full space (including data extension)let group_mint_rent = client.get_minimum_balance_for_rent_exemption(group_mint_space_with_data).await?;// GROUP MINT INSTRUCTIONSlet create_group_mint_account_instruction = create_account(&authority.pubkey(), // payer&group_mint.pubkey(), // new account (mint)group_mint_rent, // lamportsgroup_mint_space as u64, // space (pointer only for account creation)&TOKEN_2022_PROGRAM_ID, // program id);let initialize_group_pointer_instruction = initialize_group_pointer(&TOKEN_2022_PROGRAM_ID,&group_mint.pubkey(), // mintSome(authority.pubkey()), // authoritySome(group_mint.pubkey()), // group address (points to itself))?;let initialize_group_mint_instruction = initialize_mint(&TOKEN_2022_PROGRAM_ID, // program id&group_mint.pubkey(), // mint&authority.pubkey(), // mint authoritySome(&authority.pubkey()), // freeze authority0, // decimals (NFT-style))?;// Initialize group extension (added post-mint initialization)let initialize_group_instruction = initialize_group(&TOKEN_2022_PROGRAM_ID, // program id&group_mint.pubkey(), // group&group_mint.pubkey(), // mint&authority.pubkey(), // mint authoritySome(authority.pubkey()), // update authority10, // max size);// Build group mint transactionlet group_transaction = Transaction::new_signed_with_payer(&[create_group_mint_account_instruction,initialize_group_pointer_instruction,initialize_group_mint_instruction,initialize_group_instruction,],Some(&authority.pubkey()),&[&authority, &group_mint],latest_blockhash,);// Send and confirm group transactionlet group_transaction_signature = client.send_and_confirm_transaction(&group_transaction).await?;println!("Group Mint Address: {}", group_mint.pubkey());println!("Group Transaction Signature: {}",group_transaction_signature);// ===== MEMBER MINT CREATION =====// Generate keypair for member mintlet member_mint = Keypair::new();// Calculate space for mint with member pointer extension only (for account creation)let member_mint_space =ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::GroupMemberPointer])?;// Calculate space for mint with member pointer and member extension (for rent calculation)let member_mint_space_with_data = ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::GroupMemberPointer,ExtensionType::TokenGroupMember,])?;// Get rent exempt balance for full space (including data extension)let member_mint_rent = client.get_minimum_balance_for_rent_exemption(member_mint_space_with_data).await?;// Get fresh blockhash for member mint transactionlet member_latest_blockhash = client.get_latest_blockhash().await?;// MEMBER MINT INSTRUCTIONSlet create_member_mint_account_instruction = create_account(&authority.pubkey(), // payer&member_mint.pubkey(), // new account (mint)member_mint_rent, // lamportsmember_mint_space as u64, // space (pointer only for account creation)&TOKEN_2022_PROGRAM_ID, // program id);let initialize_member_pointer_instruction = initialize_group_member_pointer(&TOKEN_2022_PROGRAM_ID,&member_mint.pubkey(), // mintSome(authority.pubkey()), // authoritySome(member_mint.pubkey()), // member address (points to itself))?;let initialize_member_mint_instruction = initialize_mint(&TOKEN_2022_PROGRAM_ID, // program id&member_mint.pubkey(), // mint&authority.pubkey(), // mint authoritySome(&authority.pubkey()), // freeze authority0, // decimals (NFT-style))?;// Initialize member extension (added post-mint initialization)let initialize_member_instruction = initialize_member(&TOKEN_2022_PROGRAM_ID, // program id&member_mint.pubkey(), // member&member_mint.pubkey(), // member mint&authority.pubkey(), // member mint authority&group_mint.pubkey(), // group (references the group mint)&authority.pubkey(), // group update authority);// Build member mint transactionlet member_transaction = Transaction::new_signed_with_payer(&[create_member_mint_account_instruction,initialize_member_pointer_instruction,initialize_member_mint_instruction,initialize_member_instruction,],Some(&authority.pubkey()),&[&authority, &member_mint],member_latest_blockhash,);// Send and confirm member transactionlet member_transaction_signature = client.send_and_confirm_transaction(&member_transaction).await?;println!("Member Mint Address: {}", member_mint.pubkey());println!("Member Transaction Signature: {}",member_transaction_signature);println!("\n===== SUMMARY =====");println!("Group Mint: {}", group_mint.pubkey());println!("Member Mint: {}", member_mint.pubkey());println!("Member belongs to Group: {}", group_mint.pubkey());println!("\nSuccessfully created group and member mints with extensions");Ok(())}
Is this page helpful?