Ομάδες και Μέλη Token

Πώς να ενεργοποιήσετε το GroupPointerExtension και το GroupMemberPointerExtension

Το GroupPointerExtension ορίζει ένα mint ως mint ομάδας (όπως ένα Collection NFT) δείχνοντας σε έναν λογαριασμό που περιέχει ρυθμίσεις ομάδας όπως αρχή ενημέρωσης και μέγιστο μέγεθος.

Το GroupMemberPointerExtension ορίζει ένα mint ως mint μέλους (όπως ένα NFT σε μια συλλογή) δείχνοντας σε έναν λογαριασμό που περιέχει δεδομένα μέλους όπως διεύθυνση ομάδας και αριθμό μέλους.

Το Token Extensions Program υλοποιεί το Token Group Interface απευθείας, επιτρέποντας τόσο στα δεδομένα ομάδας όσο και στα δεδομένα μέλους να αποθηκεύονται στον ίδιο τον λογαριασμό mint. Όταν οι δείκτες επεκτάσεων δείχνουν στο ίδιο το mint, οι επεκτάσεις TokenGroup και TokenGroupMember μπορούν να χρησιμοποιηθούν για την αποθήκευση όλων των δεδομένων συλλογής και μελών στον λογαριασμό mint.

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 example
const 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 payer
await airdropFactory({ rpc, rpcSubscriptions })({
recipientAddress: authority.address,
lamports: lamports(5_000_000_000n), // 5 SOL
commitment: "confirmed"
});
// ===== GROUP MINT CREATION =====
// Generate keypair to use as address of group mint
const 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 added
maxSize: 10
});
// Get mint account size with group pointer extension
const spaceWithGroupPointerExtensions = BigInt(
getMintSize([groupPointerExtension])
);
// Get mint account size with group pointer and group extension
const spaceWithGroupAndGroupPointerExtensions = BigInt(
getMintSize([groupPointerExtension, groupExtension])
);
// Get rent exempt balance for group mint
const groupMintRent = await rpc
.getMinimumBalanceForRentExemption(spaceWithGroupAndGroupPointerExtensions)
.send();
// Get latest blockhash
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// GROUP MINT INSTRUCTIONS
const 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-style
mintAuthority: authority.address,
freezeAuthority: authority.address
});
const initializeGroupInstruction = getInitializeTokenGroupInstruction({
group: groupMint.address,
mint: groupMint.address,
mintAuthority: authority,
updateAuthority: authority.address,
maxSize: 10
});
// Build group mint transaction
const 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 mint
const 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 above
memberNumber: 1 // First member of the group
});
// Get mint account size with group pointer extension
const spaceWithMemberPointerExtension = BigInt(
getMintSize([groupPointerExtension])
);
// Get mint account size with group pointer and group extension
const spaceWithMemberAndMemberPointerExtensions = BigInt(
getMintSize([groupPointerExtension, groupExtension])
);
// Get rent exempt balance for member mint
const memberMintRent = await rpc
.getMinimumBalanceForRentExemption(spaceWithMemberAndMemberPointerExtensions)
.send();
// Get fresh blockhash for member mint transaction
const { value: memberLatestBlockhash } = await rpc.getLatestBlockhash().send();
// MEMBER MINT INSTRUCTIONS
const createMemberMintAccountInstruction = getCreateAccountInstruction({
payer: authority,
newAccount: memberMint,
lamports: memberMintRent,
space: spaceWithMemberPointerExtension, // Only pointer extension space for account creation
programAddress: 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-style
mintAuthority: 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 mint
groupUpdateAuthority: authority
});
// Build member mint transaction
const 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.

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 validator
let 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 payer
let 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 mint
let 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 INSTRUCTIONS
let create_group_mint_account_instruction = create_account(
&authority.pubkey(), // payer
&group_mint.pubkey(), // new account (mint)
group_mint_rent, // lamports
group_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(), // mint
Some(authority.pubkey()), // authority
Some(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 authority
Some(&authority.pubkey()), // freeze authority
0, // 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 authority
Some(authority.pubkey()), // update authority
10, // max size
);
// Build group mint transaction
let 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 transaction
let 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 mint
let 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 transaction
let member_latest_blockhash = client.get_latest_blockhash().await?;
// MEMBER MINT INSTRUCTIONS
let create_member_mint_account_instruction = create_account(
&authority.pubkey(), // payer
&member_mint.pubkey(), // new account (mint)
member_mint_rent, // lamports
member_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(), // mint
Some(authority.pubkey()), // authority
Some(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 authority
Some(&authority.pubkey()), // freeze authority
0, // 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 transaction
let 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 transaction
let 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?

Πίνακας Περιεχομένων

Επεξεργασία Σελίδας