Группы токенов и участники

Как включить GroupPointerExtension и GroupMemberPointerExtension

GroupPointerExtension обозначает mint аккаунт как групповой mint (например, коллекция NFT), указывая на аккаунт, содержащий групповые конфигурации, такие как полномочия на обновление и максимальный размер.

GroupMemberPointerExtension обозначает mint аккаунт как участника группы (например, NFT в коллекции), указывая на аккаунт, содержащий данные об участниках, такие как адрес группы и номер участника.

Программа Token Extensions Program напрямую реализует интерфейс Token Group, позволяя хранить как данные группы, так и данные участников непосредственно в 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?

Содержание

Редактировать страницу