TypeScript

Nâng cấp từ 1.x? Xem hướng dẫn di chuyển sang 2.0.

Cài đặt

Cài đặt gói tổng hợp (umbrella package) hoặc từng signer riêng lẻ tùy nhu cầu:

# 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

Yêu cầu @solana/kit 8.1 trở lên. Giao dịch v1 (createTransactionMessage({ version: 1 })) được hỗ trợ, ngoại trừ Fireblocks với useProgramCall: true, vốn chỉ chấp nhận legacy và v0.

Cách sử dụng cơ bản

Factory hợp nhất (Khuyến nghị cho các ứng dụng muốn hỗ trợ nhiều signer)

Dùng createKeychainSigner với cấu hình phân biệt (discriminated config) để tạo bất kỳ backend nào:

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
);

Hoặc cài đặt gói signer riêng lẻ để giảm dung lượng phụ thuộc:

import { createPrivySigner } from "@solana/keychain-privy";
const signer = await createPrivySigner({
appId: "your-app-id",
appSecret: "your-app-secret",
walletId: "your-wallet-id"
});

Sử dụng với Transaction Message

Mọi backend, trừ Crossmint và chế độ native của Fordefi, đều trả về một SolanaTransactionSigner, vốn là một TransactionPartialSigner của Kit và hoạt động cùng các helper partial-signer của @solana/signers bên dưới. Các backend quản lý việc broadcast được trình bày trong Khả năng của 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;
}

Ký và gửi với bất kỳ Signer nào

signAndSendTransaction từ @solana/keychain-core đưa giao dịch lên chain bất kể signer có dạng nào. Các signer dạng gửi (sending) sẽ broadcast thông qua provider của chúng; mọi dạng khác dùng sendTransaction mà bạn truyền vào (core không phụ thuộc vào RPC):

import { signAndSendTransaction } from "@solana/keychain-core";
const signature = await signAndSendTransaction(signer, transaction, {
sendTransaction: (tx) =>
sendAndConfirmTransaction(tx, { commitment: "confirmed" })
});

Sử dụng với Kit Client (plugin keychainSigner)

@solana/keychain-kit-plugin cài đặt trực tiếp một keychain signer lên Kit client, thiết lập nó làm payer và identity để bạn bỏ qua bước thiết lập pipe thủ công như đã trình bày ở trên:

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 chấp nhận cùng cấu hình gắn thẻ backend như createKeychainSigner, trừ Crossmint và chế độ native của Fordefi (đã đặt chain). Các backend đó viết lại giao dịch ở phía máy chủ, nên chúng không thể đóng vai trò payer hay identity của client; plugin sẽ từ chối chúng ở thời điểm biên dịch và thời điểm chạy. Chỉ backend mà nó điều phối tới mới được đóng gói (các gói backend được tải qua import() động).

Dùng keychainPayer hoặc keychainIdentity để chỉ thiết lập một vai trò, và kết hợp nhiều backend trên cùng một 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 }));

Đã có sẵn một SolanaTransactionSigner? Nó là một TransactionPartialSigner hợp lệ của Kit, nên bạn có thể cài đặt nó bằng @solana/kit-plugin-signer thay thế (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));

Hủy bỏ và các Broadcast chưa được xác nhận

signTransactions, modifyAndSignTransactions, signAndSendTransactions và signMessages đều nhận { abortSignal } làm đối số thứ hai. Việc hủy bỏ chỉ dừng việc chờ đợi; nó không hoàn tác công việc mà provider đã chấp nhận.

Với chế độ native auto của Crossmint và Fordefi, một lỗi không có nghĩa là giao dịch chưa được ghi nhận. Khi provider có thể đã chấp nhận giao dịch, mã lỗi là SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED"). context.providerTransactionId được đặt khi yêu cầu tạo đã được chấp nhận, và context.idempotencyKey chứa khóa được suy ra từ message. Gửi lại giao dịch giống hệt từng byte là an toàn; một giao dịch được dựng lại (ví dụ với blockhash mới) là một lần chuyển mới. providerMayHaveAccepted(error) và providerStatus(error) từ @solana/keychain-core giúp phân loại lỗi. Hãy đối chiếu với provider trước khi thử lại.

Cấu hình Backend

Memory

Cung cấp chính xác một trong các tùy chọn keyPair, privateKey, privateKeyString hoặc 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 chỉ hỗ trợ gửi. API của Crossmint luôn thực thi giao dịch đã được duyệt ở phía máy chủ và tài trợ phí gas, nên signer chỉ cung cấp signAndSendTransactions(): không có signTransactions() hay signMessages(), và không thể cài đặt cùng @solana/keychain-kit-plugin. Chữ ký trả về xác định giao dịch mà Crossmint đã đưa lên chain.

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 chỉ ký giao dịch; không có 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 có ba chế độ ký, được cố định khi khởi tạo bởi chain và pushMode. Mỗi chế độ cung cấp đúng một phương thức giao dịch, và cả ba đều ký message:

Chế độCấu hìnhKiểuPhương thức
Black boxchain không được đặtFordefiBlackBoxSignersignTransactions()
Native autochain được đặt, pushMode bị bỏ qua hoặc là "auto"FordefiNativeSignersignAndSendTransactions()
Native manualchain được đặt, pushMode: "manual"FordefiNativeManualSignermodifyAndSignTransactions()

Các yêu cầu API được xác thực bằng chữ ký yêu cầu ECDSA P-256: cung cấp khóa ký dưới dạng privateKeyPem, hoặc giữ khóa trong KMS/HSM bằng cách truyền requestSigner.

Chế độ native auto (khuyến nghị cho việc sử dụng on-chain): đặt chain, và Fordefi sẽ tự ký và broadcast giao dịch. Vì Fordefi có thể thay thế blockhash hoặc phí trước khi ký, signer là một TransactionSendingSigner của Kit: hãy dùng nó với signAndSendTransactionMessageWithSigners, không dùng quy trình partial-signer như đã trình bày ở trên. Chế độ native auto chỉ chấp nhận giao dịch chưa ký mà signer bắt buộc duy nhất là vault, và không thể dùng cùng @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);

Chế độ native manual: đặt chain và pushMode: "manual". Fordefi viết lại message (blockhash gần đây, các instruction phí Compute Budget) và ký nó, nhưng không broadcast. modifyAndSignTransactions() trả về giao dịch mà Fordefi đã ký, thay thế giao dịch bạn đã gửi: luôn tiếp tục từ giao dịch được trả về. Fordefi phải là người trả phí (fee payer) và phải ký trước mọi signer bắt buộc khác. Phần viết lại không được so sánh khác biệt (diff), vì vậy hãy kiểm tra kết quả trước khi broadcast.

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
]);

Chế độ black box: bỏ qua chain. Fordefi ký các byte thô và không broadcast; bạn tự ghép và gửi giao dịch. Hãy dùng chế độ này với vault black box của 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")
});

Khả năng của Signer

@solana/keychain-core định nghĩa một interface cho mỗi dạng signer của Kit. Mỗi interface bổ sung isAvailable(): Promise<boolean> vào interface tương ứng của @solana/signers:

  • SolanaTransactionSigner (TransactionPartialSigner của Kit): trả về chữ ký cho một giao dịch mà bạn sở hữu.
  • SolanaModifyingSigner (TransactionModifyingSigner của Kit): viết lại giao dịch, ký và trả về giao dịch mới mà không broadcast.
  • SolanaSendingSigner (TransactionSendingSigner của Kit): ký và broadcast thông qua provider của nó.
  • SolanaMessageSigner (MessagePartialSigner của Kit): ký các message off-chain. Dạng này tách biệt với các dạng giao dịch.

SolanaSigner là union của ba dạng giao dịch. Kiểu trả về của mỗi factory chỉ rõ chính xác dạng của backend tương ứng. Khi cấu hình chỉ được biết tại thời điểm chạy, hãy thu hẹp kiểu bằng isSolanaTransactionSigner, isSolanaModifyingSigner, isSolanaSendingSigner hoặc isSolanaMessageSigner. signerCapabilities(signer) báo cáo cả bốn cùng một lúc.

BackendDạng
memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (black box)SolanaTransactionSigner & SolanaMessageSigner
utilaSolanaTransactionSigner (không ký message)
fordefi (native manual)SolanaModifyingSigner & SolanaMessageSigner
fordefi (native auto)SolanaSendingSigner & SolanaMessageSigner
crossmintSolanaSendingSigner (không ký message)
import {
isSolanaSendingSigner,
isSolanaTransactionSigner
} from "@solana/keychain-core";
if (isSolanaSendingSigner(signer)) {
await signer.signAndSendTransactions([transaction]);
} else if (isSolanaTransactionSigner(signer)) {
await signer.signTransactions([transaction]);
}

Xây dựng Signer tùy chỉnh

Triển khai các interface khả năng mà backend của bạn hỗ trợ:

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);
}
}

Chỉ triển khai những gì backend của bạn hỗ trợ. Kit phân loại signer dựa trên những phương thức hiện có, vì vậy đừng bao giờ thêm một phương thức chỉ để ném lỗi.

Xem hướng dẫn Thêm Signer để tích hợp các dịch vụ quản lý khóa bổ sung.

Is this page helpful?