
当 AnchorPy 生成的指令明确不需要任何签名者时,仍需为交易提供至少一个有效签名者(通常是 payer),否则会触发 solders.SignerError;解决方案是使用 VersionedTransaction 并传入一个合法的 payer 签名者,而非空列表或随机密钥对。
当 anchorpy 生成的指令明确不需要任何签名者时,仍需为交易提供至少一个有效签名者(通常是 payer),否则会触发 `solders.signererror`;解决方案是使用 `versionedtransaction` 并传入一个合法的 payer 签名者,而非空列表或随机密钥对。
在 Solana 开发中,即使智能合约逻辑本身不声明任何 is_signer=True 账户(如 timeout 指令所示),链上交易仍必须由一个有效的账户签名并支付手续费——这个账户即 payer,它必须同时满足两个条件:
- 是交易的 fee payer(即
MessageV0.payer); - 提供对应的私钥签名(即
VersionedTransaction.signatures的签名源)。
原始代码中的 timeout() 函数确实未设置任何 is_signer=True 账户,这表示合约逻辑不验证签名权限,但 Solana 运行时强制要求每笔交易至少有一个签名者(payer)。因此,以下做法均不可行:
- ❌
tx.sign([])→solders.SignerError: not enough signers(Solders 库拒绝无签名者交易); - ❌ 跳过
.sign()→Transaction实例无法序列化发送(send_transaction要求已签名); - ❌ 使用不匹配的密钥对(如随机
Keypair)→keypair-pubkey mismatch(公钥与签名内容不一致)。
✅ 正确做法是:采用 v0 版本交易(VersionedTransaction),显式指定 payer 公钥用于消息编译,并仅用该 payer 的密钥对完成签名:
from solana.transaction import AccountMeta, Instruction, MessageV0, VersionedTransaction
from solders.keypair import Keypair
from solders.pubkey import Pubkey
# 假设你已有:default_signer(Keypair 类型)、client(AsyncClient)、ix(timeout 指令)
recent_blockhash = await get_latest_blockhash(client) # 推荐使用 client.get_latest_blockhash()
msg = MessageV0.try_compile(
payer=default_signer.pubkey(), # ✅ 必须与签名者公钥一致
instructions=[ix],
address_lookup_table_accounts=[], # 若未启用地址查找表,留空
recent_blockhash=recent_blockhash.blockhash, # 注意提取 blockhash 字段
)
tx = VersionedTransaction(msg, [default_signer]) # ✅ 仅传入 payer 的 Keypair 列表
txid = await client.send_transaction(tx)
⚠️ 关键注意事项:
-
default_signer.pubkey()必须严格等于MessageV0.try_compile(..., payer=...)中传入的公钥,否则触发keypair-pubkey mismatch; - 不要混用旧版
Transaction和新版VersionedTransaction:前者强制要求所有指令签名者均显式加入.sign(),而后者将签名者与消息解耦,更灵活适配无权限校验场景; - 即使合约不检查签名,payer 账户仍需具备足够 SOL 支付交易费(通常 ≥ 0.000005 SOL);
- 若后续扩展需添加地址查找表(ALT),请将对应
AddressLookupTableAccount实例填入address_lookup_table_accounts参数。
总结:无需签名者的合约逻辑 ≠ 无需签名者的交易。始终以 payer 作为唯一且必需的签名者,配合 VersionedTransaction 流程,即可安全、合规地调用此类 AnchorPy 指令。










