Solana脚本购买Magic Eden NFT报unknown signer错误排查
问题根因与修复方案
错误1:unknown signer 签名者不匹配
- 代码存在基础拼写错误:定义密钥变量时写的是
secret,调用Keypair.fromSecretKey时传入的是未定义的secrete(多了末尾的e),导致生成的签名者密钥对完全无效,和预期地址不一致。 - 冗余硬编码费付地址:手动写死
publickey变量作为feePayer,一旦硬编码地址和私钥实际派生的地址存在偏差,交易签名校验直接失败,正确做法是从加载好的Keypair对象上直接取publicKey属性,不需要手动定义。 - 指令程序ID配置错误:构建
TransactionInstruction时把programId填成了个人钱包地址,实际上该字段需要填Magic Eden交易程序的链上合约地址,填个人地址会导致指令找不到对应执行程序,节点校验直接抛出签名相关错误。 - 账户列表生成逻辑错误:自行实现的账户生成逻辑已经过时,Magic Eden V2买币接口返回值中已经携带了完整的、顺序正确的账户元数据,自行生成的账户如果顺序不对、
isSigner/isWritable标记错误,都会触发签名者不匹配报错。
错误2:助记词派生后触发verification error 校验失败
- 助记词派生逻辑不符合Solana标准:直接截取bip39生成的64字节种子前32字节作为Keypair种子是以太坊的派生规则,Solana采用ed25519签名算法,必须使用标准派生路径
m/44'/501'/0'/0'从64字节种子中派生出正确的32字节密钥种子,直接截断得到的密钥对和钱包助记词对应的地址完全不匹配,签名自然无效。 - 交易缺失必要字段:构建交易时没有设置
recentBlockhash(最近区块哈希),Solana要求所有交易必须绑定一个近期的区块哈希作为交易有效期标识,缺失该字段的交易直接会被节点拒绝。
修复步骤
- 修正密钥加载逻辑:删除硬编码的钱包地址,修复拼写错误,从私钥/助记词正确加载Keypair后,直接从Keypair对象获取公钥作为费付地址。
- 修正助记词派生逻辑:引入ed25519高清密钥派生库,使用Solana标准路径派生密钥对,不要直接截断bip39种子。
- 修正交易构建逻辑:直接使用Magic Eden接口返回的programId、账户列表、指令数据,不要自行生成账户列表,构建交易时补上最近区块哈希字段。
- 签名校验:传入
sendAndConfirmTransaction的签名者数组,必须和交易指令中所有标记为isSigner=true的账户一一对应,普通买单场景仅需要传入当前买家的Keypair作为签名者。
可直接运行的参考实现
import fs from 'fs'; import { Keypair, Connection, Transaction, TransactionInstruction, sendAndConfirmTransaction, PublicKey } from '@solana/web3.js'; import * as bip39 from 'bip39'; import { derivePath } from 'ed25519-hd-key'; // 初始化RPC连接 const connection = new Connection("https://api.mainnet-beta.solana.com", 'confirmed'); /** * 从Solana CLI生成的id.json文件加载密钥对 * @param idJsonPath id.json文件本地路径 */ function loadKeypairFromFile(idJsonPath: string): Keypair { const secretKeyArray = JSON.parse(fs.readFileSync(idJsonPath, 'utf-8')); return Keypair.fromSecretKey(Uint8Array.from(secretKeyArray)); } /** * 从助记词派生Solana标准密钥对 * @param mnemonic 12/24位助记词 */ function getKeypairFromMnemonic(mnemonic: string): Keypair { const seed = bip39.mnemonicToSeedSync(mnemonic); // Solana标准HD派生路径 const derivedSeed = derivePath("m/44'/501'/0'/0'", seed.toString('hex')).key; return Keypair.fromSeed(derivedSeed); } /** * 执行Magic Eden NFT购买交易 * @param buyResponse Magic Eden buy_now接口返回的原始响应 * @param signer 买家签名密钥对 */ async function executeBuyTx(buyResponse: any, signer: Keypair): Promise<string> { const tx = new Transaction(); // 配置交易基础参数:费付者、最近区块哈希 const latestBlock = await connection.getLatestBlockhash('confirmed'); tx.feePayer = signer.publicKey; tx.recentBlockhash = latestBlock.blockhash; // 从接口响应直接解析指令数据 const instructionData = Buffer.from(buyResponse.tx.data, 'hex'); // 从接口响应直接解析账户列表,保持接口返回的顺序不要改动 const accountMetas = buyResponse.tx.accounts.map((acc: any) => ({ pubkey: new PublicKey(acc.pubkey), isSigner: acc.signer, isWritable: acc.writable })); // 从接口响应直接解析程序ID const programId = new PublicKey(buyResponse.tx.programId); tx.add(new TransactionInstruction({ keys: accountMetas, programId, data: instructionData })); // 发送并确认交易 const txSignature = await sendAndConfirmTransaction( connection, tx, [signer], { commitment: 'confirmed' } ); console.log('交易发送成功,链上签名:', txSignature); return txSignature; }
额外注意事项
- 请求Magic Eden buy_now接口时,传入的
buyer参数必须和当前使用的签名者公钥完全一致,否则接口返回的指令中买家地址不匹配,仍会触发签名错误。 - 接口返回的账户列表顺序不可随意调整,Solana链上程序对指令账户的顺序有严格校验,顺序错误会导致程序执行失败。
- 不要在主网直接运行未测试的代码,建议先在devnet环境调试通逻辑再切换主网,避免资产损失。
内容的提问来源于stack exchange,提问作者Ray
相关产品推荐
相关产品推荐

