春江暮客

春江暮客的个人学习分享网站

从0开始搭建自己的 Solana 钱包工具(批量生成地址/SOL 与 USDT 转账)

2026-05-06 技术

很多官方钱包适合日常使用,但一旦你想做批量地址生成、资金归集、脚本化转账,就会发现现成工具往往不够灵活。尤其在 Solana 生态里,SPL Token、ATA、主网 RPC、租金账户这些细节一多,常用钱包很快就不够用了。

前一篇我已经整理过一套 TRON 钱包工具思路:从0开始搭建自己的TRON钱包工具(批量生成地址/USDT转账/质押投票)。这篇继续补上 Solana 版本,重点还是放在最常用、最容易脚本化的两个场景:地址批量生成,以及 SOL / USDT 转账。

这篇文章整理一套非常直接的 Solana 钱包工具思路,只做两件高频操作:

  1. 批量生成 Solana 钱包地址
  2. 查询余额并转账 SOL / USDT

对应 2 个 Python 脚本:

  • generate_wallet_solana.py:多进程批量生成私钥与地址
  • solana_transfer.py:查询 SOL/USDT 余额,并自动执行 USDT 或 SOL 转账

下面按可执行流程拆开说明。

一、Solana 钱包与地址的基础概念

和 TRON、ETH 常见的 secp256k1 不一样,Solana 钱包默认使用 Ed25519 密钥体系。

Solana 地址本质上就是公钥的 Base58 编码,因此地址生成逻辑相对直接:

  1. 生成 32 字节私钥
  2. 推导 32 字节公钥
  3. 将公钥做 Base58 编码,得到钱包地址

通常导出时会把 私钥 + 公钥 拼成 64 字节,再做 Base58 编码。这也是很多脚本和钱包工具里常见的 priv_b58 格式。

如果你要转的是 USDT,需要额外理解一个概念:

  • Solana 上的 USDT 不是“直接挂在主地址上”
  • 它属于 SPL Token
  • 每个地址对应某个 Token 时,都需要一个关联代币账户,也就是 ATA(Associated Token Account)

所以做 USDT 转账时,除了发送地址和接收地址,还必须处理双方的 ATA。

二、环境准备

1. 安装依赖

pip install solana solders spl-token pynacl base58

如果你只是生成地址,核心依赖是:

  • pynacl
  • base58

如果你要发起链上转账,还需要:

  • solana
  • solders
  • spl-token

2. 运行前说明

  • 脚本里会处理真实私钥,只建议在可信环境中运行。
  • 不要把导出的 csv 文件、Base58 私钥字符串提交到公开仓库。
  • 主网 RPC 建议替换成你自己的服务商节点,公开 RPC 在高频请求下容易限流。

三、批量生成 Solana 钱包地址(generate_wallet_solana.py)

这个脚本适合做地址池、批量分发钱包、归集前预生成地址。

核心逻辑很简单:

  1. SigningKey.generate() 生成 Ed25519 私钥
  2. 推导公钥
  3. 公钥做 Base58 编码得到地址
  4. 私钥 + 公钥 拼接后做 Base58 编码,得到可导入的 priv_b58
  5. 多进程并发写入多个 csv 文件

1. 运行方式

python3 generate_wallet_solana.py 1000000

如果不传参数,默认生成 1000000 个地址。

2. 输出格式

输出目录类似:

wallet-tool/sol_batch/1746500000000_T16_N1000000_sol_wallets/

每个 worker 会生成一个 csv,单行格式如下:

私钥hex,Base58私钥,Solana地址

其中第二列最实用,因为它可以直接用于后面的转账脚本。

3. 完整脚本(generate_wallet_solana.py)

#!/usr/bin/env python3

import os
import time
import base58
import multiprocessing as mp
from nacl.signing import SigningKey

BASE_DIR = "wallet-tool/sol_batch"
DEFAULT_COUNT = 1000000


def generate_batch(args):
    count, worker_id, outdir = args
    filepath = os.path.join(outdir, f"sol_wallets_{worker_id}.csv")

    with open(filepath, "w", buffering=1024*1024) as f:
        for _ in range(count):
            sk = SigningKey.generate()
            priv = sk.encode()
            pub = sk.verify_key.encode()

            address = base58.b58encode(pub).decode()
            priv_b58 = base58.b58encode(priv + pub).decode()

            f.write(f"{priv.hex()},{priv_b58},{address}\n")


def create_output_dir(total):
    ts = int(time.time() * 1000)
    folder = f"{ts}_T{mp.cpu_count()}_N{total}_sol_wallets"
    path = os.path.join(BASE_DIR, folder)
    os.makedirs(path, exist_ok=True)
    return path


def main():
    import sys

    total = int(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_COUNT
    cpu = mp.cpu_count()

    outdir = create_output_dir(total)
    per_worker = total // cpu

    print(f"Generating {total} SOL wallets")

    start = time.time()
    args = [(per_worker, i, outdir) for i in range(cpu)]

    with mp.Pool(cpu) as p:
        p.map(generate_batch, args)

    elapsed = time.time() - start

    print("Done")
    print(f"Time: {elapsed:.2f} sec")
    print(f"Speed: {int(total/elapsed):,} wallets/sec")


if __name__ == "__main__":
    main()

4. 使用建议

  • 当前脚本使用 total // cpu 分配任务,余数会被丢掉。
  • 如果你要求“严格生成 N 个地址”,需要额外补一个 remainder worker。
  • 输出的 csv 风险极高,建议生成完马上加密归档。

四、查询余额并转账 SOL / USDT(solana_transfer.py)

第二个脚本负责做归集或手动转账。

执行逻辑是:

  1. 从命令行读取 Base58 私钥
  2. 推导发送地址
  3. 查询 SOL 余额
  4. 计算发送地址的 USDT ATA 并查询 USDT 余额
  5. 如果 USDT 大于 0,优先走 SPL Token 转账
  6. 如果没有 USDT 但有足够 SOL,则执行原生 SOL 转账

1. 先修改配置

至少确认这几个常量:

RPC_URL = "https://api.mainnet-beta.solana.com"
USDT_MINT = Pubkey.from_string("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
DECIMALS = 6
TO_ADDRESS_STR = "2Z1DtyfHifHeVZSYkN9J2xzy9KjC4oeHNLeFn2U9VVXU" #你的收款地址

这里的 USDT_MINT 是 Solana 主网 USDT 的 Mint 地址,DECIMALS = 6 对应 USDT 精度。示例里直接把收款地址写死在常量里,方便测试;正式使用时,更建议改成命令行参数或配置文件。

2. 完整脚本(solana_transfer.py)

#!/usr/bin/env python3

import sys
from solana.rpc.api import Client
from solders.keypair import Keypair
from solders.pubkey import Pubkey
from solders.system_program import transfer, TransferParams
from solders.transaction import Transaction
from spl.token.instructions import (
    transfer_checked,
    TransferCheckedParams,
    get_associated_token_address,
    create_associated_token_account,
)
from spl.token.constants import TOKEN_PROGRAM_ID


RPC_URL = "https://api.mainnet-beta.solana.com"
USDT_MINT = Pubkey.from_string("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB")
DECIMALS = 6
TO_ADDRESS = Pubkey.from_string("2Z1DtyfHifHeVZSYkN9J2xzy9KjC4oeHNLeFn2U9VVXU") #你的收款地址


try:
    priv_b58 = sys.argv[1]
    keypair = Keypair.from_base58_string(priv_b58)
except Exception:
    print("Key Error: please pass a Base58 private key")
    print("Example: python3 solana_transfer.py 5Mpx...")
    sys.exit(1)

sender = keypair.pubkey()
client = Client(RPC_URL)
print(f"Sender: {sender}")


sol_balance = client.get_balance(sender).value / 1e9
sender_ata = get_associated_token_address(sender, USDT_MINT)

usdt_balance = 0
try:
    token_res = client.get_token_account_balance(sender_ata)
    usdt_balance = float(token_res.value.ui_amount)
except Exception:
    usdt_balance = 0

print(f"SOL Balance: {sol_balance}")
print(f"USDT Balance: {usdt_balance}")


if usdt_balance > 0:
    amt = float(input(f"Enter USDT amount (max {usdt_balance}): "))
    receiver_ata = get_associated_token_address(TO_ADDRESS, USDT_MINT)

    instructions = []

    if client.get_account_info(receiver_ata).value is None:
        print("Creating ATA for receiver...")
        instructions.append(
            create_associated_token_account(sender, TO_ADDRESS, USDT_MINT)
        )

    instructions.append(
        transfer_checked(
            TransferCheckedParams(
                program_id=TOKEN_PROGRAM_ID,
                source=sender_ata,
                mint=USDT_MINT,
                dest=receiver_ata,
                owner=sender,
                amount=int(amt * 10**DECIMALS),
                decimals=DECIMALS,
            )
        )
    )

    txn = Transaction(instructions, sender, [keypair])
    res = client.send_transaction(txn, keypair)
    print(f"USDT Sent: https://solscan.io/tx/{res.value}")

elif sol_balance > 0.001:
    amt = float(input(f"Enter SOL amount (max {sol_balance}): "))

    ix = transfer(
        TransferParams(
            from_pubkey=sender,
            to_pubkey=TO_ADDRESS,
            lamports=int(amt * 1e9),
        )
    )

    latest_blockhash = client.get_latest_blockhash().value.blockhash

    txn = Transaction.new_signed_with_payer(
        [ix],
        sender,
        [keypair],
        latest_blockhash,
    )

    res = client.send_transaction(txn)
    print(f"SOL Sent: https://solscan.io/tx/{res.value}")

else:
    print("Insufficient balance for transfer")

3. 运行方式

python3 solana_transfer.py 你的Base58私钥

执行后会先打印地址和余额,例如:

Sender: 7t9U...abcd
SOL Balance: 0.0831
USDT Balance: 15.0

如果当前地址持有 USDT,脚本会优先让你输入 USDT 转账数量;如果没有 USDT,但有足够 SOL,则进入 SOL 转账流程。

4. 这个脚本解决了什么问题

这个脚本虽然不复杂,但已经覆盖了 Solana 转账里最容易踩坑的几个点:

  1. 自动从 Base58 私钥恢复钱包
  2. 自动计算发送方和接收方的 USDT ATA
  3. 接收方没有 ATA 时自动创建
  4. 同一个脚本里兼容 SPL Token 转账和原生 SOL 转账

对于归集场景,这已经足够实用。


五、验证方式

1. 先做小额测试

无论是 SOL 还是 USDT,都先用极小金额验证一次:

0.001 SOL
1 USDT

确认以下几件事:

  1. 交易哈希能正常打印
  2. Solscan 能查到交易
  3. 接收地址余额确实变化
  4. 如果是 USDT,接收方 ATA 能被自动创建

2. 用 Solscan 检查结果

脚本执行成功后会输出:

https://solscan.io/tx/交易哈希

直接打开就可以确认:

  • 是否上链成功
  • 具体扣除了多少手续费
  • 是否触发了 ATA 创建
  • Token 数量和接收地址是否正确

六、常见问题与直接修复

1. ModuleNotFoundError: No module named 'spl'

说明 SPL Token 相关依赖没有装好,重新安装:

pip install spl-token solana solders

2. Base58 私钥导入失败

常见原因:

  • 传入了 32 字节 seed,而不是 私钥 + 公钥 的 64 字节导出格式
  • 私钥字符串被截断
  • csv 读取时带了换行或空格

最直接的排查办法就是先打印字符串长度,并确认它来自生成脚本的第二列。

3. USDT 转账时报余额足够但交易失败

Solana 上 Token 转账不只看 Token 余额,还要看:

  • 是否有足够 SOL 支付手续费
  • 接收方是否需要新建 ATA
  • 新建 ATA 时发送方是否有足够 SOL 支付租金

如果钱包里只有 USDT 没有 SOL,交易通常也会失败。

4. 批量生成数量不准确

原因就是前面提到的:

per_worker = total // cpu

余数没有处理,所以严格模式下需要补逻辑。

5. 转账地址被多次覆盖

有些测试脚本会写成:

TO_ADDRESS_STR = "地址1"
TO_ADDRESS_STR = "地址2"
TO_ADDRESS_STR = "地址3"

最终只有最后一个地址生效。正式使用时一定只保留一个目标地址,或者改成命令行参数。


七、总结

如果你的目标是自己掌控 Solana 钱包的底层流程,这套工具已经够用了:一个脚本负责生成地址,一个脚本负责查询并转账。它没有做成“大而全”的钱包应用,但在批量地址池、自动归集、脚本化打款这类场景里反而更直接、更实用。

后续如果要继续扩展,也很自然,比如:

  • 批量读取 csv 做归集
  • 支持多个收款地址轮询
  • 增加代币 mint 参数,支持其他 SPL Token
  • 把目标地址、金额、RPC 改成命令行参数

建议先把最基础的“生成地址 + 小额转账 + 链上验证”跑通,再继续扩展批量归集和多币种支持。这样排查问题最省时间,也更适合后面逐步演进成自己的钱包工具链。

友情链接

其它