토큰 그룹 및 멤버
GroupPointerExtension 및 GroupMemberPointerExtension 활성화 방법
GroupPointerExtension
는
업데이트 권한 및 최대 크기와 같은
그룹 구성이
포함된 계정을 가리켜 민트를 그룹 민트(컬렉션 NFT와 같은)로 지정합니다.
GroupMemberPointerExtension
는
그룹 주소 및 멤버 번호와 같은
멤버십 데이터가
포함된 계정을 가리켜 민트를 멤버 민트(컬렉션의 NFT와 같은)로 지정합니다.
Token Extensions Program은 Token Group Interface를 직접 구현하여 그룹 및 멤버 데이터를 민트 계정 자체에 저장할 수 있게 합니다. 포인터 확장이 민트 자체를 가리킬 때, TokenGroup 및 TokenGroupMember 확장을 사용하여 모든 컬렉션 및 멤버십 데이터를 민트 계정에 저장할 수 있습니다.
타입스크립트
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());
Console
Click to execute the code.
러스트
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(())}
Console
Click to execute the code.
Is this page helpful?