2.0 版本是 Python 包的首个稳定版本。 2.0 迁移指南 介绍了它与 Rust 和 TypeScript 共有的签名形态。
安装
安装基础包,并为每个需要提供商依赖的后端安装对应的一个 extra:
pip install solana-keychain # memory, vault and parapip install 'solana-keychain[aws-kms]' # AWS KMS (boto3)pip install 'solana-keychain[gcp-kms]' # GCP KMS (google-cloud-kms)pip install 'solana-keychain[privy]' # Privypip install 'solana-keychain[turnkey]' # Turnkeypip install 'solana-keychain[fireblocks]' # Fireblockspip install 'solana-keychain[cdp]' # Coinbase Developer Platformpip install 'solana-keychain[crossmint]' # Crossmintpip install 'solana-keychain[dfns]' # Dfnspip install 'solana-keychain[openfort]' # Openfortpip install 'solana-keychain[utila]' # Utilapip install 'solana-keychain[fordefi]' # Fordefi# Several at oncepip install 'solana-keychain[aws-kms,privy]'
需要 Python 3.10 或更高版本。交易为
solders 的 VersionedTransaction 对象;
legacy、v0 和 v1 消息均可接受,但使用 use_program_call=True 的 Fireblocks 除外,它仅接受 legacy 和 v0。
Memory、Vault 和 Para 可直接从包根目录导入。其他所有后端
都需从各自的子模块导入(例如 solana_keychain.aws_kms),
若未安装对应的 extra 就导入,会抛出 ImportError 并指明需要安装的 extra。Ledger 仅支持 Rust。
基本用法
每个后端都有一个配置 dataclass 和一个异步的 create_<backend>_signer
工厂函数,返回可直接使用的签名器。所有签名方法均为协程:
import asynciofrom solana_keychain import MemorySignerfrom solders.transaction import VersionedTransactionasync def sign(transaction: VersionedTransaction) -> str:signer = MemorySigner.from_private_key_file("/path/to/id.json")print("Signing with:", signer.pubkey)# Check availability (useful for remote signers)if not await signer.is_available():raise RuntimeError("Signer offline")result = await signer.sign_transaction(transaction)print("Signature:", result.signature)print("Fully signed:", result.is_complete)return result.encoded_transaction # base64 wire transaction
sign_transaction 返回 SignedTransaction,其中包含 encoded_transaction、
signature、is_complete 和 transaction。transaction 是权威的
已签名交易:请基于它继续后续操作,尤其是使用会修改交易的签名器时,
其结果与你传入的对象并非同一个对象。
统一工厂
create_keychain_signer 接收后端名称及该后端的配置
dataclass,并按需延迟导入后端,因此只需安装你用到的 extra:
from solana_keychain import create_keychain_signerfrom solana_keychain.privy import PrivySignerConfigsigner = await create_keychain_signer("privy",PrivySignerConfig(app_id="your-app-id",app_secret="your-app-secret",wallet_id="your-wallet-id",),)
支持的名称列于 SUPPORTED_BACKENDS 中:aws-kms、cdp、crossmint、
dfns、fireblocks、fordefi、gcp-kms、memory、openfort、para、
privy、turnkey、utila 和 vault。传入未知名称,或配置类不匹配,
会抛出 SignerError,错误码为 SignerErrorCode.CONFIG_ERROR。该
工厂的返回类型标注为基类 SolanaSigner,因此在签名前请先用
isinstance 缩小类型(参见 签名器能力)。
远程 HTTP 客户端
远程 HTTP 后端接受可选的 http_client(一个 httpx.AsyncClient),
用于自定义 TLS 或代理。未设置时,每个请求都使用一次性客户端,
超时时间为 60 秒,强制对基础 URL 使用 HTTPS,并拒绝
重定向。
签名器能力
每个后端都继承 SolanaSigner ABC(pubkey、sign_message、
is_available),外加恰好一个能力类:
TransactionSigner:sign_transaction(tx)就地为你的交易签名, 并返回SignedTransaction。由你自行广播。ModifyingSigner:modify_and_sign_transaction(tx)允许提供商重写 交易,然后签名但不广播。你的对象保持不变; 请基于返回的SignedTransaction.transaction继续操作。SendingSigner:sign_and_send_transaction(tx)通过提供商 签名并广播,返回最终上链交易的Signature。你的 交易不会被修改。
| 后端 | 能力类 | sign_message |
|---|---|---|
| memory, vault, privy, turnkey, aws-kms, fireblocks, gcp-kms, dfns, para, openfort | TransactionSigner | 支持 |
| cdp | TransactionSigner | 仅支持 UTF-8 载荷,否则抛出 SERIALIZATION_ERROR |
| utila | TransactionSigner | 抛出 SIGNING_FAILED |
fordefi 黑盒模式(FordefiBlackBoxSigner) | TransactionSigner | 支持 |
fordefi 原生手动模式(FordefiNativeManualSigner) | ModifyingSigner | 支持 |
fordefi 原生自动模式(FordefiNativeAutoSigner) | SendingSigner | 支持 |
| crossmint | SendingSigner | 抛出 SIGNING_FAILED |
使用 isinstance 缩小类型。所有能力类均从包根目录导出:
from solana_keychain import ModifyingSigner, SendingSigner, TransactionSignerif isinstance(signer, SendingSigner):signature = await signer.sign_and_send_transaction(transaction)elif isinstance(signer, ModifyingSigner):result = await signer.modify_and_sign_transaction(transaction)elif isinstance(signer, TransactionSigner):result = await signer.sign_transaction(transaction)
初始化
create_* 工厂函数和 create_keychain_signer 会为你完成所有必要的
初始化。如果你直接构造 Privy、Fireblocks、Dfns、Crossmint、Para、Openfort 或
Utila 签名器类,请在使用前先执行 await signer.init()。在此之前,
pubkey 以及所有签名调用都会抛出 SignerErrorCode.NOT_INITIALIZED。
后端配置
Memory
可通过 Solana CLI keypair 文件、base58 或 "[1,2,...]" 字符串、原始
字节,或 solders 的 Keypair 构建:
from solana_keychain import MemorySigner, MemorySignerConfig, create_memory_signerfrom solders.keypair import Keypairsigner = MemorySigner.from_private_key_file("/path/to/id.json")signer = MemorySigner.from_private_key_string("base58_private_key")signer = MemorySigner.from_bytes(secret_key_bytes) # 64 or 32 bytessigner = await create_memory_signer(MemorySignerConfig(keypair=Keypair()))
密钥保存在进程内存中,而 Python 无法将其清零。请将此后端 用于开发和测试。
HashiCorp Vault
密钥必须是 transit 引擎中的 ed25519 密钥。仅对环回地址接受明文 HTTP,
用于 vault server -dev:
from solana_keychain import VaultSignerConfig, create_vault_signersigner = await create_vault_signer(VaultSignerConfig(api_base_url="https://vault.example.com:8200",token="hvs.xxxxx",key_name="my-solana-key",public_key="base58_public_key",))
AWS KMS
key_id 是 ECC_NIST_EDWARDS25519 密钥的密钥 ID、ARN 或别名。如需自定义凭证或端点,可传入
预先构建的 boto3 KMS client;此时 region 会被
忽略:
from solana_keychain.aws_kms import AwsKmsSignerConfig, create_aws_kms_signersigner = await create_aws_kms_signer(AwsKmsSignerConfig(key_id="alias/my-solana-key",public_key="base58_public_key",region="us-east-1", # optional, defaults to the AWS config chain))
GCP KMS
密钥版本必须是 EC_SIGN_ED25519 密钥。client 可选地接受
预先构建的 KeyManagementServiceAsyncClient:
from solana_keychain.gcp_kms import GcpKmsSignerConfig, create_gcp_kms_signersigner = await create_gcp_kms_signer(GcpKmsSignerConfig(key_name="projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1",public_key="base58_public_key",))
Privy
from solana_keychain.privy import PrivySignerConfig, create_privy_signersigner = await create_privy_signer(PrivySignerConfig(app_id="app_id",app_secret="app_secret",wallet_id="wallet_id",))
受授权密钥保护的钱包,需将 PrivyAuthorizationConfig 作为
authorization_context 传入。
Turnkey
from solana_keychain.turnkey import TurnkeySignerConfig, create_turnkey_signersigner = await create_turnkey_signer(TurnkeySignerConfig(api_public_key="api_public_key", # hex P-256api_private_key="api_private_key", # hex P-256organization_id="org_id",private_key_id="private_key_id",public_key="base58_public_key",))
Fireblocks
from solana_keychain.fireblocks import FireblocksSignerConfig, create_fireblocks_signersigner = await create_fireblocks_signer(FireblocksSignerConfig(api_key="api_key",private_key_pem="-----BEGIN RSA PRIVATE KEY-----\n...",vault_account_id="0",asset_id="SOL", # or "SOL_TEST" for devnet))
use_program_call=True 使用 PROGRAM_CALL 操作而非 RAW 进行签名。
它以仅签名方式发送,因此 Fireblocks 既不会重写也不会广播
交易,你照常自行广播即可。它需要热钱包,并且必须由 Fireblocks 为你的工作区
启用。
CDP(Coinbase Developer Platform)
from solana_keychain.cdp import CdpSignerConfig, create_cdp_signersigner = await create_cdp_signer(CdpSignerConfig(api_key_id="api_key_id",api_key_secret="api_key_secret",wallet_secret="wallet_secret",address="base58_address",network="solana", # or "solana-devnet"; required for address lookup tables))
Crossmint
from solana_keychain.crossmint import CrossmintSignerConfig, create_crossmint_signersigner = await create_crossmint_signer(CrossmintSignerConfig(api_key="api_key",wallet_locator="wallet_locator",))signature = await signer.sign_and_send_transaction(transaction)
Crossmint 仅支持发送。其 API 始终在服务端执行已批准的交易
并代付 gas,因此 CrossmintSigner 是一个 SendingSigner,
sign_message 会抛出 SIGNING_FAILED。返回的签名对应
Crossmint 最终上链的交易,该交易可能与你提交的不同。设置
signer_secret(一个 xmsk1_... 委托签名者密钥)会自动批准交易,
这意味着将批准哪些内容的决定权委托给了 Crossmint。
Dfns
from solana_keychain.dfns import DfnsSignerConfig, create_dfns_signersigner = await create_dfns_signer(DfnsSignerConfig(auth_token="auth_token",cred_id="cred_id",private_key_pem="-----BEGIN EC PRIVATE KEY-----\n...",wallet_id="wallet_id",))
Openfort
from solana_keychain.openfort import OpenfortSignerConfig, create_openfort_signersigner = await create_openfort_signer(OpenfortSignerConfig(secret_key="sk_...",account_id="acc_...",wallet_secret="wallet_secret",))
Para
from solana_keychain import ParaSignerConfig, create_para_signersigner = await create_para_signer(ParaSignerConfig(api_key="sk_...",wallet_id="wallet_uuid",))
Utila
Utila 仅对交易签名;sign_message 会抛出 SIGNING_FAILED。
from solana_keychain.utila import UtilaSignerConfig, create_utila_signersigner = await create_utila_signer(UtilaSignerConfig(service_account_email="sa@example.com",service_account_private_key_pem="-----BEGIN PRIVATE KEY-----\n...",vault_id="vault_id",wallet_id="wallet_id",network="networks/solana-devnet",))
Fordefi
Fordefi 有三种签名模式,由构造时的 chain 和
push_mode 决定。create_fordefi_signer 会选择对应的类,每个类都会拒绝
用于其他类的配置,并且三者都支持消息签名:
| 模式 | 配置 | 类 | 方法 |
|---|---|---|---|
| 黑盒 | 未设置 chain | FordefiBlackBoxSigner | sign_transaction |
| 原生自动 | 已设置 chain,push_mode 未设置或为 "auto" | FordefiNativeAutoSigner | sign_and_send_transaction |
| 原生手动 | 已设置 chain,且 push_mode="manual" | FordefiNativeManualSigner | modify_and_sign_transaction |
API 请求使用 ECDSA P-256 请求签名进行身份验证:可将
密钥以 private_key_pem 提供,或将其保存在 KMS/HSM 中,并通过
request_signer 传入 FordefiRequestSigner 子类。
原生自动模式:设置 chain("solana_devnet" 或 "solana_mainnet"),
Fordefi 可能会替换 blockhash 和手续费,然后自行签名并广播
交易。它只接受唯一必需签名者为该保险库(vault)的未签名交易。fee 会原样
传递给 Fordefi:
import osfrom solana_keychain.fordefi import FordefiSignerConfig, create_fordefi_signersigner = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_VAULT_ID"],public_key=os.environ["FORDEFI_PUBLIC_KEY"], # vault address (base58)private_key_pem=open("./secret/private.pem").read(),chain="solana_devnet",fee={"type": "custom", "priority_fee": "1000"}, # optional))signature = await signer.sign_and_send_transaction(transaction)
原生手动模式:设置 chain 和 push_mode="manual"。Fordefi 会重写
消息(最新 blockhash、Compute Budget 手续费指令)并签名,
但不会广播。返回的交易会取代你提交的交易,
因此请始终基于 result.transaction 继续操作。Fordefi 必须是手续费支付方,
且必须先于其他任何必需签名者签名:已带有
签名的交易会被接受,但重写会使这些签名失效。重写内容不会生成差异对比,
因此广播前请检查结果,并尽快广播,因为 Fordefi
不会返回新 blockhash 的过期高度。
signer = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_VAULT_ID"],public_key=os.environ["FORDEFI_PUBLIC_KEY"],private_key_pem=open("./secret/private.pem").read(),chain="solana_devnet",push_mode="manual",))result = await signer.modify_and_sign_transaction(transaction)... # inspect result.transaction, then add any remaining signatures
按照 签名并发送 中的方式广播经检查的交易,如果发送失败, 请先对账再重试。
黑盒模式:省略 chain。Fordefi 会对你提供的确切消息字节签名,
且不会广播。请将其与 Fordefi 黑盒保险库配合使用。
signer = await create_fordefi_signer(FordefiSignerConfig(access_token=os.environ["FORDEFI_ACCESS_TOKEN"],vault_id=os.environ["FORDEFI_BB_VAULT_ID"],public_key=os.environ["FORDEFI_BB_PUBLIC_KEY"],private_key_pem=open("./secret/private.pem").read(),))
签名并发送
无论签名器是何种形态,sign_and_send_transaction 都能将交易发送上链。
SendingSigner 通过其提供商广播,你的发送函数
不会被调用。TransactionSigner 或 ModifyingSigner 负责签名,而你的发送
函数负责广播 base64 编码后的结果(该包不含 RPC 客户端):
import httpxfrom solana_keychain import sign_and_send_transactionfrom solders.signature import Signatureasync def send(encoded: str) -> Signature:async with httpx.AsyncClient() as client:response = await client.post("https://api.devnet.solana.com",json={"jsonrpc": "2.0","id": 1,"method": "sendTransaction","params": [encoded, {"encoding": "base64"}],},)return Signature.from_string(response.json()["result"])signature = await sign_and_send_transaction(signer, transaction, send)
仅签名的签名器若没有发送函数,会抛出 CONFIG_ERROR;签名后
交易仍缺少签名,则会抛出 SIGNING_FAILED。
错误处理
每个失败都是一个 SignerError,带有来自 SignerErrorCode 的稳定 code:
BROADCAST_UNCONFIRMED、CONFIG_ERROR、EXPECTED_SOLANA_SIGNER、HTTP_ERROR、
INVALID_PRIVATE_KEY、INVALID_PUBLIC_KEY、IO_ERROR、NOT_AVAILABLE、
NOT_INITIALIZED、OTHER、PARSING_ERROR、REMOTE_API_ERROR、
SERIALIZATION_ERROR 和 SIGNING_FAILED。请根据 code 匹配,而不是消息内容:
str() 和 repr() 只包含固定的通用消息,绝不会包含密钥材料或
提供商的原始响应。当失败源自提供商的响应时,会设置 status_code 和 provider_error_code。
SignerErrorCode.BROADCAST_UNCONFIRMED("SIGNER_BROADCAST_UNCONFIRMED")表示
交易可能已经上链:
- 在 Crossmint 和 Fordefi 原生自动模式下,创建请求被接受时会设置
provider_transaction_id,idempotency_key则携带由消息派生、 创建请求所使用的密钥。重新发送字节完全相同的 交易是安全的;重新构建的交易(例如使用了新的 blockhash)则是一笔新的 转账。 - 在
sign_and_send_transaction中,若发送函数失败,则会抛出该错误, 且transaction_signature设置为已完成交易的签名。 - 仅当轮询显示 Fireblocks 广播了一笔
use_program_call交易 (尽管该交易是以仅签名方式发送的)时,Fireblocks 才会抛出该错误。
from solana_keychain import SignerError, SignerErrorCodetry:signature = await sign_and_send_transaction(signer, transaction, send)except SignerError as error:if error.code is SignerErrorCode.BROADCAST_UNCONFIRMED:reconcile(error.provider_transaction_id,error.idempotency_key,error.transaction_signature,)raise
被取消的调用会重新抛出 asyncio.CancelledError,它不携带任何字段。若要
找回提供商在取消前已接受的 id,请将
PendingTransactionId()(来自 solana_keychain.core)作为 Crossmint 或
Fordefi 原生自动配置的 pending_transaction_id 传入,并在取消后
读取 .get()。重试前请先与提供商对账。
Keychain 验证的是签名环节,而不是交易本身的行为:提供商 返回的签名在附加之前,会针对本地计算出的消息进行 ed25519 验证。Python 无法将密钥缓冲区清零,因此对于 本地密钥后端(Memory,以及远程后端配置中保存的 API 密钥和 PEM), 应将整个进程内存视为敏感数据。各种签名形态 提供哪些保证,请参阅 安全模型。
添加自定义签名器
请从 solana_keychain.core 中继承与您的后端相匹配的那个能力类(TransactionSigner、
ModifyingSigner 或 SendingSigner)。若要集成
其他密钥管理服务,请参阅
添加签名器指南。
资源
Is this page helpful?