---
title: Token Groups and Members
description:
  Create group and group member mints that store group data directly on the mint
  account using the GroupPointer, GroupMemberPointer, TokenGroup, and
  TokenGroupMember extensions.
url: /docs/tokens/extensions/group-member
type: tutorial
prerequisites:
  - /docs/tokens/basics/create-mint
related:
  - /docs/tokens/extensions/metadata
  - /docs/tokens/extensions
---

## What Are Group and Group Member Extensions?

A group is a mint that represents a collection. A group member is a mint that
belongs to that collection.

Use these extensions when one mint should represent the collection and other
mints should represent items that belong to it.

The Token Extension Program can store token group data directly on a mint with
four related extensions:

- _rs`GroupPointer`_ points a mint at the account that stores the group's data.
- _rs`TokenGroup`_ stores the group data itself, including the update authority,
  current size, and maximum size.
- _rs`GroupMemberPointer`_ points a mint at the account that stores the member's
  data.
- _rs`TokenGroupMember`_ stores the member's group address and member number.

_rs`GroupPointer`_ and _rs`GroupMemberPointer`_ can reference any account owned
by a program implementing the
[Token group interface](https://github.com/solana-program/token-group/tree/main/interface).

The Token Extension Program also implements that interface directly through the
_rs`TokenGroup`_ and _rs`TokenGroupMember`_ mint extensions.

## How to Create Groups and Members Stored on the Mint Account

To create groups and members stored on the mint account:

1. Create a group mint account and initialize _rs`GroupPointer`_.
2. Initialize the group mint with _rs`InitializeMint`_.
3. Initialize _rs`TokenGroup`_ on that same mint.
4. Create a member mint account and initialize _rs`GroupMemberPointer`_.
5. Initialize the member mint with _rs`InitializeMint`_.
6. Initialize _rs`TokenGroupMember`_ on that same mint so it references the
   group mint.

<ScrollyCoding>

## !!steps Calculate group mint size and rent

Calculate the size and rent needed for the group mint.

<CodePlaceholder title="Example" />

```ts !! title="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();

// !focus(1:16)
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();
```

## !!steps Create and Initialize Group Mint

Create the group mint account, initialize _rs`GroupPointer`_, initialize the
mint, and initialize _rs`TokenGroup`_ in one transaction.

<CodePlaceholder title="Example" />

```ts !! title="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();

// !focus(1:27)
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.
  }),
  // !mark(1:5)
  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: groupMint.address,
    mintAuthority: client.payer,
    updateAuthority: client.payer.address,
    maxSize: 10n
  })
]);
```

## !!steps Calculate member mint size and rent

Calculate the size and rent needed for the member mint.

<CodePlaceholder title="Example" />

```ts !! title="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();

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.
  }),
  // !mark(1:5)
  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: groupMint.address,
    mintAuthority: client.payer,
    updateAuthority: client.payer.address,
    maxSize: 10n
  })
]);

// !focus(1:16)
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();
```

## !!steps Create and Initialize Member Mint

Create the member mint account, initialize _rs`GroupMemberPointer`_, initialize
the mint, and initialize _rs`TokenGroupMember`_ in one transaction.

<CodePlaceholder title="Example" />

```ts !! title="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();

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.
  }),
  // !mark(1:5)
  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: groupMint.address,
    mintAuthority: client.payer,
    updateAuthority: client.payer.address,
    maxSize: 10n
  })
]);

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();

// !focus(1:27)
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.
  }),
  // !mark(1:5)
  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,
    memberMint: memberMint.address,
    memberMintAuthority: client.payer,
    group: groupMint.address,
    groupUpdateAuthority: client.payer
  })
]);
```

</ScrollyCoding>

<Callout type="info" title="Pointers and Instruction Order">
_rs`GroupPointer`_ and _rs`GroupMemberPointer`_ store the account address
where the group or member data lives. _rs`TokenGroup`_ and
_rs`TokenGroupMember`_ store the actual group or member data.

_rs`GroupPointerInstruction::Initialize`_ and
_rs`GroupMemberPointerInstruction::Initialize`_ must come before
_rs`InitializeMint`_. _rs`TokenGroupInstruction::InitializeGroup`_ and
_rs`TokenGroupInstruction::InitializeMember`_ must come after
_rs`InitializeMint`_. For each mint, _rs`CreateAccount`_, the pointer initialize
instruction, and _rs`InitializeMint`_ must be included in the same transaction.

</Callout>

### Source Reference

_rs`GroupPointer`_ and _rs`GroupMemberPointer`_ are pointer instructions in the
Token Extension Program. _rs`TokenGroup`_ and _rs`TokenGroupMember`_ follow the
Token group interface, which the Token Extension Program implements.

#### Group Pointer and Group Member Pointer

| Item                                            | Description                                                                                       | Source                                                                                                                                            |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| _rs`GroupPointer`_                              | Mint extension that stores the authority and address for the account that stores a group's data.  | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/group_pointer/mod.rs#L12-L25)                |
| _rs`GroupMemberPointer`_                        | Mint extension that stores the authority and address for the account that stores a member's data. | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/group_member_pointer/mod.rs#L12-L25)         |
| _rs`GroupPointerInstruction::Initialize`_       | Initializes the group pointer extension before _rs`InitializeMint`_.                              | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/group_pointer/instruction.rs#L22-L38)        |
| _rs`GroupPointerInstruction::Update`_           | Updates the group address stored by the mint's _rs`GroupPointer`_ extension.                      | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/group_pointer/instruction.rs#L39-L55)        |
| _rs`GroupMemberPointerInstruction::Initialize`_ | Initializes the group member pointer extension before _rs`InitializeMint`_.                       | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/group_member_pointer/instruction.rs#L22-L38) |
| _rs`GroupMemberPointerInstruction::Update`_     | Updates the member address stored by the mint's _rs`GroupMemberPointer`_ extension.               | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/interface/src/extension/group_member_pointer/instruction.rs#L39-L55) |
| _rs`process_initialize`_ (`GroupPointer`)       | Writes the initial _rs`GroupPointer`_ authority and group address onto the mint.                  | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/group_pointer/processor.rs#L25-L50)            |
| _rs`process_update`_ (`GroupPointer`)           | Validates the group pointer authority, then rewrites the mint's stored group address.             | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/group_pointer/processor.rs#L52-L78)            |
| _rs`process_initialize`_ (`GroupMemberPointer`) | Writes the initial _rs`GroupMemberPointer`_ authority and member address onto the mint.           | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/group_member_pointer/processor.rs#L25-L50)     |
| _rs`process_update`_ (`GroupMemberPointer`)     | Validates the group member pointer authority, then rewrites the mint's stored member address.     | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/group_member_pointer/processor.rs#L52-L78)     |

#### Token Group and Token Group Member

| Item                                              | Description                                                                                                                             | Source                                                                                                                                 |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| _rs`TokenGroup`_                                  | Token group interface state stored on the mint, including the update authority, current size, and maximum size.                         | [Source](https://github.com/solana-program/token-group/blob/main/interface/src/state.rs#L13-L27)                                       |
| _rs`TokenGroupMember`_                            | Token group member interface state stored on the mint, including the member mint, group address, and member number.                     | [Source](https://github.com/solana-program/token-group/blob/main/interface/src/state.rs#L64-L76)                                       |
| _rs`TokenGroupInstruction::InitializeGroup`_      | Token group interface instruction supported by the Token Extension Program to initialize a new group for an already initialized mint.   | [Source](https://github.com/solana-program/token-group/blob/main/interface/src/instruction.rs#L52-L62)                                 |
| _rs`TokenGroupInstruction::UpdateGroupMaxSize`_   | Token group interface instruction supported by the Token Extension Program to update the maximum number of members allowed in a group.  | [Source](https://github.com/solana-program/token-group/blob/main/interface/src/instruction.rs#L64-L70)                                 |
| _rs`TokenGroupInstruction::UpdateGroupAuthority`_ | Token group interface instruction supported by the Token Extension Program to rotate or clear the group's update authority.             | [Source](https://github.com/solana-program/token-group/blob/main/interface/src/instruction.rs#L72-L78)                                 |
| _rs`TokenGroupInstruction::InitializeMember`_     | Token group interface instruction supported by the Token Extension Program to initialize a new member for an already initialized group. | [Source](https://github.com/solana-program/token-group/blob/main/interface/src/instruction.rs#L80-L92)                                 |
| _rs`process_initialize_group`_                    | Validates the mint, checks that _rs`GroupPointer`_ is present, and allocates the _rs`TokenGroup`_ state on the group mint.              | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/token_group/processor.rs#L43-L90)   |
| _rs`process_update_group_max_size`_               | Validates the current update authority, then updates the group's maximum size.                                                          | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/token_group/processor.rs#L95-L117)  |
| _rs`process_update_group_authority`_              | Validates the current update authority, then rotates or clears the group's update authority.                                            | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/token_group/processor.rs#L119-L141) |
| _rs`process_initialize_member`_                   | Validates the member mint and group authority, increments the group size, and allocates the _rs`TokenGroupMember`_ state on the mint.   | [Source](https://github.com/solana-program/token-2022/blob/program%40v10.0.0/program/src/extension/token_group/processor.rs#L143-L202) |

### Typescript

The `Kit` example below uses the generated instructions directly. Legacy
examples using `@solana/web3.js`, `@solana/spl-token`, and the token group
helpers are included for reference.

#### Kit

<CodeTabs storage="token-ts-kit" flags="r">

```ts !! title="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.
  }),
  // !mark(1:7)
  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.
  }),
  // !mark(1:5)
  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.
  }),
  // !mark(1:7)
  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
  )
);
```

</CodeTabs>

#### Web3.js

<CodeTabs storage="token-ts-legacy" flags="r">

```ts !! title="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.
});

// !mark(1:7)
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.
);

// !mark(1:8)
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.
});

// !mark(1:7)
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.
);

// !mark(1:8)
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
  )
);
```

</CodeTabs>

### Rust

<CodeTabs storage="token-rs" flags="r">

```rust !! title="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.
    );

    // !mark(1:6)
    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.
    )?;

    // !mark(1:8)
    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.
    );

    // !mark(1:6)
    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.
    )?;

    // !mark(1:8)
    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(())
}
```

</CodeTabs>
