Upgrade dari 1.x? Lihat panduan migrasi 2.0.
Instalasi
Instal paket lengkap atau penanda tangan individual sesuai kebutuhan:
# Umbrella package (includes all signers)pnpm add @solana/keychain# Or install individual packagespnpm add @solana/keychain-core # Core interfaces (required for custom signers)pnpm add @solana/keychain-memory # Local keypair signerpnpm add @solana/keychain-vault # HashiCorp Vaultpnpm add @solana/keychain-aws-kms # AWS KMSpnpm add @solana/keychain-gcp-kms # GCP KMSpnpm add @solana/keychain-privy # Privypnpm add @solana/keychain-turnkey # Turnkeypnpm add @solana/keychain-fireblocks # Fireblockspnpm add @solana/keychain-cdp # Coinbase Developer Platformpnpm add @solana/keychain-crossmint # Crossmintpnpm add @solana/keychain-dfns # Dfnspnpm add @solana/keychain-openfort # Openfortpnpm add @solana/keychain-para # Parapnpm add @solana/keychain-utila # Utilapnpm add @solana/keychain-fordefi # Fordefi# Kit client plugins (keychainSigner / keychainPayer / keychainIdentity)pnpm add @solana/keychain-kit-plugin
Memerlukan @solana/kit 8.1 atau yang lebih baru. Transaksi v1
(createTransactionMessage({ version: 1 })) didukung, kecuali oleh Fireblocks
dengan useProgramCall: true, yang hanya menerima legacy dan v0.
Penggunaan Dasar
Factory Terpadu (Direkomendasikan untuk aplikasi yang ingin mendukung beberapa penanda tangan)
Gunakan createKeychainSigner dengan konfigurasi diskriminan untuk membuat
backend apa pun:
import { createKeychainSigner } from "@solana/keychain";import { signTransactionWithSigners } from "@solana/signers"; // requires @solana/signers >= 8.1const signer = await createKeychainSigner({backend: "privy",appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});// Sign an already-compiled transactionconst signedTx = await signTransactionWithSigners([signer],compiledTransaction);
Atau instal paket penanda tangan individual untuk jejak dependensi yang lebih kecil:
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});
Menggunakan dengan Pesan Transaksi
Setiap backend kecuali Crossmint dan mode native Fordefi mengembalikan
SolanaTransactionSigner, yaitu TransactionPartialSigner Kit yang berfungsi
dengan helper partial-signer @solana/signers di bawah ini. Backend
managed-broadcast dibahas di Kapabilitas Signer.
import type { SolanaTransactionSigner } from "@solana/keychain-core";import { signTransactionMessageWithSigners } from "@solana/signers";import {createTransactionMessage,setTransactionMessageFeePayerSigner,pipe} from "@solana/kit";async function signWithKeychain(signer: SolanaTransactionSigner) {// Check availability (useful for remote signers)if (!(await signer.isAvailable())) {throw new Error("Signer offline");}// Use with @solana/kit transaction builderconst transaction = pipe(createTransactionMessage({ version: 0 }),(tx) => setTransactionMessageFeePayerSigner(signer, tx)// ... set a lifetime and add instructions);// Sign with the standard signers APIconst signedTx = await signTransactionMessageWithSigners(transaction);return signedTx;}
Tanda Tangani dan Kirim dengan Signer Apa Pun
signAndSendTransaction dari @solana/keychain-core membawa transaksi ke
on-chain apa pun bentuk signer-nya. Signer pengirim menyiarkan melalui
provider-nya; bentuk lainnya menggunakan sendTransaction yang Anda berikan
(core tidak memiliki dependensi RPC):
import { signAndSendTransaction } from "@solana/keychain-core";const signature = await signAndSendTransaction(signer, transaction, {sendTransaction: (tx) =>sendAndConfirmTransaction(tx, { commitment: "confirmed" })});
Menggunakan dengan Kit Client (plugin keychainSigner)
@solana/keychain-kit-plugin menginstal keychain signer langsung pada
Kit client, menghubungkannya sebagai payer
dan identity sehingga Anda melewati pengaturan pipe manual seperti yang
ditunjukkan di atas:
import { createClient } from "@solana/kit";import { keychainSigner } from "@solana/keychain-kit-plugin";const client = await createClient().use(keychainSigner({backend: "privy",appId: process.env.PRIVY_APP_ID!,appSecret: process.env.PRIVY_APP_SECRET!,walletId: process.env.PRIVY_WALLET_ID!}));client.payer; // SolanaTransactionSigner, also a Kit TransactionPartialSignerclient.identity; // same signer instance
Plugin menerima konfigurasi berlabel backend yang sama dengan
createKeychainSigner, kecuali Crossmint dan mode native Fordefi (chain
diatur). Backend tersebut menulis ulang transaksi di sisi server, sehingga tidak
dapat bertindak sebagai payer atau identity klien; plugin menolaknya saat
kompilasi maupun saat runtime. Hanya backend yang dituju yang dibundel (paket
backend dimuat melalui import() dinamis).
Gunakan keychainPayer atau keychainIdentity untuk menetapkan satu peran
saja, dan gabungkan backend pada satu client:
import { createClient } from "@solana/kit";import { keychainIdentity, keychainPayer } from "@solana/keychain-kit-plugin";const client = await createClient().use(keychainPayer({backend: "memory",privateKeyPath: `${process.env.HOME}/.config/solana/id.json`})).use(keychainIdentity({ backend: "turnkey", ...turnkeyConfig }));
Sudah punya SolanaTransactionSigner? Itu adalah TransactionPartialSigner
Kit yang valid, sehingga Anda dapat memasangnya dengan
@solana/kit-plugin-signer
sebagai gantinya (pnpm add @solana/kit-plugin-signer):
import { createClient } from "@solana/kit";import { signer } from "@solana/kit-plugin-signer";import { createKeychainSigner } from "@solana/keychain";const mySigner = await createKeychainSigner({backend: "vault",...vaultConfig});const client = createClient().use(signer(mySigner));
Pembatalan dan Siaran yang Belum Terkonfirmasi
signTransactions, modifyAndSignTransactions, signAndSendTransactions, dan
signMessages semuanya menerima { abortSignal } sebagai argumen kedua.
Pembatalan menghentikan penantian; pekerjaan yang sudah diterima provider tidak
ikut dibatalkan.
Pada mode native auto Crossmint dan Fordefi, kegagalan tidak berarti tidak ada
transaksi yang masuk. Ketika provider mungkin telah menerima transaksi, kode
error-nya adalah SignerErrorCode.BROADCAST_UNCONFIRMED
("SIGNER_BROADCAST_UNCONFIRMED"). context.providerTransactionId diatur ketika
permintaan pembuatan diterima, dan context.idempotencyKey membawa kunci yang
diturunkan dari pesan. Mengirim ulang transaksi yang identik byte demi byte
aman dilakukan; transaksi yang dibangun ulang (misalnya dengan blockhash baru)
adalah transfer baru. providerMayHaveAccepted(error) dan
providerStatus(error) dari @solana/keychain-core membantu mengklasifikasikan
error. Lakukan rekonsiliasi dengan provider sebelum mencoba lagi.
Konfigurasi Backend
Memory
Berikan tepat satu dari keyPair, privateKey, privateKeyString, atau
privateKeyPath:
import { createMemorySigner } from "@solana/keychain-memory";const signer = await createMemorySigner({privateKeyPath: "/path/to/id.json"});
HashiCorp Vault
import { createVaultSigner } from "@solana/keychain-vault";const signer = createVaultSigner({vaultAddr: "https://vault.example.com:8200",vaultToken: "hvs.xxxxx",keyName: "my-solana-key",publicKey: "base58_public_key"});
AWS KMS
import { createAwsKmsSigner } from "@solana/keychain-aws-kms";const signer = createAwsKmsSigner({keyId: "alias/my-solana-key",publicKey: "base58_public_key",region: "us-east-1" // optional});
GCP KMS
import { createGcpKmsSigner } from "@solana/keychain-gcp-kms";const signer = createGcpKmsSigner({keyName:"projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1",publicKey: "base58_public_key"});
Privy
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "app_id",appSecret: "app_secret",walletId: "wallet_id"});
Turnkey
import { createTurnkeySigner } from "@solana/keychain-turnkey";const signer = createTurnkeySigner({apiPublicKey: "api_public_key",apiPrivateKey: "api_private_key",organizationId: "org_id",privateKeyId: "private_key_id",publicKey: "base58_public_key"});
Fireblocks
import { createFireblocksSigner } from "@solana/keychain-fireblocks";const signer = await createFireblocksSigner({apiKey: "api_key",privateKeyPem: "-----BEGIN RSA PRIVATE KEY-----\n...",vaultAccountId: "0",assetId: "SOL" // or "SOL_TEST" for devnet});
CDP (Coinbase Developer Platform)
import { createCdpSigner } from "@solana/keychain-cdp";const signer = await createCdpSigner({cdpApiKeyId: "api_key_id",cdpApiKeySecret: "api_key_secret",cdpWalletSecret: "wallet_secret",address: "base58_address",network: "solana" // or "solana-devnet"; required for address lookup tables});
Crossmint
import { createCrossmintSigner } from "@solana/keychain-crossmint";const signer = await createCrossmintSigner({apiKey: "api_key",walletLocator: "wallet_locator"});
Crossmint hanya untuk pengiriman. API-nya selalu menjalankan transaksi yang
disetujui di sisi server dan menyponsori gas, sehingga signer hanya menyediakan
signAndSendTransactions(): tidak ada signTransactions() atau
signMessages(), dan tidak dapat dipasang dengan @solana/keychain-kit-plugin.
Signature yang dikembalikan mengidentifikasi transaksi yang berhasil diproses
Crossmint.
const [signature] = await signer.signAndSendTransactions([transaction]);// or: await signAndSendTransactionMessageWithSigners(transactionMessage);
Dfns
import { createDfnsSigner } from "@solana/keychain-dfns";const signer = await createDfnsSigner({authToken: "auth_token",credId: "cred_id",privateKeyPem: "-----BEGIN EC PRIVATE KEY-----\n...",walletId: "wallet_id"});
Openfort
import { createOpenfortSigner } from "@solana/keychain-openfort";const signer = await createOpenfortSigner({secretKey: "sk_...",walletSecret: "wallet_secret",accountId: "account_id"});
Para
import { createParaSigner } from "@solana/keychain-para";const signer = await createParaSigner({apiKey: "api_key",walletId: "wallet_id"});
Utila
Utila hanya menandatangani transaksi; tidak ada signMessages().
import { createUtilaSigner } from "@solana/keychain-utila";const signer = await createUtilaSigner({serviceAccountEmail: "sa@example.com",serviceAccountPrivateKeyPem: "-----BEGIN PRIVATE KEY-----\n...",vaultId: "vault_id",walletId: "wallet_id",network: "networks/solana-devnet"});
Fordefi
Fordefi memiliki tiga mode penandatanganan, yang ditetapkan saat konstruksi oleh
chain dan pushMode. Setiap mode mengekspos tepat satu metode transaksi, dan
ketiganya dapat menandatangani pesan:
| Mode | Konfigurasi | Tipe | Metode |
|---|---|---|---|
| Black box | chain tidak diatur | FordefiBlackBoxSigner | signTransactions() |
| Native auto | chain diatur, pushMode dihilangkan atau "auto" | FordefiNativeSigner | signAndSendTransactions() |
| Native manual | chain diatur, pushMode: "manual" | FordefiNativeManualSigner | modifyAndSignTransactions() |
Permintaan API diautentikasi dengan tanda tangan permintaan ECDSA P-256:
berikan kunci penandatanganan sebagai privateKeyPem, atau simpan di KMS/HSM
dengan meneruskan requestSigner.
Mode native auto (direkomendasikan untuk penggunaan on-chain): atur chain,
dan Fordefi menandatangani serta menyiarkan transaksi itu sendiri. Karena
Fordefi dapat mengganti blockhash atau biaya sebelum menandatangani, signer-nya
adalah TransactionSendingSigner Kit: gunakan dengan
signAndSendTransactionMessageWithSigners, bukan alur partial-signer yang
ditunjukkan di atas. Mode native auto hanya menerima transaksi tanpa tanda
tangan yang satu-satunya penandatangan wajibnya adalah vault, dan tidak dapat
digunakan dengan @solana/keychain-kit-plugin.
import { createFordefiSigner } from "@solana/keychain-fordefi";import { signAndSendTransactionMessageWithSigners } from "@solana/signers";import { readFileSync } from "node:fs";const signer = await createFordefiSigner({accessToken: process.env.FORDEFI_ACCESS_TOKEN!,vaultId: process.env.FORDEFI_VAULT_ID!,publicKey: process.env.FORDEFI_PUBLIC_KEY!, // Solana vault address (base58)privateKeyPem: readFileSync("./secret/private.pem", "utf8"),chain: "solana_devnet", // or "solana_mainnet"fee: { type: "custom", priority_fee: "1000" } // optional});const signature =await signAndSendTransactionMessageWithSigners(transactionMessage);
Mode native manual: atur chain dan pushMode: "manual". Fordefi menulis
ulang pesan (blockhash terbaru, instruksi biaya Compute Budget) dan
menandatanganinya, tetapi tidak menyiarkannya. modifyAndSignTransactions()
mengembalikan transaksi yang ditandatangani Fordefi, yang menggantikan transaksi
yang Anda kirimkan: selalu lanjutkan dari transaksi yang dikembalikan. Fordefi
harus menjadi pembayar biaya dan harus menandatangani sebelum penandatangan wajib
lainnya. Hasil penulisan ulang tidak dibandingkan (diff), jadi periksa hasilnya
sebelum menyiarkan.
import { createFordefiSigner } from "@solana/keychain-fordefi";import { readFileSync } from "node:fs";const signer = await createFordefiSigner({accessToken: process.env.FORDEFI_ACCESS_TOKEN!,vaultId: process.env.FORDEFI_VAULT_ID!,publicKey: process.env.FORDEFI_PUBLIC_KEY!,privateKeyPem: readFileSync("./secret/private.pem", "utf8"),chain: "solana_devnet",pushMode: "manual"});const [signedTransaction] = await signer.modifyAndSignTransactions([transaction]);
Mode black box: hilangkan chain. Fordefi menandatangani byte mentah dan tidak menyiarkan; Anda merakit dan mengirimkan transaksi sendiri. Gunakan ini dengan vault black box Fordefi.
import { createFordefiSigner } from "@solana/keychain-fordefi";import { readFileSync } from "node:fs";const signer = await createFordefiSigner({accessToken: process.env.FORDEFI_ACCESS_TOKEN!,vaultId: process.env.FORDEFI_BB_VAULT_ID!,publicKey: process.env.FORDEFI_BB_PUBLIC_KEY!,privateKeyPem: readFileSync("./secret/private.pem", "utf8")});
Kapabilitas Signer
@solana/keychain-core mendefinisikan satu interface untuk setiap bentuk signer
Kit. Masing-masing menambahkan isAvailable(): Promise<boolean> ke interface
@solana/signers yang sesuai:
SolanaTransactionSigner(TransactionPartialSignerKit): mengembalikan signature untuk transaksi yang Anda miliki.SolanaModifyingSigner(TransactionModifyingSignerKit): menulis ulang transaksi, menandatanganinya, dan mengembalikan transaksi baru tanpa menyiarkannya.SolanaSendingSigner(TransactionSendingSignerKit): menandatangani dan menyiarkan melalui provider-nya.SolanaMessageSigner(MessagePartialSignerKit): menandatangani pesan off-chain. Ini terpisah dari bentuk-bentuk transaksi.
SolanaSigner adalah gabungan (union) dari ketiga bentuk transaksi. Tipe
kembalian setiap factory menyebutkan bentuk persis dari backend-nya. Ketika
konfigurasi baru diketahui saat runtime, persempit dengan
isSolanaTransactionSigner, isSolanaModifyingSigner, isSolanaSendingSigner
atau isSolanaMessageSigner. signerCapabilities(signer) melaporkan keempatnya
sekaligus.
| Backend | Bentuk |
|---|---|
| memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (black box) | SolanaTransactionSigner & SolanaMessageSigner |
| utila | SolanaTransactionSigner (tanpa penandatanganan pesan) |
| fordefi (native manual) | SolanaModifyingSigner & SolanaMessageSigner |
| fordefi (native auto) | SolanaSendingSigner & SolanaMessageSigner |
| crossmint | SolanaSendingSigner (tanpa penandatanganan pesan) |
import {isSolanaSendingSigner,isSolanaTransactionSigner} from "@solana/keychain-core";if (isSolanaSendingSigner(signer)) {await signer.signAndSendTransactions([transaction]);} else if (isSolanaTransactionSigner(signer)) {await signer.signTransactions([transaction]);}
Membangun Signer Kustom
Implementasikan interface kapabilitas yang didukung backend Anda:
import type {SolanaMessageSigner,SolanaTransactionSigner} from "@solana/keychain-core";import type { Address } from "@solana/addresses";import type {MessagePartialSignerConfig,SignableMessage,TransactionPartialSignerConfig} from "@solana/signers";import type { Transaction } from "@solana/transactions";class MyCustomSigner implements SolanaTransactionSigner, SolanaMessageSigner {constructor(readonly address: Address) {}async isAvailable(): Promise<boolean> {return await myBackend.healthCheck();}async signMessages(messages: readonly SignableMessage[],config?: MessagePartialSignerConfig) {return await myBackend.signMessages(messages, config?.abortSignal);}async signTransactions(transactions: readonly Transaction[],config?: TransactionPartialSignerConfig) {return await myBackend.signTransactions(transactions, config?.abortSignal);}}
Implementasikan hanya yang didukung backend Anda. Kit mengklasifikasikan signer berdasarkan metode yang ada, jadi jangan pernah menambahkan metode yang hanya melempar error.
Lihat panduan Menambahkan Signer untuk mengintegrasikan layanan manajemen kunci tambahan.
Is this page helpful?