Anurag
Anurag

Reputation: 83

Solana USDC Transfer: "Cannot Read Properties of Undefined (Reading 'from')" Error in @solana/spl-token

I'm trying to transfer USDC on Solana using @solana/spl-token in a React app with @solana/wallet-adapter-react, but I'm getting this error:

Uncaught TypeError: Cannot read properties of undefined (reading 'from')
    at @solana_spl-token.js?v=0c2b08b7:5387:43

🔹 What I'm Doing:

  1. Fetching the Associated Token Address (ATA) using getAssociatedTokenAddress.
  2. Checking if the sender's ATA exists using getAccount.
  3. Creating the recipient's ATA if it doesn't exist.
  4. Using createTransferInstruction to transfer USDC.
  5. Ensuring TOKEN_PROGRAM_ID is imported.

🔍 Troubleshooting I've Tried:

  1. ✅ Added debug logs to check getAssociatedTokenAddress values.
  2. ✅ Verified sender's token account exists before transfer.
  3. ✅ Converted amount to BigInt (BigInt(Math.round(amount * 1e6))).
  4. ✅ Ensured TOKEN_PROGRAM_ID is correctly imported.

❓ Where Could This Error Be Coming From?

  1. Could getAssociatedTokenAddress return undefined under certain conditions?
  2. Do I need to explicitly specify the program ID when fetching accounts?
  3. Any known issues with createTransferInstruction that might cause this?

Here's my full React/TypeScript code for reference:

    import { useState, useEffect } from "react";
    import { useWallet } from "@solana/wallet-adapter-react";
    import {
        Connection,
        PublicKey,
        Transaction,
    } from "@solana/web3.js";
    import {
        getAssociatedTokenAddress,
        createAssociatedTokenAccountInstruction,
        createTransferInstruction,
        getAccount,
        TOKEN_PROGRAM_ID, // Ensure TOKEN_PROGRAM_ID is imported
    } from "@solana/spl-token";
    import axios from "axios";
    
    const USDC_MINT_ADDRESS = new PublicKey("11111111111111111111111111111111111111111111");
    
    const USDC = () => {
        const { publicKey, sendTransaction } = useWallet();
        const [recipient, setRecipient] = useState("");
        const [amount, setAmount] = useState<string>(""); // Keep it as a string for input handling
        const connection = new Connection("https://api.devnet.solana.com");
    
        useEffect(() => {
            fetchRecipientAddress();
        }, []);
    
        const fetchRecipientAddress = async () => {
            try {
                console.log("Fetching recipient wallet address...");
                const response = await axios.get("http://192.168.29.183:5000/wallet");
                setRecipient(response.data.wallet_address);
                console.log("✅ Recipient Wallet Address:", response.data.wallet_address);
            } catch (error) {
                console.error("❌ Error fetching recipient address:", error);
            }
        };
    
        const transferUSDC = async (recipientWallet: string, amount: number): Promise<void> => {
            try {
                console.log("🔹 USDC Transfer Initiated...");
    
                if (!publicKey) {
                    console.error("🚨 Wallet not connected. Aborting transaction.");
                    alert("⚠️ Wallet not connected!");
                    return;
                }
    
                console.log("✅ Wallet Connected:", publicKey.toBase58());
    
                const senderPublicKey = publicKey;
                const recipientPublicKey = new PublicKey(recipientWallet);
                console.log("✅ Recipient Public Key:", recipientPublicKey.toBase58());
    
                // ✅ Log ATA Before Fetching
                console.log("🔍 Fetching Associated Token Addresses...");
                const senderTokenAccount = await getAssociatedTokenAddress(
                    USDC_MINT_ADDRESS, senderPublicKey, false, TOKEN_PROGRAM_ID
                );
                const recipientTokenAccount = await getAssociatedTokenAddress(
                    USDC_MINT_ADDRESS, recipientPublicKey, false, TOKEN_PROGRAM_ID
                );
    
                if (!senderTokenAccount || !recipientTokenAccount) {
                    console.error("❌ Error fetching token accounts!");
                    alert("⚠️ Error retrieving token accounts.");
                    return;
                }
    
                console.log("✅ Sender USDC Token Account:", senderTokenAccount.toBase58());
                console.log("✅ Recipient USDC Token Account:", recipientTokenAccount.toBase58());
    
                // ✅ Ensure sender has a USDC token account
                try {
                    console.log("🔍 Checking sender's USDC token account...");
                    await getAccount(connection, senderTokenAccount);
                    console.log("✅ Sender USDC token account exists.");
                } catch (error) {
                    console.error("❌ Sender does not have a USDC token account!");
                    alert("⚠️ Your wallet doesn't have a USDC account. Please receive some USDC first.");
                    return;
                }
    
                // ✅ Check if recipient's ATA exists
                console.log("🔍 Checking recipient's USDC token account...");
                const recipientATAInfo = await connection.getAccountInfo(recipientTokenAccount);
                const transaction = new Transaction();
    
                if (!recipientATAInfo) {
                    console.log("⚠️ Recipient does not have a USDC ATA. Creating one...");
                    transaction.add(
                        createAssociatedTokenAccountInstruction(
                            senderPublicKey,
                            recipientTokenAccount,
                            recipientPublicKey,
                            USDC_MINT_ADDRESS,
                            TOKEN_PROGRAM_ID
                        )
                    );
                    console.log("✅ Recipient USDC ATA Created.");
                } else {
                    console.log("✅ Recipient already has a USDC ATA.");
                }
    
                // ✅ Convert amount to smallest unit & use BigInt
                const amountInSmallestUnit = BigInt(Math.round(amount * 1e6));
    
                // ✅ Add transfer instruction
                console.log(`🔹 Adding transfer instruction: ${amount} USDC`);
                transaction.add(
                    createTransferInstruction(
                        senderTokenAccount,
                        recipientTokenAccount,
                        senderPublicKey,
                        amountInSmallestUnit,
                        [],
                        TOKEN_PROGRAM_ID
                    )
                );
    
                console.log("✅ Transaction built, sending transaction...");
    
                // ✅ Send transaction
                const signature = await sendTransaction(transaction, connection);
                console.log("✅ USDC Transfer Successful! Signature:", signature);
                alert(`✅ Transfer successful! Transaction: ${signature}`);
    
            } catch (error) {
                console.error("❌ USDC Transfer Failed:", error);
                alert("❌ Transfer failed. Check console for details.");
            }
        };
    
        const handleTransfer = async () => {
            if (!publicKey) {
              alert("⚠️ Connect your wallet first!");
              return;
            }
            if (!recipient) {
              alert("⚠️ Recipient address not fetched!");
              return;
            }
          
            // ✅ Convert amount to number
            const numericAmount = parseFloat(amount);
          
            if (isNaN(numericAmount) || numericAmount <= 0) {
              alert("⚠️ Please enter a valid amount.");
              return;
            }
          
            console.log("🔹 Initiating transfer process...");
            try {
              await transferUSDC(recipient, numericAmount); // Pass number, not string
            } catch (error) {
              console.error(error);
            }
          };
          
    
        return (
            <div>
                <h2>USDC Transfer</h2>
                <p><strong>Recipient Wallet:</strong> {recipient || "Fetching..."}</p>
                <input
                    type="number"
                    placeholder="Amount (USDC)"
                    value={amount}
                    onChange={(e) => setAmount(e.target.value)} // Keep as string
                />
    
                <button onClick={handleTransfer}>Send USDC</button>
            </div>
        );
    };
    
    export default USDC;

Upvotes: 1

Views: 30

Answers (0)

Related Questions