> ## 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

export const Card = ({imgUrl, title, description, href, horizontal = false, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  const handleClick = e => {
    e.preventDefault();
    if (newTab) {
      window.open(href, '_blank', 'noopener,noreferrer');
    } else {
      window.location.href = href;
    }
  };
  return <div className={`not-prose relative my-2 p-[1px] rounded-xl transition-all duration-300 ${isHovered ? 'bg-gradient-to-r from-[#FF4E00] to-[#874AE3]' : 'bg-gray-200'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <a href={href} onClick={handleClick} className={`not-prose flex ${horizontal ? 'flex-row' : 'flex-col'} font-normal h-full bg-white overflow-hidden w-full cursor-pointer rounded-[11px] no-underline`}>
        {imgUrl && <div className={`relative overflow-hidden flex-shrink-0 ${horizontal ? 'w-[30%] rounded-l-[11px]' : 'w-full'}`} onClick={e => e.stopPropagation()}>
            <img src={imgUrl} alt={title} className="w-full h-full object-cover pointer-events-none select-none" draggable="false" />
            <div className="absolute inset-0 pointer-events-none" />
          </div>}
        <div className={`flex-grow px-6 py-5 ${horizontal ? 'w-[70%]' : 'w-full'} flex flex-col ${horizontal && imgUrl ? 'justify-center' : 'justify-start'}`}>
          {title && <h2 className="font-semibold text-base text-gray-800 m-0">{title}</h2>}
          {description && <div className={`font-normal text-gray-500 re leading-6 ${horizontal || !imgUrl ? 'mt-0' : 'mt-1'}`}>
              <p className="m-0 text-xs">{description}</p>
            </div>}
        </div>
      </a>
    </div>;
};

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="/v2/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="/v2/react/guides/web3-operations/solana/send-tokens" icon="paper-plane" />

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

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