토큰 그룹 및 멤버

그룹 및 그룹 멤버 확장이란?

그룹은 컬렉션을 나타내는 민트입니다. 그룹 멤버는 해당 컬렉션에 속하는 민트입니다.

하나의 민트가 컬렉션을 나타내고 다른 민트들이 그 컬렉션에 속한 항목들을 나타내야 할 때 이러한 확장을 사용합니다.

Token Extensions Program은 네 가지 관련 확장을 통해 토큰 그룹 데이터를 민트에 직접 저장할 수 있습니다:

  • *rsGroupPointer*는 민트를 그룹 데이터가 저장된 계정으로 연결합니다.
  • *rsTokenGroup*는 업데이트 권한, 현재 크기, 최대 크기를 포함한 그룹 데이터 자체를 저장합니다.
  • *rsGroupMemberPointer*는 민트를 멤버 데이터가 저장된 계정으로 연결합니다.
  • *rsTokenGroupMember*는 멤버의 그룹 주소와 멤버 번호를 저장합니다.

*rsGroupPointer*와 *rsGroupMemberPointer*는 Token group interface를 구현하는 프로그램이 소유한 모든 계정을 참조할 수 있습니다.

Token Extensions Program은 또한 TokenGroupTokenGroupMember 민트 확장을 통해 해당 인터페이스를 직접 구현합니다.

mint account에 저장된 그룹 및 멤버를 생성하는 방법

mint account에 저장된 그룹 및 멤버를 생성하려면:

  1. 그룹 mint account를 생성하고 *rsGroupPointer*를 초기화합니다.
  2. *rsInitializeMint*로 그룹 민트를 초기화합니다.
  3. 동일한 민트에 *rsTokenGroup*를 초기화합니다.
  4. 멤버 mint account를 생성하고 *rsGroupMemberPointer*를 초기화합니다.
  5. *rsInitializeMint*로 멤버 민트를 초기화합니다.
  6. 그룹 민트를 참조하도록 동일한 민트에 *rsTokenGroupMember*를 초기화합니다.

그룹 민트 크기 및 렌트 계산

그룹 민트에 필요한 크기와 렌트를 계산합니다.

그룹 민트 생성 및 초기화

그룹 mint account를 생성하고, *rsGroupPointer*를 초기화하며, mint를 초기화하고, 한 트랜잭션에서 *rsTokenGroup*를 초기화합니다.

멤버 민트 크기 및 렌트 계산

멤버 민트에 필요한 크기와 렌트를 계산합니다.

멤버 민트 생성 및 초기화

멤버 mint account를 생성하고, *rsGroupMemberPointer*를 초기화하며, mint를 초기화하고, 한 트랜잭션에서 *rsTokenGroupMember*를 초기화합니다.

그룹 민트 크기 및 렌트 계산

그룹 민트에 필요한 크기와 렌트를 계산합니다.

그룹 민트 생성 및 초기화

그룹 mint account를 생성하고, *rsGroupPointer*를 초기화하며, mint를 초기화하고, 한 트랜잭션에서 *rsTokenGroup*를 초기화합니다.

멤버 민트 크기 및 렌트 계산

멤버 민트에 필요한 크기와 렌트를 계산합니다.

멤버 민트 생성 및 초기화

멤버 mint account를 생성하고, *rsGroupMemberPointer*를 초기화하며, mint를 초기화하고, 한 트랜잭션에서 *rsTokenGroupMember*를 초기화합니다.

Example
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const groupMint = await generateKeyPairSigner();
const memberMint = await generateKeyPairSigner();
const groupPointerExtension = extension("GroupPointer", {
authority: client.payer.address,
groupAddress: groupMint.address
});
const groupExtension = extension("TokenGroup", {
updateAuthority: client.payer.address,
mint: groupMint.address,
size: 0n,
maxSize: 10n
});
const groupMintSpace = BigInt(
getMintSize([groupPointerExtension, groupExtension])
);
const groupMintCreateSpace = BigInt(getMintSize([groupPointerExtension]));
const groupMintRent = await client.rpc
.getMinimumBalanceForRentExemption(groupMintSpace)
.send();

포인터 및 명령어 순서

_rsGroupPointer_와 _rsGroupMemberPointer_는 그룹 또는 멤버 데이터가 저장된 계정 주소를 저장합니다. _rsTokenGroup_와 _rsTokenGroupMember_는 실제 그룹 또는 멤버 데이터를 저장합니다.

*rsGroupPointerInstruction::Initialize*와 *rsGroupMemberPointerInstruction::Initialize*는 InitializeMint 이전에 와야 합니다. *rsTokenGroupInstruction::InitializeGroup*와 *rsTokenGroupInstruction::InitializeMember*는 InitializeMint 이후에 와야 합니다. 각 민트에 대해 CreateAccount, 포인터 초기화 명령어, 그리고 *rsInitializeMint*는 동일한 트랜잭션에 포함되어야 합니다.

소스 참조

*rsGroupPointer*와 *rsGroupMemberPointer*는 Token Extensions Program의 포인터 명령어입니다. *rsTokenGroup*와 *rsTokenGroupMember*는 Token Extensions Program이 구현하는 Token group 인터페이스를 따릅니다.

그룹 포인터 및 그룹 멤버 포인터

항목설명소스
GroupPointer그룹 데이터를 저장하는 계정의 권한 및 주소를 저장하는 민트 확장입니다.소스
GroupMemberPointer멤버 데이터를 저장하는 계정의 권한 및 주소를 저장하는 민트 확장입니다.소스
GroupPointerInstruction::InitializeInitializeMint 이전에 그룹 포인터 확장을 초기화합니다.소스
GroupPointerInstruction::Update민트의 GroupPointer 확장에 저장된 그룹 주소를 업데이트합니다.소스
GroupMemberPointerInstruction::InitializeInitializeMint 이전에 그룹 멤버 포인터 확장을 초기화합니다.소스
GroupMemberPointerInstruction::Update민트의 GroupMemberPointer 확장에 저장된 멤버 주소를 업데이트합니다.소스
process_initialize (GroupPointer)초기 GroupPointer 권한과 그룹 주소를 민트에 기록합니다.소스
process_update (GroupPointer)그룹 포인터 권한을 검증한 다음 민트에 저장된 그룹 주소를 다시 기록합니다.소스
process_initialize (GroupMemberPointer)초기 GroupMemberPointer 권한과 멤버 주소를 민트에 기록합니다.소스
process_update (GroupMemberPointer)그룹 멤버 포인터 권한을 검증한 다음 민트에 저장된 멤버 주소를 다시 기록합니다.소스

토큰 그룹 및 토큰 그룹 멤버

항목설명출처
TokenGroup업데이트 권한, 현재 크기 및 최대 크기를 포함하여 민트에 저장된 토큰 그룹 인터페이스 상태입니다.출처
TokenGroupMember멤버 민트, 그룹 주소 및 멤버 번호를 포함하여 민트에 저장된 토큰 그룹 멤버 인터페이스 상태입니다.출처
TokenGroupInstruction::InitializeGroup이미 초기화된 민트에 대해 새 그룹을 초기화하기 위해 Token Extensions Program에서 지원하는 토큰 그룹 인터페이스 명령입니다.출처
TokenGroupInstruction::UpdateGroupMaxSize그룹에 허용되는 최대 멤버 수를 업데이트하기 위해 Token Extensions Program에서 지원하는 토큰 그룹 인터페이스 명령입니다.출처
TokenGroupInstruction::UpdateGroupAuthority그룹의 업데이트 권한을 교체하거나 제거하기 위해 Token Extensions Program에서 지원하는 토큰 그룹 인터페이스 명령입니다.출처
TokenGroupInstruction::InitializeMember이미 초기화된 그룹에 대해 새 멤버를 초기화하기 위해 Token Extensions Program에서 지원하는 토큰 그룹 인터페이스 명령입니다.출처
process_initialize_group민트를 검증하고 *rsGroupPointer*가 존재하는지 확인한 후 그룹 민트에 TokenGroup 상태를 할당합니다.출처
process_update_group_max_size현재 업데이트 권한을 검증한 후 그룹의 최대 크기를 업데이트합니다.출처
process_update_group_authority현재 업데이트 권한을 검증한 후 그룹의 업데이트 권한을 교체하거나 제거합니다.출처
process_initialize_member멤버 민트와 그룹 권한을 검증하고 그룹 크기를 증가시킨 후 민트에 TokenGroupMember 상태를 할당합니다.출처

Typescript

아래의 Kit 예제는 생성된 명령어를 직접 사용합니다. @solana/web3.js, @solana/spl-token 및 토큰 그룹 헬퍼를 사용하는 레거시 예제는 참고용으로 포함되어 있습니다.

Kit

Instructions
import {
lamports,
createClient,
generateKeyPairSigner,
unwrapOption
} from "@solana/kit";
import { solanaRpc, rpcAirdrop } from "@solana/kit-plugin-rpc";
import { generatedPayer, airdropPayer } from "@solana/kit-plugin-signer";
import { getCreateAccountInstruction } from "@solana-program/system";
import {
extension,
fetchMint,
getInitializeGroupMemberPointerInstruction,
getInitializeGroupPointerInstruction,
getInitializeMintInstruction,
getInitializeTokenGroupInstruction,
getInitializeTokenGroupMemberInstruction,
getMintSize,
isExtension,
TOKEN_2022_PROGRAM_ADDRESS
} from "@solana-program/token-2022";
const client = await createClient()
.use(generatedPayer())
.use(
solanaRpc({
rpcUrl: "http://localhost:8899",
rpcSubscriptionsUrl: "ws://localhost:8900"
})
)
.use(rpcAirdrop())
.use(airdropPayer(lamports(1_000_000_000n)));
const groupMint = await generateKeyPairSigner();
const memberMint = await generateKeyPairSigner();
const groupPointerExtension = extension("GroupPointer", {
authority: client.payer.address,
groupAddress: groupMint.address
});
const groupExtension = extension("TokenGroup", {
updateAuthority: client.payer.address,
mint: groupMint.address,
size: 0n,
maxSize: 10n
});
const groupMintSpace = BigInt(
getMintSize([groupPointerExtension, groupExtension])
);
const groupMintCreateSpace = BigInt(getMintSize([groupPointerExtension]));
const groupMintRent = await client.rpc
.getMinimumBalanceForRentExemption(groupMintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer, // Account funding the new mint account.
newAccount: groupMint, // New group mint account to create.
lamports: groupMintRent, // Lamports funding the mint account rent.
space: groupMintCreateSpace, // Account size in bytes for the mint plus GroupPointer.
programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.
}),
getInitializeGroupPointerInstruction({
mint: groupMint.address, // Mint account that stores the GroupPointer extension.
authority: client.payer.address, // Authority allowed to update the group pointer later.
groupAddress: groupMint.address // Account address that stores the group data.
}),
getInitializeMintInstruction({
mint: groupMint.address, // Mint account to initialize.
decimals: 0, // Number of decimals for the token.
mintAuthority: client.payer.address, // Authority allowed to mint new tokens.
freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.
}),
getInitializeTokenGroupInstruction({
group: groupMint.address, // Mint account that stores the group data.
mint: groupMint.address, // Mint that the group data describes.
mintAuthority: client.payer, // Signer authorizing group initialization for the mint.
updateAuthority: client.payer.address, // Authority allowed to update the group later.
maxSize: 10n // Maximum number of members allowed in the group.
})
]);
const memberPointerExtension = extension("GroupMemberPointer", {
authority: client.payer.address,
memberAddress: memberMint.address
});
const memberExtension = extension("TokenGroupMember", {
mint: memberMint.address,
group: groupMint.address,
memberNumber: 1n
});
const memberMintSpace = BigInt(
getMintSize([memberPointerExtension, memberExtension])
);
const memberMintCreateSpace = BigInt(getMintSize([memberPointerExtension]));
const memberMintRent = await client.rpc
.getMinimumBalanceForRentExemption(memberMintSpace)
.send();
await client.sendTransaction([
getCreateAccountInstruction({
payer: client.payer, // Account funding the new mint account.
newAccount: memberMint, // New member mint account to create.
lamports: memberMintRent, // Lamports funding the mint account rent.
space: memberMintCreateSpace, // Account size in bytes for the mint plus GroupMemberPointer.
programAddress: TOKEN_2022_PROGRAM_ADDRESS // Program that owns the mint account.
}),
getInitializeGroupMemberPointerInstruction({
mint: memberMint.address, // Mint account that stores the GroupMemberPointer extension.
authority: client.payer.address, // Authority allowed to update the member pointer later.
memberAddress: memberMint.address // Account address that stores the member data.
}),
getInitializeMintInstruction({
mint: memberMint.address, // Mint account to initialize.
decimals: 0, // Number of decimals for the token.
mintAuthority: client.payer.address, // Authority allowed to mint new tokens.
freezeAuthority: client.payer.address // Authority allowed to freeze token accounts.
}),
getInitializeTokenGroupMemberInstruction({
member: memberMint.address, // Mint account that stores the member data.
memberMint: memberMint.address, // Mint that the member data describes.
memberMintAuthority: client.payer, // Signer authorizing member initialization for the mint.
group: groupMint.address, // Group mint that this member belongs to.
groupUpdateAuthority: client.payer // Signer matching the group's update authority.
})
]);
const groupMintAccount = await fetchMint(client.rpc, groupMint.address);
const memberMintAccount = await fetchMint(client.rpc, memberMint.address);
const groupExtensions = unwrapOption(groupMintAccount.data.extensions) ?? [];
const memberExtensions = unwrapOption(memberMintAccount.data.extensions) ?? [];
console.log(
JSON.stringify(
{
groupMint: groupMint.address,
groupPointer: groupExtensions.find((item) =>
isExtension("GroupPointer", item)
),
group: groupExtensions.find((item) => isExtension("TokenGroup", item)),
memberMint: memberMint.address,
memberPointer: memberExtensions.find((item) =>
isExtension("GroupMemberPointer", item)
),
member: memberExtensions.find((item) =>
isExtension("TokenGroupMember", item)
)
},
(_, value) => (typeof value === "bigint" ? value.toString() : value),
2
)
);
Console
Click to execute the code.

Web3.js

Instructions
import {
Connection,
Keypair,
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
sendAndConfirmTransaction
} from "@solana/web3.js";
import {
TOKEN_2022_PROGRAM_ID,
ExtensionType,
getMintLen,
getMint,
createInitializeMintInstruction,
createInitializeGroupPointerInstruction,
createInitializeGroupInstruction,
createInitializeGroupMemberPointerInstruction,
createInitializeMemberInstruction,
getGroupPointerState,
getGroupMemberPointerState,
getTokenGroupState,
getTokenGroupMemberState
} from "@solana/spl-token";
const connection = new Connection("http://localhost:8899", "confirmed");
const authority = Keypair.generate();
const airdropSignature = await connection.requestAirdrop(
authority.publicKey,
5 * LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature, "confirmed");
const groupMint = Keypair.generate();
const groupPointerExtensions = [ExtensionType.GroupPointer];
const spaceWithGroupPointerExtensions = getMintLen(groupPointerExtensions);
const groupAndGroupPointerExtensions = [
ExtensionType.GroupPointer,
ExtensionType.TokenGroup
];
const spaceWithGroupAndGroupPointerExtensions = getMintLen(
groupAndGroupPointerExtensions
);
const groupMintRent = await connection.getMinimumBalanceForRentExemption(
spaceWithGroupAndGroupPointerExtensions
);
const { blockhash: latestBlockhash } = await connection.getLatestBlockhash();
const createGroupMintAccountInstruction = SystemProgram.createAccount({
fromPubkey: authority.publicKey, // Account funding the new mint account.
newAccountPubkey: groupMint.publicKey, // New group mint account to create.
lamports: groupMintRent, // Lamports funding the mint account rent.
space: spaceWithGroupPointerExtensions, // Account size in bytes for the mint plus GroupPointer.
programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
});
const initializeGroupPointerInstruction =
createInitializeGroupPointerInstruction(
groupMint.publicKey, // Mint account that stores the GroupPointer extension.
authority.publicKey, // Authority allowed to update the group pointer later.
groupMint.publicKey, // Account address that stores the group data.
TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
);
const initializeGroupMintInstruction = createInitializeMintInstruction(
groupMint.publicKey, // Mint account to initialize.
0, // Number of decimals for the token.
authority.publicKey, // Authority allowed to mint new tokens.
authority.publicKey, // Authority allowed to freeze token accounts.
TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
);
const initializeGroupInstruction = createInitializeGroupInstruction({
programId: TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
group: groupMint.publicKey, // Mint account that stores the group data.
mint: groupMint.publicKey, // Mint that the group data describes.
mintAuthority: authority.publicKey, // Signer authorizing group initialization for the mint.
updateAuthority: authority.publicKey, // Authority allowed to update the group later.
maxSize: 10n // Maximum number of members allowed in the group.
});
const groupTransaction = new Transaction({
feePayer: authority.publicKey,
recentBlockhash: latestBlockhash
}).add(
createGroupMintAccountInstruction,
initializeGroupPointerInstruction,
initializeGroupMintInstruction,
initializeGroupInstruction
);
await sendAndConfirmTransaction(
connection,
groupTransaction,
[authority, groupMint],
{
commitment: "confirmed",
skipPreflight: true
}
);
const memberMint = Keypair.generate();
const memberPointerExtensions = [ExtensionType.GroupMemberPointer];
const spaceWithMemberPointerExtension = getMintLen(memberPointerExtensions);
const memberAndMemberPointerExtensions = [
ExtensionType.GroupMemberPointer,
ExtensionType.TokenGroupMember
];
const spaceWithMemberAndMemberPointerExtensions = getMintLen(
memberAndMemberPointerExtensions
);
const memberMintRent = await connection.getMinimumBalanceForRentExemption(
spaceWithMemberAndMemberPointerExtensions
);
const { blockhash: memberLatestBlockhash } =
await connection.getLatestBlockhash();
const createMemberMintAccountInstruction = SystemProgram.createAccount({
fromPubkey: authority.publicKey, // Account funding the new mint account.
newAccountPubkey: memberMint.publicKey, // New member mint account to create.
lamports: memberMintRent, // Lamports funding the mint account rent.
space: spaceWithMemberPointerExtension, // Account size in bytes for the mint plus GroupMemberPointer.
programId: TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
});
const initializeMemberPointerInstruction =
createInitializeGroupMemberPointerInstruction(
memberMint.publicKey, // Mint account that stores the GroupMemberPointer extension.
authority.publicKey, // Authority allowed to update the member pointer later.
memberMint.publicKey, // Account address that stores the member data.
TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
);
const initializeMemberMintInstruction = createInitializeMintInstruction(
memberMint.publicKey, // Mint account to initialize.
0, // Number of decimals for the token.
authority.publicKey, // Authority allowed to mint new tokens.
authority.publicKey, // Authority allowed to freeze token accounts.
TOKEN_2022_PROGRAM_ID // Program that owns the mint account.
);
const initializeMemberInstruction = createInitializeMemberInstruction({
programId: TOKEN_2022_PROGRAM_ID, // Token program that owns the mint.
member: memberMint.publicKey, // Mint account that stores the member data.
memberMint: memberMint.publicKey, // Mint that the member data describes.
memberMintAuthority: authority.publicKey, // Signer authorizing member initialization for the mint.
group: groupMint.publicKey, // Group mint that this member belongs to.
groupUpdateAuthority: authority.publicKey // Signer matching the group's update authority.
});
const memberTransaction = new Transaction({
feePayer: authority.publicKey,
recentBlockhash: memberLatestBlockhash
}).add(
createMemberMintAccountInstruction,
initializeMemberPointerInstruction,
initializeMemberMintInstruction,
initializeMemberInstruction
);
await sendAndConfirmTransaction(
connection,
memberTransaction,
[authority, memberMint],
{
commitment: "confirmed",
skipPreflight: true
}
);
const groupMintAccount = await getMint(
connection,
groupMint.publicKey,
"confirmed",
TOKEN_2022_PROGRAM_ID
);
const memberMintAccount = await getMint(
connection,
memberMint.publicKey,
"confirmed",
TOKEN_2022_PROGRAM_ID
);
console.log(
JSON.stringify(
{
groupMint: groupMint.publicKey,
groupPointer: getGroupPointerState(groupMintAccount),
group: getTokenGroupState(groupMintAccount),
memberMint: memberMint.publicKey,
memberPointer: getGroupMemberPointerState(memberMintAccount),
member: getTokenGroupMemberState(memberMintAccount)
},
(_, value) => (typeof value === "bigint" ? value.toString() : value),
2
)
);
Console
Click to execute the code.

Rust

Rust
use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_commitment_config::CommitmentConfig;
use solana_sdk::{
signature::{Keypair, Signer},
transaction::Transaction,
};
use solana_system_interface::instruction::create_account;
use spl_token_2022_interface::{
extension::{
group_member_pointer::{
instruction::initialize as initialize_group_member_pointer, GroupMemberPointer,
},
group_pointer::{instruction::initialize as initialize_group_pointer, GroupPointer},
BaseStateWithExtensions, ExtensionType, StateWithExtensions,
},
instruction::initialize_mint,
state::Mint,
ID as TOKEN_2022_PROGRAM_ID,
};
use spl_token_group_interface::{
instruction::{initialize_group, initialize_member},
state::{TokenGroup, TokenGroupMember},
};
#[tokio::main]
async fn main() -> Result<()> {
let client = RpcClient::new_with_commitment(
String::from("http://localhost:8899"),
CommitmentConfig::confirmed(),
);
let authority = Keypair::new();
let airdrop_signature = client
.request_airdrop(&authority.pubkey(), 5_000_000_000)
.await?;
loop {
let confirmed = client.confirm_transaction(&airdrop_signature).await?;
if confirmed {
break;
}
}
let group_mint = Keypair::new();
let group_mint_space =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::GroupPointer])?;
let group_mint_space_with_data = ExtensionType::try_calculate_account_len::<Mint>(&[
ExtensionType::GroupPointer,
ExtensionType::TokenGroup,
])?;
let group_mint_rent = client
.get_minimum_balance_for_rent_exemption(group_mint_space_with_data)
.await?;
let create_group_mint_account_instruction = create_account(
&authority.pubkey(), // Account funding the new mint account.
&group_mint.pubkey(), // New group mint account to create.
group_mint_rent, // Lamports funding the mint account rent.
group_mint_space as u64, // Account size in bytes for the mint plus GroupPointer.
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
);
let initialize_group_pointer_instruction = initialize_group_pointer(
&TOKEN_2022_PROGRAM_ID,
&group_mint.pubkey(), // Mint account that stores the GroupPointer extension.
Some(authority.pubkey()), // Authority allowed to update the group pointer later.
Some(group_mint.pubkey()), // Account address that stores the group data.
)?;
let initialize_group_mint_instruction = initialize_mint(
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
&group_mint.pubkey(), // Mint account to initialize.
&authority.pubkey(), // Authority allowed to mint new tokens.
Some(&authority.pubkey()), // Authority allowed to freeze token accounts.
0, // Number of decimals for the token.
)?;
let initialize_group_instruction = initialize_group(
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
&group_mint.pubkey(), // Mint account that stores the group data.
&group_mint.pubkey(), // Mint that the group data describes.
&authority.pubkey(), // Signer authorizing group initialization for the mint.
Some(authority.pubkey()), // Authority allowed to update the group later.
10, // Maximum number of members allowed in the group.
);
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],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&group_transaction)
.await?;
let member_mint = Keypair::new();
let member_mint_space =
ExtensionType::try_calculate_account_len::<Mint>(&[ExtensionType::GroupMemberPointer])?;
let member_mint_space_with_data = ExtensionType::try_calculate_account_len::<Mint>(&[
ExtensionType::GroupMemberPointer,
ExtensionType::TokenGroupMember,
])?;
let member_mint_rent = client
.get_minimum_balance_for_rent_exemption(member_mint_space_with_data)
.await?;
let create_member_mint_account_instruction = create_account(
&authority.pubkey(), // Account funding the new mint account.
&member_mint.pubkey(), // New member mint account to create.
member_mint_rent, // Lamports funding the mint account rent.
member_mint_space as u64, // Account size in bytes for the mint plus GroupMemberPointer.
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
);
let initialize_member_pointer_instruction = initialize_group_member_pointer(
&TOKEN_2022_PROGRAM_ID,
&member_mint.pubkey(), // Mint account that stores the GroupMemberPointer extension.
Some(authority.pubkey()), // Authority allowed to update the member pointer later.
Some(member_mint.pubkey()), // Account address that stores the member data.
)?;
let initialize_member_mint_instruction = initialize_mint(
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
&member_mint.pubkey(), // Mint account to initialize.
&authority.pubkey(), // Authority allowed to mint new tokens.
Some(&authority.pubkey()), // Authority allowed to freeze token accounts.
0, // Number of decimals for the token.
)?;
let initialize_member_instruction = initialize_member(
&TOKEN_2022_PROGRAM_ID, // Program that owns the mint account.
&member_mint.pubkey(), // Mint account that stores the member data.
&member_mint.pubkey(), // Mint that the member data describes.
&authority.pubkey(), // Signer authorizing member initialization for the mint.
&group_mint.pubkey(), // Group mint that this member belongs to.
&authority.pubkey(), // Signer matching the group's update authority.
);
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],
client.get_latest_blockhash().await?,
);
client
.send_and_confirm_transaction(&member_transaction)
.await?;
let group_mint_account = client.get_account(&group_mint.pubkey()).await?;
let group_mint_state = StateWithExtensions::<Mint>::unpack(&group_mint_account.data)?;
let group_pointer = group_mint_state.get_extension::<GroupPointer>()?;
let token_group = group_mint_state.get_extension::<TokenGroup>()?;
let member_mint_account = client.get_account(&member_mint.pubkey()).await?;
let member_mint_state = StateWithExtensions::<Mint>::unpack(&member_mint_account.data)?;
let member_pointer = member_mint_state.get_extension::<GroupMemberPointer>()?;
let token_group_member = member_mint_state.get_extension::<TokenGroupMember>()?;
println!("\nGroup Mint: {}", group_mint.pubkey());
println!("\nGroup Pointer: {:#?}", group_pointer);
println!("\nToken Group: {:#?}", token_group);
println!("\nMember Mint: {}", member_mint.pubkey());
println!("\nGroup Member Pointer: {:#?}", member_pointer);
println!("\nToken Group Member: {:#?}", token_group_member);
Ok(())
}
Console
Click to execute the code.

Is this page helpful?

목차

페이지 편집

관리자

© 2026 솔라나 재단.
모든 권리 보유.
연결하기