Skip to main content

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.

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.
import { useParaSolanaSigner, useParaSolanaSignAndSend } from "@getpara/react-native-wallet/solana";
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 transactionMessage = 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 signature = await signAndSendAsync({ transactionMessage });
      console.log("Created token account:", ata);
      console.log("Transaction signature:", signature);
      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 transactionMessage = 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 signature = await signAndSendAsync({ transactionMessage });
      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>
  );
}

Next Steps