I am following the inner instructions of a Solana NFT to create the process of listing an NFT for sale. According to this NFT, https://solscan.io/tx/25g9L7rDCn8ZbZAZoCvyVNe5woZUoK2PVU3VEXftqySa2EYsLUJB2GA42qDwxsezUBRH2A9eJX1iUUD2LCK9Fua9 , the first instruction is to create an account.
The instruction shows
NewAccount - E61SDPzHP61C4KwwiSqG4FAHXof852wsaSjh3F4kLbwmicon
Source - H2eud1RCJu8u8vEQ8jJLQ32jM5oKfARGxz5LwEKKfTLuicon
TransferAmount(SOL) - 0.00223416
ProgramOwner - M2mx93ekt1fmXSVkTrUL9xVFHkmME8HTUi5Cyc5aF7Kicon
New Account seems to be a newly generated account and Source is the wallet which was used when the user connected their wallet to the client.
I am using the this code from the Solana Cookbook for reference
import { clusterApiUrl, Connection, PublicKey, Keypair, Transaction, SystemProgram } from "@solana/web3.js";
import { Token, TOKEN_PROGRAM_ID, AccountLayout } from "@solana/spl-token";
import * as bs58 from "bs58";
(async () => {
// connection
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
// 5YNmS1R9nNSCDzb5a7mMJ1dwK9uHeAAF4CmPEwKgVWr8
const feePayer = Keypair.fromSecretKey(
bs58.decode("588FU4PktJWfGfxtzpAAXywSNt74AvtroVzGfKkVN1LwRuvHwKGr851uH8czM5qm4iqLbs1kKoMKtMJG4ATR7Ld2")
);
// G2FAbFQPFa5qKXCetoFZQEvF9BVvCKbvUZvodpVidnoY
const alice = Keypair.fromSecretKey(
bs58.decode("4NMwxzmYj2uvHuq8xoqhY8RXg63KSVJM1DXkpbmkUY7YQWuoyQgFnnzn6yo3CMnqZasnNPNuAT2TLwQsCaKkUddp")
);
const mintPubkey = new PublicKey("54dQ8cfHsW1YfKYpmdVZhWpb9iSi6Pac82Nf7sg3bVb");
// generate a new keypair for token account
const tokenAccount = Keypair.generate();
console.log(`token account: ${tokenAccount.publicKey.toBase58()}`);
let tx = new Transaction().add(
// create token account
SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
newAccountPubkey: tokenAccount.publicKey,
space: AccountLayout.span,
lamports: await Token.getMinBalanceRentForExemptAccount(connection),
programId: TOKEN_PROGRAM_ID,
}),
/**... some other instruction*/
);
console.log(`txhash: ${await connection.sendTransaction(tx, [feePayer, tokenAccount])}`);
})();
From my understanding, in this scenario the feePayer is the user who connected their wallet. ( Source from the instructions), tokenAccount is the account that we want to transfer the SOL to ( NewAccount from the instructions), and programID is ProgramOwner from the instructions.
When I run this exact code from the Solana Cookbook it works. When I try using the provider
as the feePayer
(wallet that was connected in the client) it doesn't work.
I have a function to get my provider
getProvider = () => {
if ("solana" in window) {
const anyWindow = window;
const provider = anyWindow.solana;
if (provider.isPhantom) {
return provider;
}
}
window.open("https://phantom.app/", "_blank");
};
Then I grab my provider with
const provider = this.getProvider();
I replace the original feePayer created with Keypair.fromSecretKey
with the provider
.
My assumption as to why provider isn't working as one of the signers in await connection.sendTransaction(tx, [provider, tokenAccount])
is because the signer requires a secret key.
So what I try to do instead is sign the transaction myself using some code I've found online on how to sign a transaction.
const blockHash = await connection.getRecentBlockhash()
tx.feePayer = provider.publicKey
tx.recentBlockhash = await blockHash.blockhash
const signed = await provider.signTransaction(tx);
This runs fine and I'm able to see the results when I do console.log(signed);
The problem I'm getting is that there are 2 signatures and one of them is null. One is the signature of the provider
and the other is the signature of the tokenAccount
. When I use console.log()
to show the public key of the provider
and the public key of the tokenAccount
and try to match it to the public keys within the signature, I find that the signature of the tokenAccount
is the one that is returning null.
So when it's time to run const signature = await connection.sendRawTransaction(signed.serialize());
I get an error from signed.serialize()
saying Signature verification failed
What am I doing wrong here? Can I create a Signer from a user connecting their phantom wallet? If not, how can I sign the transaction so that neither of the signatures come out null?
So I would recommend using the wallet-adapter to connect and send transactions from wallets.
This will make the code above look something like this:
const { publicKey, sendTransaction } = useWallet();
// G2FAbFQPFa5qKXCetoFZQEvF9BVvCKbvUZvodpVidnoY
const alice = Keypair.fromSecretKey(
bs58.decode("4NMwxzmYj2uvHuq8xoqhY8RXg63KSVJM1DXkpbmkUY7YQWuoyQgFnnzn6yo3CMnqZasnNPNuAT2TLwQsCaKkUddp")
);
const mintPubkey = new PublicKey("54dQ8cfHsW1YfKYpmdVZhWpb9iSi6Pac82Nf7sg3bVb");
// generate a new keypair for token account
const tokenAccount = Keypair.generate();
console.log(`token account: ${tokenAccount.publicKey.toBase58()}`);
let tx = new Transaction().add(
// create token account
SystemProgram.createAccount({
fromPubkey: publicKey,
newAccountPubkey: tokenAccount.publicKey,
space: AccountLayout.span,
lamports: await Token.getMinBalanceRentForExemptAccount(connection),
programId: TOKEN_PROGRAM_ID,
}),
/**... some other instruction*/
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, 'processed');
Hi i have similar issue, There are two signers admin and user, admin is signing the tx from node and serializing tx. this i send to frontend and user signs it. But as initially the signature is null, so wallet.signTransaction(tx); gives the Signature verification failed
You have to partially sign with the tokenAccount before sending the transaction.
const blockHash = await connection.getRecentBlockhash()
tx.feePayer = provider.publicKey
tx.recentBlockhash = await blockHash.blockhash
const signed = await provider.signTransaction(tx);
// now sign with the tokenAccount
signed.partialSign(tokenAccount);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.