0

I have a multisig address with 3 other signers. Not sure if this was created with cli or serum project (the developer that created it is working anymore).

When trying to transfer a token, I get this error:

   'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]',
   'Program log: Instruction: Transfer',
   'Program log: Error: InvalidAccountData',
   'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 1810 of 200000 compute units',
   'Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA failed: invalid account data for instruction'

Code:

const signer1Account = Keypair.fromSecretKey(key1);
const signer2Account = Keypair.fromSecretKey(key2);
const signer3Account = Keypair.fromSecretKey(key3);

const tokenAmount = 10000

const tokenMintPubkey = new PublicKey(' ');
const fromMultisigAddress = new PublicKey(' ');
const fromMultisigOwnerAddress = new PublicKey('');
const toAddress = new PublicKey('');

const amountToTransfer = tokenAmount * Math.pow(10, 6);

(async () => {

  let toAddressTokenAccount = await getOrCreateAssociatedTokenAccount(
    connection,
    signer1Account,
    tokenMintPubkey,
    toAddress,
    true
  );
  console.log(`toAddressTokenAccount: ${toAddressTokenAccount.address}`);

  
  let tx = new Transaction();
  try {
    tx.add(
      createTransferInstruction(
        fromMultisigAddress, // from
        toAddressTokenAccount.address, // to
        TOKEN_PROGRAM_ID, // from's owner
        amountToTransfer, // amount
        [signer1Account, signer2Account, signer3Account] //signers
      )
    );    
  } catch (error) {
    console.error(error)
  }  

  try {
    const latestBlockHash = await connection.getLatestBlockhash('confirmed');
    tx.recentBlockhash = await latestBlockHash.blockhash;    
    const signature = await sendAndConfirmTransaction(connection, tx, [signer1Account, signer2Account, signer3Account]);
  } catch (error) {
    console.error(error)
  }
})();

I search and read all questions on it, found this but I am not sure how to solve it. Error: Invalid account data for instruction

Thanks for any help

Update - solved thanks to the help of Jon C.

Adding the working code maybe it will help someone else

  import { clusterApiUrl, Keypair, Transaction, Connection, PublicKey, sendAndConfirmTransaction, ParsedAccountData } from "@solana/web3.js";
    import { TOKEN_PROGRAM_ID, getOrCreateAssociatedTokenAccount, createTransferInstruction } from "@solana/spl-token";
    
    // connection
    const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");

    // your array keys
    const key1 = new Uint8Array([]);
    const key2 = new Uint8Array([]);
    const key3 = new Uint8Array([]);
    
    
    const signer1Account = Keypair.fromSecretKey(key1);
    const signer2Account = Keypair.fromSecretKey(key2);
    const signer3Account = Keypair.fromSecretKey(key3);
    
    
    const tokenAmount = 1
    
    // your public keys
    const tokenMintPubkey = new PublicKey('');
    const fromMultisigAddress = new PublicKey('');
    const fromMultisigOwnerAddress = new PublicKey('');
    const toAddress = new PublicKey(''); 
    const tokenAddressOnMultisigAddress = new PublicKey(''); 
    
    
    async function getNumberDecimals(mintPubkey: PublicKey) : Promise<number> {
      const info = await connection.getParsedAccountInfo(mintPubkey);
      const result = (info.value?.data as ParsedAccountData).parsed.info.decimals as number;
      return result;
    }
    
    (async () => {
    
      let toAddressTokenAccount = await getOrCreateAssociatedTokenAccount(
        connection,
        signer1Account,
        tokenMintPubkey,
        toAddress,
        true
      );
    
      console.log(`toAddressTokenAccount: ${toAddressTokenAccount.address}`);
      
      const decimals = await getNumberDecimals(tokenMintPubkey); 
      console.log(`Token decimals: ${decimals}`);
    
      const amountToTransfer = tokenAmount * Math.pow(10, decimals);
    
      let tx = new Transaction();
    
      try {
        tx.add(
          createTransferInstruction(
            tokenAddressOnMultisigAddress, // from (token account on the source address)
            toAddressTokenAccount.address, // to
            fromMultisigAddress, // from's owner (owner of the token account - the multisig address)
            amountToTransfer, // amount
            [signer1Account, signer2Account, signer3Account] //signers
          )
        );    
      } catch (error) {
        console.error(error)
      }  
    
      try {
    
        const latestBlockHash = await connection.getLatestBlockhash('confirmed');
        tx.recentBlockhash = await latestBlockHash.blockhash;    
        const signature = await sendAndConfirmTransaction(connection, tx, [signer1Account, signer2Account, signer3Account]);
    
      } catch (error) {
        console.error(error)
      }
    })();

1 Answer 1

1

You're very close! The term owner is unfortunately overloaded in the context of SPL tokens, since owner can either mean:

  • the address that can sign for transfers from an account
  • more generally, the program that owns the account and can modify the data

You've passed in TOKEN_PROGRAM_ID as from's owner, but you should pass in fromMultisigOwnerAddress, which is the address that can sign for transfers from an account. TOKEN_PROGRAM_ID is the owner of the Solana account, but not the SPL Token account.

You can read more about the Solana account / ownership model at https://docs.solana.com/developing/programming-model/accounts#ownership-and-assignment-to-programs

2
  • Thanks but this address can't sign as we don't have the key. We have 3 other signers. The address with the funds is not a signer. I tried to put that as the owner also, didn't work. Commented Jun 1, 2023 at 13:03
  • OK WORKED! The from parameter needS to be the token account that holds the token in the multisig address, and the owner is the multisig address. Thank you very much! This one was hard to catch and I did searched a LOT. Thanks! Commented Jun 1, 2023 at 13:35

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.