バージョン2.0は、Pythonパッケージ初の安定版リリースです。2.0移行ガイドでは、RustおよびTypeScriptと共通の署名の形式について説明しています。
インストール
ベースパッケージをインストールし、プロバイダー依存関係が必要なバックエンドごとにextraを1つずつ追加でインストールします:
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オブジェクトです。レガシー、v0、v1のメッセージはすべて受け付けられますが、use_program_call=Trueを指定したFireblocksのみ、レガシーとv0だけを受け付けます。
Memory、Vault、Paraはパッケージのルートからインポートできます。それ以外のバックエンドは、それぞれのサブモジュール(例: solana_keychain.aws_kms)からインポートします。extraを入れずにインポートすると、インストールすべきextraを示すImportErrorが発生します。LedgerはRust専用です。
基本的な使い方
各バックエンドには、設定用のdataclassと、すぐに使えるsignerを返す非同期の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は、encoded_transaction、signature、is_complete、transactionを持つSignedTransactionを返します。transactionが正式な署名済みトランザクションです。特にmodifying signerでは、結果が渡したオブジェクトとは別のオブジェクトになるため、以降の処理はこの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で絞り込んでください(Signerの機能を参照)。
リモートHTTPクライアント
リモートHTTPバックエンドは、カスタムTLSやプロキシを使用するための任意のhttp_client(httpx.AsyncClient)を受け付けます。未設定の場合、各リクエストはタイムアウト60秒の使い切りクライアントを使用し、ベースURLにはHTTPSが強制され、リダイレクトは拒否されます。
Signerの機能
すべてのバックエンドはSolanaSigner ABC(pubkey、sign_message、is_available)を継承し、さらに機能クラスをちょうど1つ継承します:
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 black box(FordefiBlackBoxSigner) | TransactionSigner | 対応 |
fordefi native manual(FordefiNativeManualSigner) | ModifyingSigner | 対応 |
fordefi native auto(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のsignerクラスを直接生成する場合は、使用前に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",))
authorization keyで保護されたウォレットでは、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を指定すると、RAWの代わりにPROGRAM_CALLオペレーションで署名します。署名のみの形で送信されるため、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は承認されたトランザクションを常にサーバーサイドで実行し、ガス代をスポンサーするため、CrossmintSignerはSendingSignerであり、sign_messageはSIGNING_FAILEDを発生させます。返される署名は、Crossmintがチェーンに反映したトランザクションを識別するもので、送信したものと異なる場合があります。signer_secret(xmsk1_...形式のdelegated-signerシークレット)を設定すると、トランザクションが自動的に承認されます。これは、何を承認するかの判断を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には3つの署名モードがあり、chainとpush_modeによって構築時に決まります。create_fordefi_signerが適切なクラスを選択し、各クラスは他のモード向けの設定を拒否します。3つのモードはいずれもメッセージへの署名に対応しています:
| モード | 設定 | クラス | メソッド |
|---|---|---|---|
| Black box | chainを未設定 | FordefiBlackBoxSigner | sign_transaction |
| Native auto | chainを設定し、push_modeは未設定または"auto" | FordefiNativeAutoSigner | sign_and_send_transaction |
| Native manual | chainを設定し、push_mode="manual" | FordefiNativeManualSigner | modify_and_sign_transaction |
APIリクエストは、ECDSA P-256のリクエスト署名で認証されます。キーをprivate_key_pemとして渡すか、FordefiRequestSignerのサブクラスをrequest_signerとして渡して、KMS/HSM内にキーを保持してください。
Native autoモード: chain("solana_devnet"または"solana_mainnet")を設定すると、Fordefiがブロックハッシュと手数料を置き換える場合があり、その後、トランザクションへの署名とブロードキャストを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)
Native manualモード: chainを設定し、push_mode="manual"を指定します。Fordefiはメッセージ(最新のブロックハッシュ、Compute Budgetの手数料instructions)を書き換えて署名しますが、ブロードキャストは行いません。返されるトランザクションは、送信したものと置き換わるため、必ず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
確認したトランザクションは署名と送信の手順でブロードキャストし、送信に失敗した場合は、再試行する前に状態を照合してください。
Black boxモード: chainを省略します。Fordefiは渡されたメッセージのバイト列そのものに署名し、ブロードキャストは行いません。Fordefi black box 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は、signerの種類にかかわらず、トランザクションをチェーンに反映させます。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)
署名のみのsignerで送信関数がない場合はCONFIG_ERRORが発生し、署名後も署名が不足しているトランザクションではSIGNING_FAILEDが発生します。
エラー処理
すべての失敗は、SignerErrorCodeの安定したcodeを持つSignerErrorとして発生します: 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 native autoでは、作成リクエストが受理された時点で
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を回復するには、solana_keychain.coreのPendingTransactionId()を、CrossmintまたはFordefi native autoの設定のpending_transaction_idとして渡し、キャンセル後に.get()で読み取ってください。再試行する前に、プロバイダーと状態を照合してください。
Keychainが検証するのは署名の経路であり、トランザクションの内容ではありません。プロバイダーが返した署名は、アタッチされる前に、ローカルで計算したメッセージに対してed25519で検証されます。Pythonではキーのバッファをゼロ化できないため、ローカルキーのバックエンド(Memory、およびリモートバックエンドの設定内に保持されるAPIキーとPEM)では、プロセスのメモリ全体を機密として扱ってください。各署名の形式が何を保証するかについては、セキュリティモデルを参照してください。
カスタムsignerの追加
バックエンドに対応する機能クラスを1つ選び(TransactionSigner、
ModifyingSigner、または SendingSigner)、solana_keychain.core からサブクラス化してください。
追加のキー管理サービスを統合する方法については、
署名者の追加ガイドをご覧ください。
リソース
Is this page helpful?