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。如果名称未知,或配置的类不正确,将抛出带有 SignerErrorCode.CONFIG_ERROR 的 SignerError。该工厂的类型声明为返回基类 SolanaSigner,因此在签名前请先用 isinstance 缩小类型(参见签名器能力)。
远程 HTTP 客户端
远程 HTTP 后端接受可选的 http_client(一个 httpx.AsyncClient),用于自定义 TLS 或代理。未设置时,每个请求都会使用一次性客户端,超时时间为 60 秒,基础 URL 强制使用 HTTPS,并拒绝重定向。
签名器能力
每个后端都继承 SolanaSigner 抽象基类(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 密钥。仅当地址为回环地址时(用于 vault server -dev)才接受纯 HTTP:
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 可能会替换区块哈希和费用,然后自行签名并广播交易。它只接受未签名、且唯一必需签名者为 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 会重写消息(最新区块哈希、Compute Budget 费用指令)并签名,但不会广播。返回的交易会取代你提交的交易,因此请务必基于 result.transaction 继续操作。Fordefi 必须是费用支付方,并且必须先于任何其他必需签名者签名:已带有签名的交易会被接受,但重写会使这些签名失效。重写内容不会生成差异对比,因此广播前请检查结果,并尽快广播,因为 Fordefi 不会返回新区块哈希的过期高度。
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 黑盒 vault 搭配使用。
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携带由消息派生出的密钥,创建请求即是以该密钥发出的。重新发送字节完全相同的交易是安全的;重新构建的交易(例如使用了新的区块哈希)则是一笔新的转账。 - 在
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?