TypeScript

Оновлюєтеся з 1.x? Перегляньте посібник із міграції на 2.0.

Встановлення

Встановіть загальний пакет або окремі підписанти за потреби:

# 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

Потрібен @solana/kit 8.1 або новіший. Транзакції v1 (createTransactionMessage({ version: 1 })) підтримуються, окрім Fireblocks із useProgramCall: true, який приймає лише legacy та v0.

Базове використання

Уніфікована фабрика (рекомендується для додатків, які хочуть підтримувати кілька підписантів)

Використовуйте createKeychainSigner з дискримінованою конфігурацією для створення будь-якого бекенду:

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

Або встановіть окремий пакет підписанта для меншого обсягу залежностей:

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

Використання з повідомленнями транзакцій

Кожен бекенд, окрім Crossmint і нативного режиму Fordefi, повертає SolanaTransactionSigner, який є Kit TransactionPartialSigner і працює із наведеними нижче хелперами часткових підписувачів @solana/signers. Бекенди із керованою трансляцією описано в розділі Можливості підписувачів.

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

Підписання та надсилання з будь-яким підписувачем

signAndSendTransaction із @solana/keychain-core доставляє транзакцію в мережу незалежно від типу підписувача. Підписувачі з надсиланням транслюють транзакцію через свого провайдера; усі інші типи використовують передану вами функцію sendTransaction (core не має залежності від RPC):

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

Використання з Kit Client (плагін keychainSigner)

@solana/keychain-kit-plugin встановлює підписувач ключового ланцюжка безпосередньо на Kit client, підключаючи його як payer та identity, що дозволяє пропустити ручне налаштування pipe, показане вище:

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

Плагін приймає ту саму конфігурацію з тегом бекенду, що й createKeychainSigner, окрім Crossmint і нативного режиму Fordefi (заданий chain). Ці бекенди переписують транзакцію на стороні сервера, тому не можуть виступати клієнтським payer чи identity; плагін відхиляє їх і під час компіляції, і під час виконання. У збірку входить лише той бекенд, до якого він звертається (пакети бекендів завантажуються через динамічний import()).

Використовуйте keychainPayer або keychainIdentity, щоб призначити лише одну роль і поєднувати бекенди на одному клієнті:

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

Уже маєте SolanaTransactionSigner? Це валідний Kit TransactionPartialSigner, тож замість цього його можна встановити через @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));

Скасування та непідтверджені трансляції

signTransactions, modifyAndSignTransactions, signAndSendTransactions і signMessages приймають { abortSignal } другим аргументом. Переривання припиняє очікування; воно не скасовує роботу, яку провайдер уже прийняв.

У нативному авторежимі Crossmint і Fordefi збій не означає, що транзакція не потрапила в мережу. Якщо провайдер міг прийняти транзакцію, код помилки — SignerErrorCode.BROADCAST_UNCONFIRMED ("SIGNER_BROADCAST_UNCONFIRMED"). context.providerTransactionId встановлюється, коли створення було прийнято, а context.idempotencyKey містить ключ, отриманий із повідомлення. Повторне надсилання побайтово ідентичної транзакції безпечне; перебудована транзакція (наприклад, з новим blockhash) є новим переказом. Класифікувати помилку допомагають providerMayHaveAccepted(error) та providerStatus(error) із @solana/keychain-core. Перед повторною спробою звірте стан із провайдером.

Конфігурація бекенду

Пам'ять

Вкажіть рівно одне з: keyPair, privateKey, privateKeyString або 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 працює лише на надсилання. Його API завжди виконує схвалену транзакцію на стороні сервера та спонсорує газ, тому підписувач надає лише signAndSendTransactions(): у нього немає signTransactions() чи signMessages(), і його не можна встановити через @solana/keychain-kit-plugin. Повернутий підпис ідентифікує транзакцію, яку 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 лише підписує транзакції; 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 має три режими підписання, які визначаються під час створення через chain і pushMode. Кожен режим надає рівно один метод для транзакцій, а всі три підписують повідомлення:

РежимКонфігураціяТипМетод
Чорна скринькаchain не заданоFordefiBlackBoxSignersignTransactions()
Нативний автоchain задано, pushMode пропущено або "auto"FordefiNativeSignersignAndSendTransactions()
Нативний ручнийchain задано, pushMode: "manual"FordefiNativeManualSignermodifyAndSignTransactions()

API-запити автентифікуються підписом запиту ECDSA P-256: передайте ключ підписання як privateKeyPem або зберігайте його в KMS/HSM, передавши requestSigner.

Нативний авторежим (рекомендований для використання в мережі): задайте chain, і Fordefi сам підписує та транслює транзакцію. Оскільки Fordefi може замінити blockhash або комісії перед підписанням, підписувач є Kit TransactionSendingSigner: використовуйте його з signAndSendTransactionMessageWithSigners, а не з потоком часткового підписувача, показаним вище. Нативний авторежим приймає лише непідписану транзакцію, єдиним необхідним підписувачем якої є vault, і його не можна використовувати з @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);

Нативний ручний режим: задайте chain і pushMode: "manual". Fordefi переписує повідомлення (recent blockhash, інструкції комісій Compute Budget) та підписує його, але не транслює. modifyAndSignTransactions() повертає транзакцію, підписану Fordefi, яка замінює ту, що ви надіслали: завжди продовжуйте з повернутою транзакцією. 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_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
]);

Режим чорної скриньки: пропустіть chain. Fordefi підписує необроблені байти та не транслює; ви самостійно збираєте та надсилаєте транзакцію. Використовуйте це з vault-сховищем типу "чорна скринька" 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")
});

Можливості підписувачів

@solana/keychain-core визначає один інтерфейс для кожного типу підписувача Kit. Кожен додає isAvailable(): Promise<boolean> до відповідного інтерфейсу @solana/signers:

  • SolanaTransactionSigner (Kit TransactionPartialSigner): повертає підписи для транзакції, якою ви володієте.
  • SolanaModifyingSigner (Kit TransactionModifyingSigner): переписує транзакцію, підписує її та повертає нову транзакцію без трансляції.
  • SolanaSendingSigner (Kit TransactionSendingSigner): підписує та транслює через свого провайдера.
  • SolanaMessageSigner (Kit MessagePartialSigner): підписує повідомлення поза мережею. Це окремо від типів для транзакцій.

SolanaSigner — це об'єднання трьох типів для транзакцій. Тип, що повертає кожна фабрика, вказує точну форму свого бекенду. Коли конфігурація відома лише під час виконання, звужуйте тип за допомогою isSolanaTransactionSigner, isSolanaModifyingSigner, isSolanaSendingSigner або isSolanaMessageSigner. signerCapabilities(signer) повідомляє про всі чотири одразу.

БекендТип
memory, vault, aws-kms, gcp-kms, privy, turnkey, fireblocks, cdp, dfns, para, openfort, fordefi (чорна скринька)SolanaTransactionSigner & SolanaMessageSigner
utilaSolanaTransactionSigner (без підписання повідомлень)
fordefi (нативний ручний)SolanaModifyingSigner & SolanaMessageSigner
fordefi (нативний авто)SolanaSendingSigner & SolanaMessageSigner
crossmintSolanaSendingSigner (без підписання повідомлень)
import {
isSolanaSendingSigner,
isSolanaTransactionSigner
} from "@solana/keychain-core";
if (isSolanaSendingSigner(signer)) {
await signer.signAndSendTransactions([transaction]);
} else if (isSolanaTransactionSigner(signer)) {
await signer.signTransactions([transaction]);
}

Створення власних підписантів

Реалізуйте інтерфейси можливостей, які підтримує ваш бекенд:

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

Реалізуйте лише те, що підтримує ваш бекенд. Kit класифікує підписувачів за наявними методами, тож ніколи не додавайте метод, який лише викидає помилку.

Перегляньте посібник із додавання підписантів, щоб інтегрувати додаткові служби керування ключами.

Is this page helpful?