> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getpara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage SPL Token Accounts

> Create, close, and manage SPL token accounts on Solana

Create and manage SPL token accounts for holding tokens on Solana. This includes creating Associated Token Accounts (ATAs) and closing empty accounts to reclaim SOL.

<Card title="Setup Solana Libraries First" description="You must complete the Solana library setup before managing token accounts" href="/v3/react/guides/web3-operations/solana/setup-libraries" horizontal />

<Tabs>
  <Tab title="@solana/kit">
    ```typescript theme={null}
    import { useParaSolanaSigner, useParaSolanaSignAndSend } from '@getpara/react-sdk';
    import {
      createSolanaRpc,
      pipe,
      createTransactionMessage,
      setTransactionMessageFeePayer,
      setTransactionMessageLifetimeUsingBlockhash,
      appendTransactionMessageInstruction,
      address
    } from '@solana/kit';
    import {
      getCreateAssociatedTokenAccountInstruction,
      findAssociatedTokenPda,
      getCloseAccountInstruction
    } from '@solana-program/token';

    const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");

    function ManageTokenAccounts() {
      const { solanaSigner, isLoading } = useParaSolanaSigner({ rpc });
      const { signAndSendAsync, isPending } = useParaSolanaSignAndSend(solanaSigner);

      const createTokenAccount = async (mint: string, owner?: string) => {
        if (!solanaSigner) return;

        const mintAddress = address(mint);
        const ownerAddress = owner ? address(owner) : solanaSigner.address;

        const [ata] = await findAssociatedTokenPda({
          mint: mintAddress,
          owner: ownerAddress,
        });

        const accountInfo = await rpc.getAccountInfo(ata).send();
        if (accountInfo.value) {
          console.log("Token account already exists:", ata);
          return ata;
        }

        const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();

        const transaction = pipe(
          createTransactionMessage({ version: 0 }),
          tx => setTransactionMessageFeePayer(solanaSigner.address, tx),
          tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
          tx => appendTransactionMessageInstruction(
            getCreateAssociatedTokenAccountInstruction({
              ata,
              mint: mintAddress,
              owner: ownerAddress,
              payer: solanaSigner,
            }),
            tx
          )
        );

        try {
          const result = await signAndSendAsync({ transactions: [transaction] });
          console.log("Created token account:", ata);
          console.log("Transaction signature:", result);
          return ata;
        } catch (error) {
          console.error("Failed to create token account:", error);
          throw error;
        }
      };

      const closeTokenAccount = async (tokenAccount: string) => {
        if (!solanaSigner) return;

        const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();

        const transaction = pipe(
          createTransactionMessage({ version: 0 }),
          tx => setTransactionMessageFeePayer(solanaSigner.address, tx),
          tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
          tx => appendTransactionMessageInstruction(
            getCloseAccountInstruction({
              account: address(tokenAccount),
              destination: solanaSigner.address,
              authority: solanaSigner,
            }),
            tx
          )
        );

        try {
          const result = await signAndSendAsync({ transactions: [transaction] });
          console.log("Closed token account:", tokenAccount);
          console.log("Transaction signature:", result);
          return result;
        } catch (error) {
          console.error("Failed to close token account:", error);
          throw error;
        }
      };

      return (
        <div>
          <button onClick={() => createTokenAccount("USDC_MINT_ADDRESS")}>
            Create USDC Account
          </button>
          <button onClick={() => closeTokenAccount("TOKEN_ACCOUNT_ADDRESS")}>
            Close Token Account
          </button>
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="@solana/web3.js">
    ```typescript theme={null}
    import { useParaSolana } from './hooks/useParaSolana';
    import { Transaction, PublicKey } from '@solana/web3.js';
    import {
      createAssociatedTokenAccountInstruction,
      getAssociatedTokenAddress,
      createCloseAccountInstruction,
      TOKEN_PROGRAM_ID,
      ASSOCIATED_TOKEN_PROGRAM_ID
    } from '@solana/spl-token';

    function ManageTokenAccounts() {
      const { connection, signer } = useParaSolana();

      const createTokenAccount = async (mint: string, owner?: string) => {
        const mintPubkey = new PublicKey(mint);
        const ownerPubkey = owner ? new PublicKey(owner) : signer.sender;

        const ata = await getAssociatedTokenAddress(mintPubkey, ownerPubkey);

        const accountInfo = await connection.getAccountInfo(ata);
        if (accountInfo) {
          console.log("Token account already exists:", ata.toString());
          return ata;
        }

        const transaction = new Transaction().add(
          createAssociatedTokenAccountInstruction(
            signer.sender,
            ata,
            ownerPubkey,
            mintPubkey,
            TOKEN_PROGRAM_ID,
            ASSOCIATED_TOKEN_PROGRAM_ID
          )
        );

        try {
          const signature = await signer.sendTransaction(transaction);
          console.log("Created token account:", ata.toString());
          console.log("Transaction signature:", signature);
          return ata;
        } catch (error) {
          console.error("Failed to create token account:", error);
          throw error;
        }
      };

      const closeTokenAccount = async (tokenAccount: string) => {
        const tokenAccountPubkey = new PublicKey(tokenAccount);

        const transaction = new Transaction().add(
          createCloseAccountInstruction(
            tokenAccountPubkey,
            signer.sender,
            signer.sender,
            [],
            TOKEN_PROGRAM_ID
          )
        );

        try {
          const signature = await signer.sendTransaction(transaction);
          console.log("Closed token account:", tokenAccount);
          console.log("Transaction signature:", signature);
          return signature;
        } catch (error) {
          console.error("Failed to close token account:", error);
          throw error;
        }
      };

      return (
        <div>
          <button onClick={() => createTokenAccount("USDC_MINT_ADDRESS")}>
            Create USDC Account
          </button>
          <button onClick={() => closeTokenAccount("TOKEN_ACCOUNT_ADDRESS")}>
            Close Token Account
          </button>
        </div>
      );
    }
    ```
  </Tab>

  <Tab title="Anchor">
    Token account management uses `@solana-program/token` with Codama-generated helpers:

    ```typescript theme={null}
    import { useParaSolanaSigner } from "@getpara/react-sdk";
    import { createSolanaRpc } from "@solana/kit";
    import { pipe, createTransactionMessage, setTransactionMessageFeePayerSigner,
      setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstruction,
      signAndSendTransactionMessageWithSigners } from "@solana/kit";
    import { getCreateAssociatedTokenInstruction, findAssociatedTokenPda } from "@solana-program/token";

    const rpc = createSolanaRpc("https://api.devnet.solana.com");

    function CreateTokenAccount() {
      const { solanaSigner, isLoading } = useParaSolanaSigner({ rpc });

      const createAccount = async (mintAddress: string) => {
        if (!solanaSigner) return;

        const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();

        const [ata] = await findAssociatedTokenPda({
          mint: mintAddress as any,
          owner: solanaSigner.address as any,
        });

        const instruction = getCreateAssociatedTokenInstruction({
          payer: solanaSigner,
          owner: solanaSigner.address as any,
          mint: mintAddress as any,
          ata,
        });

        const tx = pipe(
          createTransactionMessage({ version: 0 }),
          tx => setTransactionMessageFeePayerSigner(solanaSigner, tx),
          tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
          tx => appendTransactionMessageInstruction(instruction, tx),
        );

        const signature = await signAndSendTransactionMessageWithSigners(tx);
        console.log("Token account created:", signature);
      };

      if (isLoading) return <p>Loading...</p>;
      return <button onClick={() => createAccount("mint-address...")}>Create Token Account</button>;
    }
    ```
  </Tab>
</Tabs>

## Next Steps

<CardGroup cols={3}>
  <Card title="Send Tokens" description="Transfer SPL tokens between accounts" href="/v3/react/guides/web3-operations/solana/send-tokens" icon="paper-plane" />

  <Card title="Query Balances" description="Check token account balances" href="/v3/react/guides/web3-operations/solana/query-balances" icon="wallet" />

  <Card title="Interact with Programs" description="Call Solana programs" href="/v3/react/guides/web3-operations/solana/interact-with-programs" icon="code" />
</CardGroup>
