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، وهو TransactionPartialSigner في Kit ويعمل مع دوال المساعدة الخاصة بالموقّعين الجزئيين في @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 التي تمررها أنت (لا تعتمد النواة على أي RPC):

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

الاستخدام مع عميل Kit (إضافة keychainSigner)

يقوم @solana/keychain-kit-plugin بتثبيت موقّع سلسلة المفاتيح مباشرةً على عميل Kit، ويربطه بوصفه 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؟ إنه TransactionPartialSigner صالح في Kit، لذا يمكنك تثبيته باستخدام @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)

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 للإرسال فقط. تنفّذ واجهته البرمجية دائمًا المعاملة المعتمدة على جهة الخادم وتتكفل برسوم الغاز، لذا لا يعرض الموقّع سوى 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"
});

فقرة

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 أو الرسوم قبل التوقيع، فإن الموقّع هو TransactionSendingSigner في Kit: استخدمه مع signAndSendTransactionMessageWithSigners، لا مع مسار الموقّع الجزئي الموضح أعلاه. يقبل الوضع الأصلي التلقائي فقط معاملة غير موقّعة يكون الموقّع الوحيد المطلوب فيها هو الخزنة، ولا يمكن استخدامه مع @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 كتابة الرسالة (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 البايتات الخام دون بثّها؛ وعليك أنت تجميع المعاملة وإرسالها. استخدم هذا الوضع مع خزنة الصندوق الأسود في 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 (TransactionPartialSigner في Kit): يُرجع التوقيعات لمعاملة تملكها.
  • SolanaModifyingSigner (TransactionModifyingSigner في Kit): يعيد كتابة المعاملة ويوقّعها ويُرجع المعاملة الجديدة دون بثّها.
  • SolanaSendingSigner (TransactionSendingSigner في Kit): يوقّع ويبثّ عبر مزوّده.
  • SolanaMessageSigner (MessagePartialSigner في Kit): يوقّع الرسائل خارج السلسلة. وهو منفصل عن أشكال المعاملات.

SolanaSigner هو اتحاد (union) أشكال المعاملات الثلاثة. يسمّي نوع الإرجاع لكل مصنع الشكل الدقيق لواجهته الخلفية. وعندما لا يُعرف الإعداد إلا أثناء التشغيل، استخدم التضييق عبر 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?