Anchor
Token2022 with Anchor

Token2022 with Anchor

The Group and Member Extension

The Group and Member extensions are Mint account extensions that introduce the ability to create groups, like collections for NFTs, that are linked with multiple assets.

Initializing the Mint Account

The Member and Group extension are a little different from what we're used to doing because it's composed of 2 different extensions that both go on a Mint account:

  • The Extension that contains all the information about the group or member.
  • The Pointer Extension that references the Mint account where the Group or Member extension lives.

Usually, when used, the Extension and the Pointer Extension live on the same Mint account; and we're going to do the same for this example.

The Group and Member extension can't be on the same account

Let's start with some basics before diving into the code:

While the GroupPointer and MemberPointer extension lives in the @solana/spl-token package, to initialize the Group and Member we need to use the @solana/spl-token-group package.

So let's install the required package:

npm i @solana/spl-token-group

Additionally, the Group and Member extension is one of the "only" extensions that requires you to initialize the extension after having initialized the Mint account.

This is because the metadata initialization instruction dynamically allocates the required space for the length of the group and member content.

At the same time, this means that we're going to need to initialize the Mint account with enough lamports to be rent exempt with the Group or Member extension included, but allocating enough space only for the GroupPointer or MemberPointer extension since the initializeGroup() and intializeMember() instruction actually increases the space correctly.

In the code initializing the Group looks like this:

const mint = Keypair.generate();
 
// Size of Mint Account with extensions
const mintLen = getMintLen([ExtensionType.GroupPointer]);
 
// Minimum lamports required for Mint Account
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen + TYPE_SIZE + LENGTH_SIZE + TOKEN_GROUP_SIZE);
 
const createAccountInstruction = SystemProgram.createAccount({
    fromPubkey: keypair.publicKey,
    newAccountPubkey: mint.publicKey,
    space: mintLen,
    lamports,
    programId: TOKEN_2022_PROGRAM_ID,
});
 
const initializeGroupPointer = createInitializeGroupPointerInstruction(
    mint.publicKey,
    keypair.publicKey,
    mint.publicKey,
    TOKEN_2022_PROGRAM_ID,
);
 
const initializeMintInstruction = createInitializeMintInstruction(
    mint.publicKey,
    6,
    keypair.publicKey,
    null,
    TOKEN_2022_PROGRAM_ID,
);
 
const initializeGroupInstruction = createInitializeGroupInstruction(
    {
        programId: TOKEN_2022_PROGRAM_ID,
        group: mint.publicKey,
        mint: mint.publicKey,
        mintAuthority: keypair.publicKey,
        updateAuthority: keypair.publicKey,
        maxSize: BigInt(100),
    }
);
 
const transaction = new Transaction().add(
    createAccountInstruction,
    initializeGroupPointer,
    initializeMintInstruction,
    initializeGroupInstruction,
);
 
const signature = await sendAndConfirmTransaction(connection, transaction, [keypair, mint], {commitment: "finalized"});
 
console.log(`Mint created! Check out your TX here: https://explorer.solana.com/tx/${signature}?cluster=devnet`);

And after this, we can use the group that we just created to add a member to it like this:

const member = Keypair.generate();
 
// Size of Member Account with extensions
const memberLen = getMintLen([ExtensionType.GroupMemberPointer]);
 
// Minimum lamports required for Member Account
const lamports = await connection.getMinimumBalanceForRentExemption(memberLen + TYPE_SIZE + LENGTH_SIZE + TOKEN_GROUP_MEMBER_SIZE);
 
const createAccountInstruction = SystemProgram.createAccount({
    fromPubkey: keypair.publicKey,
    newAccountPubkey: member.publicKey,
    space: memberLen,
    lamports,
    programId: TOKEN_2022_PROGRAM_ID,
});
 
const initializeGroupMemberPointer = createInitializeGroupMemberPointerInstruction(
    member.publicKey,
    keypair.publicKey,
    member.publicKey,
    TOKEN_2022_PROGRAM_ID,
);
 
const initializeMintInstruction = createInitializeMintInstruction(
    member.publicKey,
    6,
    keypair.publicKey,
    null,
    TOKEN_2022_PROGRAM_ID,
);
 
const initializeGroupMemberInstruction = createInitializeMemberInstruction(
    {
        programId: TOKEN_2022_PROGRAM_ID,
        group: mint.publicKey,
        member: member.publicKey,
        memberMint: member.publicKey,
        memberMintAuthority: keypair.publicKey,
        groupUpdateAuthority: keypair.publicKey,
    }
);
 
const transaction = new Transaction().add(
    createAccountInstruction,
    initializeGroupMemberPointer,
    initializeMintInstruction,
    initializeGroupMemberInstruction,
);
 
const signature = await sendAndConfirmTransaction(connection, transaction, [keypair, member], {commitment: "finalized"});
 
console.log(`Member created! Check out your TX here: https://explorer.solana.com/tx/${signature}?cluster=devnet`);

Updating the maxSize for Groups

As you can see, when we created the group, we allocated a maxSize field that will limit the maximum amount of Member that we can have in that specific group.

If we change idea, and we still have the updateAuthority of the group, we can use the updateGroupMaxSize() instruction to shrink or increase that number like so:

const updateGroupMaxSizeInstructions = createUpdateGroupMaxSizeInstruction(
    {
        programId: TOKEN_2022_PROGRAM_ID,
        group: mint.publicKey,
        updateAuthority: keypair.publicKey,
        maxSize: BigInt(100),
    }
);

Updating the updateAuthority for Groups

If we want to change the UpdateAuthority or make it immutable in order to not let anyone add any more Member to it, we can use the updateGroupAuthority() instruction like so:

const updateGroupAuthorityInstructions = createUpdateGroupAuthorityInstruction(
    {
        programId: TOKEN_2022_PROGRAM_ID,
        group: mint.publicKey,
        currentAuthority: keypair.publicKey,
        newAuthority: null,
    }
);
Contents
View Source
Blueshift © 2025Commit: 02aab65