Serum Anchor中实现双账户向同一Vault转账时遇自定义程序错误0x3的排查求助
分析你的Solana智能合约错误(0x3)
错误码0x3在Solana自定义程序错误中对应ConstraintHasOne,也就是你的SecondSend结构体里的has_one约束检查失败了。我们一步步排查问题:
1. from账户的has_one = owner约束不满足
看你的测试代码:
await program.rpc.secondSend({ accounts: { // ... "from": satan, "owner": program.provider.wallet.publicKey, // ... }, });
这里的from是satan对应的TokenAccount,而TokenAccount的owner应该是satan的钱包公钥,不是program.provider.wallet.publicKey。你的合约里from字段标注了has_one = owner,意味着传入的owner必须等于from这个TokenAccount的owner字段值,这是当前最可能触发0x3错误的原因。
修复方案:把测试里的owner改成satan对应的钱包公钥,比如如果satan是你创建的测试钱包,就用satan.publicKey。
2. 确认check的has_one = vault约束
你的check账户标注了has_one = vault,需要确保测试中传入的vault公钥和创建支票时存入check.vault的公钥完全一致。如果创建支票时用的是另一个vault地址,这里就会触发约束错误。
可以在测试中打印check.account.vault和传入的vault.publicKey,对比是否相等。
3. 验证check_signer的PDA是否正确
合约中second_send用的是支票账户的PDA作为签名者,seeds是:
let seeds = &[ ctx.accounts.check.to_account_info().key.as_ref(), &[ctx.accounts.check.nonce], ];
所以测试中的checkSigner必须是通过相同seeds生成的PDA:
const [checkSigner] = await web3.PublicKey.findProgramAddress( [check.publicKey.toBuffer(), Buffer.from([nonce])], // nonce是创建支票时用的那个nonce program.programId );
如果你的checkSigner不是这么生成的,签名验证会失败,也可能间接触发约束错误。
4. 其他潜在问题
- 确保
satan的TokenAccount余额足够支付check.amount,余额不足也可能导致错误,但0x3更偏向约束问题。 - 检查
Check结构体中burned字段是否在创建时初始化为false,如果没有初始化,可能导致后续逻辑错误,但不是当前0x3的直接原因。
把这些点逐一排查,尤其是from和owner的匹配问题,应该就能解决这个错误了。
内容的提问来源于stack exchange,提问作者annie70




