2

I'm building a transaction on devnet that previously worked when I tested my program. I don't think it's the program itself, but how I am passing down the signatures which is slightly different from what I was doing in the test.ts file where I used this to sign all of the transactions...const ownerWallet = provider.wallet as anchor.Wallet;

I'm not using a provider clientside, just passing down the program ID as a string to the PDA using new PublicKey(). I'm creating 3 PDA accounts, 2 of which use the signer publicKey as part of the seed. The last one is meant to be a PDA that isn't controlled by the person creating the game account (hence the absence of the wallet.publicKey.

However, I'm getting an error that says, Signature Verification Failed: Missing Signature for <the wallet publicKey I'm using> which seems odd because I'm using await wallet?.signTransaction(tx); at the bottom within the try-catch.

export const useGameBuilder = () => {
    const PROGRAM_ID = new PublicKey('<ProgramIDHere>')

    const initializeGame = async (wallet: any, connection: Connection): Promise<void> => {
        if (!wallet) return;

        let [gameAcctPDA] = web3.PublicKey.findProgramAddressSync(
            [Buffer.from( "game" ), wallet.publicKey!.toBuffer()],
            PROGRAM_ID
        );
        console.log('gameAcct: ', gameAcctPDA.toString());

        let [wordVaultAcctPDA] = web3.PublicKey.findProgramAddressSync(
            [Buffer.from("vault"), wallet.publicKey!.toBuffer()],
            PROGRAM_ID
        );
        console.log('wordVaultAcct: ', wordVaultAcctPDA.toString())

        let [moneyVaultAcctPDA, bump] = web3.PublicKey.findProgramAddressSync(
            [Buffer.from("pot")],
            PROGRAM_ID
        );
        console.log('moneyVaultAcct: ', moneyVaultAcctPDA.toString())

        const gameAccounts: InitializeCreateGameInstructionAccounts = {
            game: gameAcctPDA,
            wordVault: wordVaultAcctPDA,
            moneyPotVault: moneyVaultAcctPDA,
            owner: wallet.publicKey!
        }

        const gameCreationArgs: InitializeCreateGameInstructionArgs = {
            name: 'TestVerse',
            minDeposit: 0.1
        }

        const transactionInstruction = createInitializeCreateGameInstruction(gameAccounts, gameCreationArgs);

        try {
            const tx = new Transaction().add(transactionInstruction);
            tx!.feePayer = wallet!.publicKey!;

            const latestBlockhash = await connection.getLatestBlockhash();
            tx.recentBlockhash = latestBlockhash.blockhash;
       
            await wallet?.signTransaction(tx);

            // Serialize and send the transaction
            const signedTx = tx!.serialize();
            const txId = await connection.sendRawTransaction(signedTx);

            console.log('Transaction ID:', txId);
        } catch (error) {
            console.log('error creating game', error);
        }
    };

    return { initializeGame };
};

I don't think I need to use partialSign here but I could be wrong. I'm not explicitly creating the accounts in the transaction. I'm passing the wallet from phantom that I'm using with useWallet to be the owner/signer of the instruction and to be used as the seed phrase when the accounts as created. Not sure why it's saying it's missing the signature for the publicKey of the wallet I'm trying to sign with though since I'm running await wallet?.signTransaction(tx);

I also get the amount of the transaction in the Phantom wallet. So I know part of it is working. Tell me it costs 0.0039 SOL

EDIT: Ok this works. I just pass in the wallet seed manually and create a Signer, I'd still like to get it working with a wallet though.

const initializeGame = async (wallet: AnchorWallet, connection: Connection): Promise<void> => {
        if (!wallet) return;
        const seed = new Uint8Array([numbers here])
        const keypair = Keypair.fromSecretKey(seed)
        const sign: Signer = {
            publicKey: keypair.publicKey,
            secretKey: seed
        }

        let [gameAcctPDA] = web3.PublicKey.findProgramAddressSync(
            [Buffer.from( "game" ), keypair.publicKey.toBuffer()],
            PROGRAM_ID
        );
        console.log('gameAcct: ', gameAcctPDA.toString());

        let [wordVaultAcctPDA] = web3.PublicKey.findProgramAddressSync(
            [Buffer.from("word"), keypair.publicKey.toBuffer()],
            PROGRAM_ID
        );
        console.log('wordVaultAcct: ', wordVaultAcctPDA.toString())

        let [moneyVaultAcctPDA, bump] = web3.PublicKey.findProgramAddressSync(
            [Buffer.from("pot")],
            PROGRAM_ID
        );
        console.log('moneyVaultAcct: ', moneyVaultAcctPDA.toString())

        const gameAccounts: InitializeCreateGameInstructionAccounts = {
            game: gameAcctPDA,
            wordVault: wordVaultAcctPDA,
            moneyPotVault: moneyVaultAcctPDA,
            owner: sign.publicKey
        }

        const gameCreationArgs: InitializeCreateGameInstructionArgs = {
            name: 'TestVerse',
            minDeposit: 0.1
        }

        const transactionInstruction = createInitializeCreateGameInstruction(gameAccounts, gameCreationArgs);

        try {
            const tx = new Transaction().add(transactionInstruction);
            tx!.feePayer = sign!.publicKey!;

            const latestBlockhash = await connection.getLatestBlockhash();
            tx.recentBlockhash = latestBlockhash.blockhash;
        
            tx!.sign(sign);
            // await wallet?.signTransaction(tx);

            // Serialize and send the transaction
            const signedTx = tx!.serialize();
            const txId = await connection.sendRawTransaction(signedTx);

            console.log('Transaction ID:', txId);
        } catch (error) {
            console.log('error creating game', error);
        }
    };

    return { initializeGame };
0

2 Answers 2

1

Some ways to debug this:

  1. Print the transaction before you send it and make sure all the accounts are the same and double check mut and signer parameters.

console.log(JSON.stringify(tx));

  1. Skip preflight checks and see what happens on chain actually in the solana explorer:

connection.sendRawTransaction(rawTransaction, { skipPreflight: true, });

  1. Make sure all the PDAs are derived the same in the client and the program

  2. Phantom does add additional instructions to transactions to add priority fees, which can sometimes screw up transactions.

  3. Check the order of accounts and put your signer at the right spot.

  4. Try if it works with signAndSendTransaction instead of sign and then sendingRawTransaction

2
  • 1
    Thanks for the reply! I actually figured it out. When signing with a wallet, you don't have to do all of the extra stuff. All you have to do is run sendTransaction through the adapter, I wasn't doing it that way before. I'm using useWallet() from @solana/wallet-adapter-react and passing that into a hook like this wallet.wallet?.adapter?.sendTransaction(transactionThree, connection). This works better than trying to send the transaction through Connection
    – Brian M.
    Commented Jan 11 at 20:29
  • Ahh cool happy you figured it out. You can answer your own question and except it as valid answer. Then people who run into the same problem will see it directly :)
    – Jonas H.
    Commented Jan 11 at 21:06
0

See this line:

await wallet?.signTransaction(tx);

You need to reassign the tx variable to this:

tx = await wallet?.signTransaction(tx);

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.