调用带PDA的Solana Anchor电影评论程序时交易失败求助
问题:Devnet环境下Vue应用调用Anchor电影评论程序addMovieReview失败
本地克隆Unboxed-Software/anchor-movie-review-program测试正常,但在Vue应用中调用地址为CLA5Pk8tpcsA3RPmCYPaRNBdU6xBknRnqshsT1bKT7if的程序时,两种调用方式均交易失败。
方式一:使用Anchor Program客户端调用
代码实现
const wallet = useWallet(); const programId = new PublicKey("CLA5Pk8tpcsA3RPmCYPaRNBdU6xBknRnqshsT1bKT7if"); const provider = new anchor.AnchorProvider( connection, wallet, anchor.AnchorProvider.defaultOptions() ); // Generate the program client from IDL. Where MyRaffle is the idl generated by program const program = new anchor.Program(MyRaffle, programId, provider); const movie = { title: "Just a test movie", description: "Wow what a good movie it was real great", rating: 5, }; const [moviePda] = anchor.web3.PublicKey.findProgramAddressSync( [Buffer.from(movie.title), wallet.publicKey.value.toBuffer()], program.programId ); const tx = await program.methods.addMovieReview(movie.title, movie.description, movie.rating ).accounts({ movieReview: moviePda, initializer: wallet.publicKey.value, systemProgram: anchor.web3.SystemProgram.programId, }).rpc();
对应IDL
{ "version": "0.1.0", "name": "anchor_movie_review_program", "instructions": [ { "name": "addMovieReview", "accounts": [ { "name": "movieReview", "isMut": true, "isSigner": false, "pda": { "seeds": [ { "kind": "arg", "type": "string", "path": "title" }, { "kind": "account", "type": "publicKey", "path": "initializer" } ] } }, { "name": "initializer", "isMut": true, "isSigner": true }, { "name": "systemProgram", "isMut": false, "isSigner": false } ], "args": [ { "name": "title", "type": "string" }, { "name": "description", "type": "string" }, { "name": "rating", "type": "u8" } ] }, { "name": "updateMovieReview", "accounts": [ { "name": "movieReview", "isMut": true, "isSigner": false, "pda": { "seeds": [ { "kind": "arg", "type": "string", "path": "title" }, { "kind": "account", "type": "publicKey", "path": "initializer" } ] } }, { "name": "initializer", "isMut": true, "isSigner": true }, { "name": "systemProgram", "isMut": false, "isSigner": false } ], "args": [ { "name": "title", "type": "string" }, { "name": "description", "type": "string" }, { "name": "rating", "type": "u8" } ] }, { "name": "deleteMovieReview", "accounts": [ { "name": "movieReview", "isMut": true, "isSigner": false, "pda": { "seeds": [ { "kind": "arg", "type": "string", "path": "title" }, { "kind": "account", "type": "publicKey", "path": "initializer" } ] } }, { "name": "initializer", "isMut": true, "isSigner": true } ], "args": [ { "name": "title", "type": "string" } ] } ], "accounts": [ { "name": "MovieAccountState", "type": { "kind": "struct", "fields": [ { "name": "reviewer", "type": "publicKey" }, { "name": "rating", "type": "u8" }, { "name": "title", "type": "string" }, { "name": "description", "type": "string" } ] } } ], "metadata": { "address": "CLA5Pk8tpcsA3RPmCYPaRNBdU6xBknRnqshsT1bKT7if" } }
报错信息
AnchorError: Error Code: AccountNotInitialized. Error Number: 3012. Error Message: The program expected this account to be already initialized.
方式二:手动构造Transaction调用
代码实现
const MOVIE_REVIEW_PROGRAM_ID = 'CLA5Pk8tpcsA3RPmCYPaRNBdU6xBknRnqshsT1bKT7if'; const movie = new Movie( "Just a test movie", "Wow what a good movie it was real great", 5, ); const wallet = useWallet(); const connection = new Connection(clusterApiUrl('devnet')); const buffer = movie.serialize(); const transaction = new Transaction(); const [pda] = await PublicKey.findProgramAddressSync( [Buffer.from(movie.title), wallet.publicKey.value.toBuffer()], new PublicKey(MOVIE_REVIEW_PROGRAM_ID) ); const instruction = new TransactionInstruction({ keys: [ { pubkey: pda, isSigner: false, isWritable: true, }, { pubkey: publicKey.value, isSigner: true, isWritable: true }, { pubkey: SystemProgram.programId, isSigner: false, isWritable: false } ], data: buffer, programId: new PublicKey(MOVIE_REVIEW_PROGRAM_ID) }); transaction.add(instruction); try { let txid = await sendTransaction(transaction, connection) } catch (e) { alert(JSON.stringify(e)) }
对应Movie类
import * as borsh from "@project-serum/borsh" export class Movie { constructor(title, description, rating) { this.title = title this.description = description this.rating = rating } borshInstructionSchema = borsh.struct([ borsh.u8("variant"), borsh.str("title"), borsh.str("description"), borsh.u8("rating") ]) static borshAccountSchema = borsh.struct([ borsh.bool("initialized"), borsh.str("title"), borsh.str("description"), borsh.u8("rating") ]) serialize() { const buffer = Buffer.alloc(1000) this.borshInstructionSchema.encode({ ...this, variant: 0 }, buffer) return buffer.slice(0, this.borshInstructionSchema.getSpan(buffer)) } static deserialize(buffer) { if (!buffer) { return null } try { const { title, description , rating} = this.borshAccountSchema.decode( buffer ) return new Movie(title, description, rating) } catch (e) { console.log("Deserialization error:", e) console.log(buffer) return null } } }
报错信息
Transaction simulation failed: Error processing Instruction 0: custom program error: 0xbed(对应Anchor的AccountNotInitialized错误)
排查与修复方案
1. 核心问题定位
两种方式的报错本质都是**AccountNotInitialized**,说明程序期望的PDA账户已被初始化,或PDA生成逻辑存在偏差,也可能是指令序列化不匹配。
2. 方式一修复步骤
- 修正IDL引用:代码中使用
MyRaffle作为IDL传入anchor.Program,需替换为电影评论程序的正确IDL:import idl from './idl/anchor_movie_review_program.json'; const program = new anchor.Program(idl, programId, provider); - 验证PDA生成一致性:确认
wallet.publicKey.value正确获取钱包公钥(Vue钱包适配器中useWallet()返回的publicKey是Ref对象,需.value),且种子编码统一使用UTF-8(Buffer.from(movie.title)默认符合要求)。 - 避免重复提交:若该PDA对应的评论已存在,需调用
updateMovieReview而非addMovieReview。
3. 方式二修复步骤
- 修复Borsh序列化逻辑:Anchor指令需使用8字节的指令Discriminator,而非手动指定的
variant:0,修正序列化方法:// 获取addMovieReview指令的Discriminator const addMovieDiscriminator = anchor.utils.bytes.utf8.encode("addMovieReview").slice(0,8); serialize() { const titleBuffer = Buffer.from(this.title); const descBuffer = Buffer.from(this.description); const buffer = Buffer.alloc(8 + 4 + titleBuffer.length + 4 + descBuffer.length + 1); let offset = 0; // 写入指令Discriminator addMovieDiscriminator.copy(buffer, offset); offset +=8; // 写入title字符串(长度+内容) buffer.writeUInt32LE(titleBuffer.length, offset); offset +=4; titleBuffer.copy(buffer, offset); offset += titleBuffer.length; // 写入description字符串 buffer.writeUInt32LE(descBuffer.length, offset); offset +=4; descBuffer.copy(buffer, offset); offset += descBuffer.length; // 写入rating buffer.writeUInt8(this.rating, offset); return buffer.slice(0, offset); } - 补充交易签名参数:
sendTransaction需传入钱包作为签名者:const txid = await wallet.sendTransaction(transaction, connection);
4. 通用排查建议
- 查看交易日志:在Solana Explorer搜索失败交易ID,查看程序日志细节。
- 确认环境一致性:确保Vue应用连接的是Devnet,与本地测试环境一致。
- 检查钱包状态:确认钱包已授权应用访问,且账户有足够SOL支付手续费。
内容的提问来源于stack exchange,提问作者kc.sol
相关产品推荐
相关产品推荐

