I’m working on a system that moves both SOL tokens and NFTs from user wallets to our main wallet. The process involves creating a transaction with these steps:
- Token.createAssociatedTokenAccountInstruction - Only runs if needed for the receiving wallet
- Token.createTransferInstruction - Moves the NFT from user’s account to our account
- SystemProgram.transfer - Handles the SOL movement
Here’s my implementation using spl-token v0.1.8:
let buildTransferInstructions = async function (tokenMint, sender, receiver, network) {
let conn = new Connection(network, "confirmed")
const tokenMintKey = new PublicKey(tokenMint);
const senderKey = new PublicKey(sender);
const receiverKey = new PublicKey(receiver);
// FIND SENDER'S TOKEN ACCOUNT
const senderTokenAccount = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
tokenMintKey,
senderKey
);
// FIND RECEIVER'S TOKEN ACCOUNT
const receiverTokenAccount = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
tokenMintKey,
receiverKey
);
const targetAccount = await conn.getAccountInfo(receiverTokenAccount);
const txInstructions = [];
if (targetAccount === null)
txInstructions.push(
Token.createAssociatedTokenAccountInstruction(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
tokenMintKey,
receiverTokenAccount,
receiverKey,
senderKey
)
)
txInstructions.push(
Token.createTransferInstruction(
TOKEN_PROGRAM_ID,
senderTokenAccount,
receiverTokenAccount,
senderKey,
[],
1
)
);
return txInstructions;
}
Transaction building code:
var tx = new this.solana.Transaction({
feePayer: new this.solana.PublicKey(sender),
recentBlockhash: await hashData.blockhash
});
for (let instruction of nftInstructions) {
tx.add(instruction);
}
tx.add(this.solana.SystemProgram.transfer({
fromPubkey: new this.solana.PublicKey(this.provider.publicKey),
toPubkey: new this.solana.PublicKey(targetWallet.address),
lamports: 10000000
}));
Most of the time this works fine and users can approve transactions in their wallets like Phantom or Solflare. But some users report getting validation errors when trying to confirm the transaction in their wallet apps.
The strange part is that these same NFTs can be transferred normally through the wallet’s built-in transfer feature and can be listed on marketplaces without problems. Once the NFT gets moved to a different wallet, my code works perfectly with it.
Could this be related to how the associated token accounts are handled? What might be causing this inconsistent behavior?