使用Python的Solders库从JSON钱包文件转移SOL时遇ValueError错误求助
使用Python的Solders库从JSON钱包文件转移SOL时遇ValueError错误求助
看起来你遇到的ValueError是因为处理私钥的方式不对,我们一步步来解决这个问题:
错误原因分析
你代码里的create_keypair函数用了bytes.fromhex(private_key)来转换私钥,但JSON里的私钥是Base58编码的Solana标准私钥格式,不是十六进制字符串。Solana的私钥默认是用Base58编码的,用十六进制解析肯定会遇到非0-9/a-f的字符,直接触发这个错误。
修复步骤
1. 替换私钥解码方式
首先需要安装base58库(如果没装的话),用Base58解码私钥,而不是十六进制:
pip install base58
然后修改create_keypair函数(其实不需要传入公钥,Keypair可以直接从私钥推导公钥):
import base58 # 新增导入 def create_keypair(private_key: str) -> Keypair: # 用Base58解码Solana私钥 private_bytes = base58.b58decode(private_key) return Keypair.from_secret_key(private_bytes)
2. 修复接收方地址处理
你现在的代码里receiver是用户输入的字符串,直接用receiver.pubkey会报错,需要把它转换成solders.pubkey.Pubkey对象:
from solders.pubkey import Pubkey # 新增导入 # 替换原来的receiver输入部分 receiver_addr = input(str("To which Address should be the SOL sent? \n --> ")) receiver = Pubkey.from_string(receiver_addr)
3. 修复交易创建与发送的细节
你的代码还有几个遗漏的问题:
MessageV0.try_compile里的sender.pubkey()是错误写法,应该用sender.pubkey(Keypair的pubkey是属性,不是方法)- 固定转2140000 lamports不合理,应该获取钱包余额后扣除手续费再转出
- 你只创建了交易但没有发送到链上,需要补充发送和确认逻辑
- Blockhash需要用返回值里的
blockhash字段,还要注意它的过期时间
完整修复后的代码
from solders.keypair import Keypair from solders.pubkey import Pubkey from solana.rpc.api import Client from solana.rpc.types import TxOpts from solders.system_program import transfer, TransferParams from solders.transaction import VersionedTransaction from solders.message import MessageV0 import json import base58 SOLANA_RPC_URL = "https://api.mainnet-beta.solana.com" client = Client(SOLANA_RPC_URL) with open(r"C:\Users\Fabsen\Desktop\Coding\Python\Projekte\Volume Bot\wallets.json", "r") as file: wallets_data = json.load(file) def create_keypair(private_key: str) -> Keypair: # 用Base58解码Solana私钥 private_bytes = base58.b58decode(private_key) return Keypair.from_secret_key(private_bytes) # 生成发送方Keypair列表(无需传入公钥,私钥可直接推导) senders = [ create_keypair(wallet['private key']) for wallet in wallets_data ] # 处理接收方地址 receiver_addr = input(str("To which Address should be the SOL sent? \n --> ")) receiver = Pubkey.from_string(receiver_addr) # 获取最新Blockhash blockhash_resp = client.get_latest_blockhash() blockhash = blockhash_resp.value.blockhash # 逐个处理钱包转账 for idx, sender in enumerate(senders): print(f"正在处理钱包 {idx+1}: {sender.pubkey}") # 获取钱包当前余额(单位:lamports) balance_resp = client.get_balance(sender.pubkey) balance = balance_resp.value if balance <= 5000: # 预留足够手续费,避免余额不足 print(f"钱包 {sender.pubkey} 余额不足,跳过") continue # 计算可转账金额:余额减去手续费(这里留5000 lamports,可根据交易大小调整) transfer_lamports = balance - 5000 # 构建转账指令 ix = transfer( TransferParams( from_pubkey=sender.pubkey, to_pubkey=receiver, lamports=transfer_lamports, ) ) # 构建V0版本交易消息 msg = MessageV0.try_compile( payer=sender.pubkey, instructions=[ix], address_lookup_table_accounts=[], recent_blockhash=blockhash, ) # 签名交易 tx = VersionedTransaction(msg, [sender]) # 发送并确认交易 try: send_resp = client.send_transaction(tx, opts=TxOpts(skip_preflight=True)) tx_signature = send_resp.value print(f"交易已发送,签名: {tx_signature}") # 等待交易确认 confirm_resp = client.confirm_transaction(tx_signature) if confirm_resp.value.err is None: print(f"交易 {tx_signature} 已成功确认") else: print(f"交易 {tx_signature} 失败: {confirm_resp.value.err}") except Exception as e: print(f"处理钱包 {sender.pubkey} 时出错: {str(e)}")
额外注意事项
- 手续费预留:一定要给钱包留足够的手续费(至少5000 lamports),否则交易会因为余额不足失败。
- Blockhash过期:Solana的Blockhash约2分钟后过期,如果处理大量钱包,建议每处理几个就重新获取一次最新Blockhash。
- 测试建议:在主网操作前,务必先在Devnet测试代码,避免资金损失。
- 冗余字段:JSON里的
public key字段其实可以不用,Keypair能直接从私钥推导公钥,所以代码里我简化了这部分逻辑。
备注:内容来源于stack exchange,提问作者Fabiano_Pe
相关产品推荐
相关产品推荐

