TypeScript

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 packages
pnpm add @solana/keychain-core # Core interfaces (required for custom signers)
pnpm add @solana/keychain-memory # Local keypair signer
pnpm add @solana/keychain-vault # HashiCorp Vault
pnpm add @solana/keychain-aws-kms # AWS KMS
pnpm add @solana/keychain-gcp-kms # GCP KMS
pnpm add @solana/keychain-privy # Privy
pnpm add @solana/keychain-turnkey # Turnkey
pnpm add @solana/keychain-fireblocks # Fireblocks
pnpm add @solana/keychain-cdp # Coinbase Developer Platform
pnpm add @solana/keychain-crossmint # Crossmint
pnpm add @solana/keychain-dfns # Dfns
pnpm add @solana/keychain-openfort # Openfort
pnpm add @solana/keychain-para # Para
pnpm add @solana/keychain-utila # Utila
pnpm 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.1
const signer = await createKeychainSigner({
backend: "privy",
appId: "your-app-id",
appSecret: "your-app-secret",
walletId: "your-wallet-id"
});
// Sign an already-compiled transaction
const 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 builder
const transaction = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(signer, tx)
// ... set a lifetime and add instructions
);
// Sign with the standard signers API
const 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 TransactionPartialSigner
client.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:

ModeKonfigurasiTipeMetode
Black boxchain tidak diaturFordefiBlackBoxSignersignTransactions()
Native autochain diatur, pushMode dihilangkan atau "auto"FordefiNativeSignersignAndSendTransactions()
Native manualchain diatur, pushMode: "manual"FordefiNativeManualSignermodifyAndSignTransactions()

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 (TransactionPartialSigner Kit): mengembalikan signature untuk transaksi yang Anda miliki.
  • SolanaModifyingSigner (TransactionModifyingSigner Kit): menulis ulang transaksi, menandatanganinya, dan mengembalikan transaksi baru tanpa menyiarkannya.
  • SolanaSendingSigner (TransactionSendingSigner Kit): menandatangani dan menyiarkan melalui provider-nya.
  • SolanaMessageSigner (MessagePartialSigner Kit): 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.

BackendBentuk
memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (black box)SolanaTransactionSigner & SolanaMessageSigner
utilaSolanaTransactionSigner (tanpa penandatanganan pesan)
fordefi (native manual)SolanaModifyingSigner & SolanaMessageSigner
fordefi (native auto)SolanaSendingSigner & SolanaMessageSigner
crossmintSolanaSendingSigner (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?

© 2026 Yayasan Solana. Semua hak dilindungi.