2

I'm trying to add a balance of USDC in a connected wallet that updates on a nextjs typescript website. I want to add it to my SOL balance context provider or do I need to make a completely separate context for the USDC balance?

import { useWallet } from '@solana/wallet-adapter-react';
import { LAMPORTS_PER_SOL } from '@solana/web3.js';
import { createContext, useContext, useEffect, useState, useMemo } from 'react';
import * as anchor from '@project-serum/anchor';


const BalanceContext = createContext(null);

const rpcHost = process.env.NEXT_PUBLIC_SOLANA_RPC_HOST;
const connection = new anchor.web3.Connection(rpcHost);

export default function useWalletBalance() {
  const { balance, walletPubKey, walletAddress } = useContext(BalanceContext);
  return { balance, walletPubKey, walletAddress };
}


export const WalletBalanceProvider = ({ children }) => {
  const wallet = useWallet();
  const [balance, setBalance] = useState(0);
  const [walletPubKey, setWalletPubKey] = useState({});
  const [walletAddress, setWalletAddress] = useState('');

  const anchorWallet = useMemo(() => {
    if (
      !wallet ||
      !wallet.publicKey ||
      !wallet.signAllTransactions ||
      !wallet.signTransaction
    ) {
      return;
    }

    return {
      publicKey: wallet.publicKey,
      signAllTransactions: wallet.signAllTransactions,
      signTransaction: wallet.signTransaction,
    };
  }, [wallet]);

  const refreshWallet = async () => {
    if (!anchorWallet) {
      setBalance(0);
      setWalletAddress('');
      setWalletPubKey({});
      return;
    }

    try {
      const balance = await connection.getBalance(anchorWallet.publicKey);
      setBalance(balance / LAMPORTS_PER_SOL);
      setWalletPubKey(anchorWallet.publicKey);
      setWalletAddress(anchorWallet.publicKey.toBase58());
    } catch (e) {
      console.error(e);
    }
  };

  useEffect(() => {
    refreshWallet();
  }, [anchorWallet, balance, walletAddress]);

  return (
    <BalanceContext.Provider value={{ balance, walletPubKey, walletAddress }}>
      {children}
    </BalanceContext.Provider>
  );
};

Not sure the best way to include it in refresh async function.

3 Answers 3

2

You can easily add any token to your existing context. There are various ways to do this. I am using the most basic (using spl-token library):

First import spl-token library:

import * as token from "@solana/spl-token"

I have added //new comment in every new line I added:

export const WalletBalanceProvider = ({ children }) => {
  const wallet = useWallet();
  const [balance, setBalance] = useState(0);
  const [walletPubKey, setWalletPubKey] = useState({});
  const [walletAddress, setWalletAddress] = useState('');
  const [usdcBalance, setUsdcBalance] = useState(0); //new

  const anchorWallet = useMemo(() => {
    if (
      !wallet ||
      !wallet.publicKey ||
      !wallet.signAllTransactions ||
      !wallet.signTransaction
    ) {
      return;
    }
    
    return {
      publicKey: wallet.publicKey,
      signAllTransactions: wallet.signAllTransactions,
      signTransaction: wallet.signTransaction,
    };
  }, [wallet]);

  const refreshWallet = async () => {
    if (!anchorWallet) {
      setBalance(0);
      setUsdcBalance(0); //new
      setWalletAddress('');
      setWalletPubKey({});
      return;
    }

    try {
      const balance = await connection.getBalance(anchorWallet.publicKey);
      const usdcMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); //new
      const usdcAddress = await token.getAssociatedTokenAddress(usdcMint, anchorWallet.publicKey); //new

      try {
        const usdcDetails = await token.getAccount(connection, usdcAddress); //new
        const usdcDecimals = 6; //new
        const usdcBalance = Number(usdcDetails.amount) / Math.pow(10, usdcDecimals); //new
        setUsdcBalance(usdcBalance); //new
      } catch {
        console.log("USDC Account doesn't exist for the given address"); //new
      }

      setBalance(balance / LAMPORTS_PER_SOL);
      setWalletPubKey(anchorWallet.publicKey);
      setWalletAddress(anchorWallet.publicKey.toBase58());
    } catch (e) {
      console.error(e);
    }
  };

  useEffect(() => {
    refreshWallet();
  }, [anchorWallet, balance, walletAddress, usdcBalance]);

  return (
    <BalanceContext.Provider value={{ balance, walletPubKey, walletAddress, usdcBalance }}>
      {children}
    </BalanceContext.Provider>
  );
};
3
  • I imported {token} from @solana/spl-token and get TypeError: Cannot read properties of undefined (reading 'getAssociatedTokenAddress') at refreshWallet (WalletBalanceProvider.jsx:55:33) c Commented Apr 26, 2023 at 18:16
  • found it. Instead of token.getAssociatedTokenAddress had to import { getAssociatedTokenAddress } from '@solana/spl-token'; now to get it to read tokens. returning no usdc when they are there. Commented Apr 26, 2023 at 18:26
  • Got it working. import { getAccount} from '@solana/spl-token' Latest npm updates have changed syntax a little. I'll post the changes for future users. Commented Apr 26, 2023 at 21:47
0

If you want to update the USDC constantly you can use accountSubscribe to listen to the account and like this you will get the changes pushed to he client via a websocket connection. https://docs.solana.com/de/api/websocket This would look something like this:

const subscriptionID = connection.onAccountChange(
      tokenAccount,
      (account) => {
                const usdcDecimals = 6; //new
        const usdcBalance = Number(usdcDetails.amount) / Math.pow(10, usdcDecimals); //new
        setUsdcBalance(usdcBalance);
      }
    )
1
  • Thanks for the added knowledge. Commented Apr 26, 2023 at 18:11
0

Here is the updated syntax for newer versions of Solana packages.

import { useWallet } from '@solana/wallet-adapter-react';
import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
import { createContext, useContext, useEffect, useState, useMemo } from 'react';
import * as anchor from '@project-serum/anchor';
import { getAssociatedTokenAddress, getAccount } from '@solana/spl-token';


const BalanceContext = createContext(null);

const rpcHost = process.env.NEXT_PUBLIC_SOLANA_RPC_HOST;
const connection = new anchor.web3.Connection(rpcHost);

export default function useWalletBalance() {
  const { balance, walletPubKey, walletAddress, usdcBalance } = useContext(BalanceContext);
  return { balance, walletPubKey, walletAddress, usdcBalance };
}


export const WalletBalanceProvider = ({ children }) => {
  const wallet = useWallet();
  const [balance, setBalance] = useState(0);
  const [walletPubKey, setWalletPubKey] = useState({});
  const [walletAddress, setWalletAddress] = useState('');
  const [usdcBalance, setUsdcBalance] = useState(0); //new

  const anchorWallet = useMemo(() => {
    if (
      !wallet ||
      !wallet.publicKey ||
      !wallet.signAllTransactions ||
      !wallet.signTransaction
    ) {
      return;
    }
    
    return {
      publicKey: wallet.publicKey,
      signAllTransactions: wallet.signAllTransactions,
      signTransaction: wallet.signTransaction,
    };
  }, [wallet]);

  const refreshWallet = async () => {
    if (!anchorWallet) {
      setBalance(0);
      setUsdcBalance(0); //new
      setWalletAddress('');
      setWalletPubKey({});
      return;
    }

    try {
      const balance = await connection.getBalance(anchorWallet.publicKey);
      const usdcMint = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); //new
      const usdcAddress = await getAssociatedTokenAddress(usdcMint, anchorWallet.publicKey); //new

      try {
        const usdcDetails = await getAccount(connection, usdcAddress); //new
        const usdcDecimals = 6; //new
        const usdcBalance = Number(usdcDetails.amount) / Math.pow(10, usdcDecimals); //new
        setUsdcBalance(usdcBalance); //new
      } catch {
        console.log("USDC Account doesn't exist for the given address"); //new
      }

      setBalance(balance / LAMPORTS_PER_SOL);
      setWalletPubKey(anchorWallet.publicKey);
      setWalletAddress(anchorWallet.publicKey.toBase58());
    } catch (e) {
      console.error(e);
    }
  };

  useEffect(() => {
    refreshWallet();
  }, [anchorWallet, balance, walletAddress, usdcBalance]);

  return (
    <BalanceContext.Provider value={{ balance, walletPubKey, walletAddress, usdcBalance }}>
      {children}
    </BalanceContext.Provider>
  );
};

Thanks for the help!!!

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.