Aktualizujesz z wersji 1.x? Zobacz przewodnik migracji do wersji 2.0.
Instalacja
Zainstaluj pakiet główny lub poszczególne moduły podpisujące według potrzeb:
# 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
Wymaga @solana/kit w wersji 8.1 lub nowszej. Transakcje v1
(createTransactionMessage({ version: 1 })) są obsługiwane, z wyjątkiem Fireblocks
z useProgramCall: true, które akceptuje wyłącznie transakcje legacy i v0.
Podstawowe użycie
Ujednolicona fabryka (Zalecana dla aplikacji chcących obsługiwać wiele modułów podpisujących)
Użyj createKeychainSigner z rozróżnioną konfiguracją, aby utworzyć dowolny
backend:
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);
Lub zainstaluj pojedynczy pakiet modułu podpisującego, aby zmniejszyć zależności:
import { createPrivySigner } from "@solana/keychain-privy";const signer = await createPrivySigner({appId: "your-app-id",appSecret: "your-app-secret",walletId: "your-wallet-id"});
Użycie z komunikatami transakcji
Każdy backend poza Crossmint i trybem natywnym Fordefi zwraca
SolanaTransactionSigner, który jest TransactionPartialSigner z Kit i działa
z poniższymi helperami częściowych signerów z @solana/signers. Backendy z zarządzanym rozgłaszaniem
opisano w sekcji Możliwości signerów.
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;}
Podpisywanie i wysyłanie z dowolnym signerem
signAndSendTransaction z @solana/keychain-core umieszcza transakcję
w łańcuchu niezależnie od typu signera. Signery wysyłające rozgłaszają ją przez swojego
dostawcę; każdy inny typ używa przekazanej przez Ciebie funkcji sendTransaction (core nie ma
zależności od RPC):
import { signAndSendTransaction } from "@solana/keychain-core";const signature = await signAndSendTransaction(signer, transaction, {sendTransaction: (tx) =>sendAndConfirmTransaction(tx, { commitment: "confirmed" })});
Używanie z klientem Kit (wtyczka keychainSigner)
@solana/keychain-kit-plugin instaluje sygnatariusza pęku kluczy bezpośrednio
na kliencie Kit, podłączając go jako payer
i identity, dzięki czemu pomijasz ręczną konfigurację pipe pokazaną powyżej:
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 akceptuje tę samą konfigurację oznaczoną backendem co createKeychainSigner,
z wyjątkiem Crossmint i trybu natywnego Fordefi (ustawione chain). Te backendy przepisują
transakcję po stronie serwera, więc nie mogą pełnić roli klienckiego payer ani
identity; plugin odrzuca je podczas kompilacji i w czasie działania. Dołączany jest tylko
backend, do którego następuje przekierowanie (pakiety backendów ładowane są przez dynamiczny
import()).
Użyj keychainPayer lub keychainIdentity, aby przypisać tylko jedną rolę i
łączyć backendy na jednym kliencie:
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 }));
Masz już SolanaTransactionSigner? Jest on prawidłowym TransactionPartialSigner
z Kit, więc możesz zamiast tego zainstalować go za pomocą
@solana/kit-plugin-signer
(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));
Anulowanie i niepotwierdzone rozgłoszenia
signTransactions, modifyAndSignTransactions, signAndSendTransactions i
signMessages przyjmują { abortSignal } jako drugi argument. Przerwanie
kończy oczekiwanie, ale nie cofa pracy, którą dostawca już zaakceptował.
W trybie natywnym automatycznym Crossmint i Fordefi niepowodzenie nie oznacza, że nic
nie trafiło do sieci. Gdy dostawca mógł zaakceptować transakcję, kodem błędu jest
SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED").
context.providerTransactionId jest ustawiane, gdy utworzenie zostało zaakceptowane, a
context.idempotencyKey zawiera klucz wyprowadzony z wiadomości. Ponowne wysłanie
identycznej co do bajta transakcji jest bezpieczne; odbudowana transakcja (na przykład z nowym
blockhashem) to nowy transfer. providerMayHaveAccepted(error) i
providerStatus(error) z @solana/keychain-core pomagają sklasyfikować błąd.
Przed ponowną próbą uzgodnij stan z dostawcą.
Konfiguracja backendu
Memory
Podaj dokładnie jedno z: keyPair, privateKey, privateKeyString lub
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 obsługuje wyłącznie wysyłanie. Jego API zawsze wykonuje zatwierdzoną transakcję
po stronie serwera i sponsoruje opłaty gas, więc signer udostępnia tylko
signAndSendTransactions(): nie ma signTransactions() ani signMessages()
i nie można go zainstalować za pomocą @solana/keychain-kit-plugin. Zwrócona
sygnatura identyfikuje transakcję, którą Crossmint umieścił w łańcuchu.
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 podpisuje wyłącznie transakcje; nie ma 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 ma trzy tryby podpisywania, ustalane podczas tworzenia przez chain i
pushMode. Każdy tryb udostępnia dokładnie jedną metodę transakcji, a wszystkie trzy podpisują
wiadomości:
| Tryb | Konfiguracja | Typ | Metoda |
|---|---|---|---|
| Czarna skrzynka | chain nieustawione | FordefiBlackBoxSigner | signTransactions() |
| Natywny automatyczny | chain ustawione, pushMode pominięte lub "auto" | FordefiNativeSigner | signAndSendTransactions() |
| Natywny ręczny | chain ustawione, pushMode: "manual" | FordefiNativeManualSigner | modifyAndSignTransactions() |
Żądania API są uwierzytelniane sygnaturą żądania ECDSA P-256: podaj klucz
podpisujący jako privateKeyPem albo przechowuj go w KMS/HSM, przekazując
requestSigner.
Tryb natywny automatyczny (zalecany do użycia on-chain): ustaw chain, a Fordefi
samo podpisze i rozgłosi transakcję. Ponieważ Fordefi może zastąpić
blockhash lub opłaty przed podpisaniem, signer jest TransactionSendingSigner
z Kit: używaj go z
signAndSendTransactionMessageWithSigners, a nie z przepływem częściowych signerów pokazanym
powyżej. Tryb natywny automatyczny akceptuje wyłącznie niepodpisaną transakcję, w której jedynym wymaganym
signerem jest sejf, i nie można go używać z @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);
Tryb natywny ręczny: ustaw chain i pushMode: "manual". Fordefi przepisuje
wiadomość (recent blockhash, instrukcje opłat Compute Budget) i podpisuje ją,
ale nie rozgłasza. modifyAndSignTransactions() zwraca transakcję podpisaną przez
Fordefi, która zastępuje tę, którą przesłano: zawsze kontynuuj od
zwróconej transakcji. Fordefi musi być płatnikiem opłaty i musi podpisać przed każdym
innym wymaganym signerem. Przepisanie nie jest porównywane (diff), więc sprawdź wynik przed
rozgłoszeniem.
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]);
Tryb czarnej skrzynki: pomiń chain. Fordefi podpisuje surowe bajty i nie rozgłasza transakcji; samodzielnie składasz i przesyłasz transakcję. Używaj tego trybu z sejfem czarnej skrzynki 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")});
Możliwości signerów
@solana/keychain-core definiuje jeden interfejs dla każdego typu signera Kit. Każdy dodaje
isAvailable(): Promise<boolean> do odpowiadającego interfejsu z @solana/signers:
SolanaTransactionSigner(TransactionPartialSignerz Kit): zwraca sygnatury dla transakcji, którą posiadasz.SolanaModifyingSigner(TransactionModifyingSignerz Kit): przepisuje transakcję, podpisuje ją i zwraca nową transakcję bez rozgłaszania.SolanaSendingSigner(TransactionSendingSignerz Kit): podpisuje i rozgłasza przez swojego dostawcę.SolanaMessageSigner(MessagePartialSignerz Kit): podpisuje wiadomości off-chain. Jest to oddzielne od typów transakcyjnych.
SolanaSigner to unia trzech typów transakcyjnych. Typ zwracany przez każdą fabrykę
wskazuje dokładny typ jej backendu. Gdy konfiguracja jest znana dopiero w czasie
działania, zawęź typ za pomocą isSolanaTransactionSigner, isSolanaModifyingSigner,
isSolanaSendingSigner lub isSolanaMessageSigner. signerCapabilities(signer)
raportuje wszystkie cztery naraz.
| Backend | Typ |
|---|---|
| memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (czarna skrzynka) | SolanaTransactionSigner & SolanaMessageSigner |
| utila | SolanaTransactionSigner (bez podpisywania wiadomości) |
| fordefi (natywny ręczny) | SolanaModifyingSigner & SolanaMessageSigner |
| fordefi (natywny automatyczny) | SolanaSendingSigner & SolanaMessageSigner |
| crossmint | SolanaSendingSigner (bez podpisywania wiadomości) |
import {isSolanaSendingSigner,isSolanaTransactionSigner} from "@solana/keychain-core";if (isSolanaSendingSigner(signer)) {await signer.signAndSendTransactions([transaction]);} else if (isSolanaTransactionSigner(signer)) {await signer.signTransactions([transaction]);}
Tworzenie niestandardowych signerów
Zaimplementuj interfejsy możliwości obsługiwane przez Twój backend:
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);}}
Implementuj tylko to, co obsługuje Twój backend. Kit klasyfikuje signery według tego, jakie metody istnieją, więc nigdy nie dodawaj metody, która tylko zgłasza wyjątek.
Zobacz przewodnik Dodawanie signerów, aby zintegrować dodatkowe usługi zarządzania kluczami.
Is this page helpful?