0

I built a ui using NextJS and the Solana wallet adapter library. I'm using getParsedTokenAccountsByowner to display all token accounts when the user connects their wallet. I want to map over all these token accounts and add a key value pair of isMintAuthority to them so I can display a "Mint Tokens" button to the user, IF they have mintAuthority. Here's the react-query hook I put together:

export function useGetMintAuthority({ mintAddress }: { mintAddress: PublicKey }) {
  const { connection } = useConnection();

  return useQuery({
    queryKey: ["get-mint-authority", { endpoint: connection.rpcEndpoint, mintAddress }],
    queryFn: async () => {
      // Fetch the account info for the mint address
      const accountInfo = await connection.getAccountInfo(mintAddress);
      if (!accountInfo) throw new Error("Failed to find mint account");

      // Parse the account info to get the mint data
      const mintData = MintLayout.decode(accountInfo.data);

      // The mint authority is an optional field, so it could be null
      const mintAuthority = mintData.mintAuthority ? new PublicKey(mintData.mintAuthority) : null;

      return mintAuthority;
    },
  });
}

The problem is this only works for one account at a time. Without getting into the limitations and headache of React Hooks inside loops or conditionals and the asynchronous nature of fetching mint authority. Is there a best way or recommended way to do this?

2 Answers 2

1

You can fetch multiple accounts together, using the getMultipleAccounts RPC call. That's also available in web3.js as connection.getMultipleAccountsInfo and connection.getMultipleParsedAccounts. In any case that request will fetch up to 100 accounts at a time.

Then your hook would take a set of mint addresses as input, and perhaps return a map of address -> mint authority.

Depending on your usage pattern you might still want to cache per mint and just fetch the missing ones, instead of caching by list.

1

So here's what worked for me. I abandoned trying to do this within a hook and instead opted for an async function. Since I already have all the token accounts of the owner, which I am getting back from getParsedTokenAccountsByOwner I just wanted to add this isMintAuthority: boolean key pair to that object before I do some bit of rendering logic. This way I can easily check this key pair and display a mint button if it's true. Obviously TS will make you write twice the amount of code, but whatevs.

type EnhancedAccount = {
  pubkey: PublicKey;
  account: AccountInfo<ParsedAccountData>;
  isMintAuthority: boolean;
};

type TokenAccount = {
  pubkey: PublicKey;
  account: AccountInfo<ParsedAccountData>;
};

async function enhanceAccountsWithMintAuthority(
  accounts: TokenAccount[],
  connection: Connection,
  userAddress: PublicKey
): Promise<EnhancedAccount[]> {
  const enhancedAccounts = await Promise.all(
    accounts.map(async (account) => {
      const mintAddress = new PublicKey(account.account.data.parsed.info.mint);
      const mintAccountInfo = await connection.getAccountInfo(mintAddress);
      if (!mintAccountInfo) throw new Error("Failed to find mint account");

      const mintData = MintLayout.decode(mintAccountInfo.data);
      const isMintAuthority = mintData.mintAuthority && new PublicKey(mintData.mintAuthority).equals(userAddress);

      // Ensure the returned object matches the EnhancedAccount type
      return {
        pubkey: account.pubkey,
        account: account.account,
        isMintAuthority,
      };
    })
  );

  return enhancedAccounts;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.