Rust

Installation

Ajoutez solana-keychain à votre Cargo.toml avec uniquement les backends dont vous avez besoin :

Cargo.toml
[dependencies]
# Memory signer only (default, Solana SDK 2.x)
solana-keychain = "2.0"
# Or specify backends explicitly (pick exactly one SDK feature)
solana-keychain = { version = "2.0", default-features = false, features = ["vault", "aws_kms", "sdk-v2"] }
# All remote backends on the Solana 4.x SDK line
solana-keychain = { version = "2.0", default-features = false, features = ["all", "sdk-v4"] }

Vous migrez depuis la version 1.x ? Consultez le guide de migration vers la version 2.0.

Indicateurs de fonctionnalités

FonctionnalitéDescription
memorySignature keypair locale (par défaut)
vaultMoteur Transit HashiCorp Vault
aws_kmsAWS KMS avec Ed25519
gcp_kmsGCP Cloud KMS avec Ed25519
privyPortefeuilles intégrés Privy
turnkeyAPI Turnkey
fireblocksMPC Fireblocks
cdpCoinbase Developer Platform
crossmintPortefeuilles gérés Crossmint
dfnsInfrastructure de portefeuilles Dfns
openfortPortefeuilles intégrés Openfort
paraPortefeuilles MPC Para
utilaConservation MPC Utila
fordefiGarde MPC Fordefi
ledgerPortefeuille matériel Ledger via USB-HID (Rust uniquement, sur option, non inclus dans all)
allActive tous les backends sauf ledger
sdk-v2SDK Solana 2.x (par défaut)
sdk-v3SDK Solana 3.x
sdk-v4SDK Solana 4.x (requis pour les transactions v1)
unsafe-debugJournalise les réponses d'erreur complètes du fournisseur ; ne l'activez jamais en production

Une seule des options sdk-v2, sdk-v3 et sdk-v4 doit être activée, ni plus, ni moins.

Utilisation de base

Tous les signataires implémentent le trait SolanaSigner de base (pubkey, sign_message, is_available). Chaque backend implémente en outre exactement un trait de capacité :

  • TransactionSigner : signe votre transaction telle quelle.
  • ModifyingSigner : le fournisseur réécrit la transaction, puis la signe.
  • SendingSigner : le fournisseur la signe et la diffuse.
use solana_keychain::{SignerError, SolanaSigner, TransactionSigner};
use solana_sdk::transaction::VersionedTransaction;
async fn sign_transaction(
signer: &impl TransactionSigner,
tx: &mut VersionedTransaction,
) -> Result<(), SignerError> {
// Get the signer's public key
let pubkey = signer.pubkey();
println!("Signing with: {}", pubkey);
// Check availability (useful for remote signers)
if !signer.is_available().await {
return Err(SignerError::NotAvailable("Signer offline".into()));
}
// Sign the transaction: returns Complete or Partial
let result = signer.sign_transaction(tx).await?;
let (base64_tx, signature) = result.into_signed_transaction();
println!("Signature: {}", signature);
Ok(())
}

Convertissez une Transaction héritée (legacy) avec .into(). Les transactions legacy et v0 fonctionnent avec toutes les fonctionnalités du SDK ; les transactions v1 nécessitent sdk-v4.

Capacités des signataires

BackendTrait de capacitésign_message
CrossmintSendingSignerNon pris en charge
Fordefi natif automatiqueSendingSignerOui
Fordefi natif manuelModifyingSignerOui
UtilaTransactionSignerNon pris en charge
CDPTransactionSignerCharges utiles UTF-8 uniquement
LedgerTransactionSignerOui, via une enveloppe hors chaîne
Tous les autres (y compris Fordefi boîte noire)TransactionSignerOui

Les constructeurs Signer::from_* exécutent init() pour vous. Si vous construisez directement les types de signataires Privy, Fireblocks, Dfns, Crossmint, Para, Openfort ou Utila, appelez init().await avant de les utiliser.

Configuration des backends

Signataire en mémoire

Pour le développement et les tests avec des paires de clés locales :

use solana_keychain::Signer;
// From base58 private key
let signer = Signer::from_memory("base58_private_key")?;
// From JSON keypair file path
let signer = Signer::from_memory_file("/path/to/keypair.json")?;
// From byte array string
let signer = Signer::from_memory("[1,2,3,...]")?;

HashiCorp Vault

Pour HSM auto-hébergé avec moteur Transit :

use solana_keychain::Signer;
let signer = Signer::from_vault(
"https://vault.example.com:8200".to_string(), // Vault address
"hvs.xxxxx".to_string(), // Vault token
"my-solana-key".to_string(), // Key name in Transit
"base58_public_key".to_string(), // Expected public key
None, // Default HTTP timeouts
)?;

AWS KMS

Pour signature cloud-native avec clés Ed25519 AWS KMS :

use solana_keychain::Signer;
let signer = Signer::from_aws_kms(
"alias/my-solana-key".to_string(), // KMS key ID or alias
"base58_public_key".to_string(), // Expected public key
Some("us-east-1".to_string()), // Region (optional)
).await?;

Privy

Pour signature de portefeuille intégré :

use solana_keychain::Signer;
let signer = Signer::from_privy(
"app_id".to_string(), // Privy app ID
"app_secret".to_string(), // Privy app secret
"wallet_id".to_string(), // Wallet ID
None, // Default HTTP timeouts
).await?;

Turnkey

Pour signature non-custodiale :

use solana_keychain::Signer;
let signer = Signer::from_turnkey(
"api_public_key".to_string(), // Turnkey API public key
"api_private_key".to_string(), // Turnkey API private key
"org_id".to_string(), // Organization ID
"private_key_id".to_string(), // Private key ID in Turnkey
"base58_public_key".to_string(),// Expected public key
None, // Default HTTP timeouts
)?;

Fireblocks

Pour la signature MPC institutionnelle :

use solana_keychain::{Signer, FireblocksSignerConfig};
let config = FireblocksSignerConfig {
api_key: "api_key".to_string(),
private_key_pem: "-----BEGIN RSA PRIVATE KEY-----\n...".to_string(),
vault_account_id: "0".to_string(),
asset_id: Some("SOL".to_string()), // None defaults to "SOL"; "SOL_TEST" for devnet
api_base_url: None,
poll_interval_ms: None,
max_poll_attempts: None,
use_program_call: None, // Some(true) signs via PROGRAM_CALL (sign-only, legacy/v0, hot wallet)
http_client_config: None,
};
let signer = Signer::from_fireblocks(config).await?;

GCP KMS

Pour la signature native cloud avec les clés Ed25519 de GCP Cloud KMS :

use solana_keychain::Signer;
let signer = Signer::from_gcp_kms(
"projects/my-project/locations/us-east1/keyRings/my-ring/cryptoKeys/my-key/cryptoKeyVersions/1".to_string(),
"base58_public_key".to_string(),
).await?;

CDP (Coinbase Developer Platform)

Pour la signature de portefeuille géré par Coinbase :

use solana_keychain::{Signer, CdpSignerConfig, CDP_NETWORK_MAINNET};
let signer = Signer::from_cdp(CdpSignerConfig {
api_key_id: "api_key_id".to_string(), // CDP API key ID
api_key_secret: "api_key_secret".to_string(), // CDP API private key (base64 Ed25519)
wallet_secret: "wallet_secret".to_string(), // CDP wallet secret (base64 PKCS#8 DER)
address: "base58_address".to_string(), // Solana account address
network: Some(CDP_NETWORK_MAINNET.to_string()), // Required to sign transactions with address lookup tables
api_base_url: None,
http_client_config: None, // Default HTTP timeouts
})?;

sign_message de CDP n'accepte que les charges utiles UTF-8.

Crossmint

Crossmint fonctionne uniquement en mode envoi : son API exécute chaque transaction approuvée côté serveur, il n'existe donc aucun chemin de signature seule, et sign_message échoue. Appelez sign_and_send_transaction, qui laisse votre transaction intacte et renvoie la signature de la transaction effectivement enregistrée. Cette transaction peut différer de la vôtre si Crossmint sponsorise les frais de gas.

use solana_keychain::{Signer, CrossmintSignerConfig};
let config = CrossmintSignerConfig {
api_key: "api_key".to_string(),
wallet_locator: "wallet_locator".to_string(),
signer_secret: None,
signer: None,
api_base_url: None,
poll_interval_ms: None,
max_poll_attempts: None,
};
let signer = Signer::from_crossmint(config).await?;
let signature = signer
.as_sending_signer()
.expect("Crossmint is a sending signer")
.sign_and_send_transaction(&tx)
.await?;

Dfns

Pour la signature de l'infrastructure de portefeuille Dfns :

use solana_keychain::{Signer, DfnsSignerConfig};
let config = DfnsSignerConfig {
auth_token: "auth_token".to_string(),
cred_id: "cred_id".to_string(),
private_key_pem: "-----BEGIN EC PRIVATE KEY-----\n...".to_string(),
wallet_id: "wallet_id".to_string(),
api_base_url: None,
http_client_config: None,
};
let signer = Signer::from_dfns(config).await?;

Para

Pour la signature de portefeuille MPC Para :

use solana_keychain::Signer;
let signer = Signer::from_para(
"api_key".to_string(), // Para API key
"wallet_id".to_string(), // Para wallet ID
None, // Default API base URL
).await?;

Openfort

Pour la signature avec un portefeuille backend Openfort :

use solana_keychain::Signer;
let signer = Signer::from_openfort(
"sk_live_...".to_string(), // Openfort secret key
"acc_...".to_string(), // Account ID
"wallet_secret".to_string(), // Wallet secret (base64 PKCS#8 DER or PEM)
None, // Default HTTP timeouts
).await?;

Utila

Pour la signature avec la conservation MPC d'Utila. sign_message n'est pas pris en charge, et vous diffusez vous-même la transaction signée.

use solana_keychain::{Signer, UtilaSignerConfig};
let signer = Signer::from_utila(UtilaSignerConfig {
service_account_email: "sa@example.com".to_string(),
service_account_private_key_pem: "-----BEGIN PRIVATE KEY-----\n...".to_string(),
vault_id: "vault_id".to_string(),
wallet_id: "wallet_id".to_string(),
network: "networks/solana-devnet".to_string(),
api_base_url: None,
poll_interval_ms: None,
max_poll_attempts: None,
designated_signers: None,
http_client_config: None,
}).await?;

Fordefi

Pour la signature avec la conservation MPC de Fordefi. Les requêtes API sont authentifiées par une signature de requête ECDSA P-256. Fournissez exactement l'un des éléments suivants : private_key_pem ou request_signer (un Arc<dyn FordefiRequestSigner>, utilisé pour conserver cette clé dans un KMS/HSM).

Fordefi existe en trois types de signataires, fixés à la construction par chain et push_mode :

chainpush_modeTypeCapacitéComportement
Nonen'importe lequelFordefiBlackBoxSignerTransactionSignerSigne les octets bruts ; vous diffusez
Some(..)None ou AutoFordefiNativeAutoSignerSendingSignerFordefi peut réécrire le blockhash et les frais, puis signe et diffuse
Some(..)ManualFordefiNativeManualSignerModifyingSignerFordefi réécrit et signe ; vous diffusez la transaction renvoyée

Le mode natif manuel remplace votre transaction par celle que Fordefi a signée. Poursuivez à partir de celle-ci, jamais de celle que vous avez construite. Il doit s'exécuter avant tout autre signataire requis.

use solana_keychain::{
Signer, FordefiSignerConfig, FordefiPushMode, SolanaChainUniqueId, FordefiSolanaFee,
FordefiPriorityLevel,
};
let config = FordefiSignerConfig {
access_token: "api_token".to_string(),
vault_id: "vault_uuid".to_string(),
private_key_pem: Some(std::fs::read_to_string("./secret/private.pem")?),
request_signer: None, // Or Some(Arc::new(your_kms_signer))
public_key: "base58_public_key".to_string(), // Solana vault address
chain: Some(SolanaChainUniqueId::SolanaDevnet), // None for black box mode
fee: Some(FordefiSolanaFee::Priority {
priority_level: FordefiPriorityLevel::Medium,
}),
push_mode: Some(FordefiPushMode::Auto), // Manual: sign without broadcasting
api_base_url: None, // Default: https://api.fordefi.com
poll_interval_ms: None, // Default: 2000
max_poll_attempts: None, // Default: 50
http_client_config: None, // Default HTTP timeouts
};
let signer = Signer::from_fordefi(config).await?;

Ledger

Pour la signature avec un portefeuille matériel via USB-HID (Rust uniquement). Nécessite la fonctionnalité ledger, un appareil déverrouillé exécutant l'application Solana et, sous Linux, les règles udev de Ledger. Ledger n'est pas couvert par l'audit actuel ; consultez l'état de l'audit.

use solana_keychain::{LedgerConfig, Signer};
let signer = Signer::from_ledger(LedgerConfig {
confirm_pubkey_on_device: true,
..LedgerConfig::default()
})
.await?;

sign_message signe une enveloppe hors chaîne encapsulant votre charge utile, et non les octets bruts ; reconstruisez-la avec solana_keychain::ledger::ledger_offchain_envelope pour la vérifier. Définissez auto_open_app: false pour une utilisation sans surveillance. Une invite refusée sur l'appareil renvoie SignerError::UserRejected.

Énumération unifiée des signataires

Utilisez l'énumération Signer pour sélectionner les backends à l'exécution :

use solana_keychain::{Signer, SolanaSigner, SignerError};
async fn get_signer(backend: &str) -> Result<Signer, SignerError> {
match backend {
"memory" => Signer::from_memory("base58_key"),
"vault" => Signer::from_vault(/* ... */),
"kms" => Signer::from_aws_kms(/* ... */).await,
_ => Err(SignerError::ConfigError("Unknown backend".into())),
}
}
// Use it generically
let signer = get_signer("kms").await?;
let pubkey = signer.pubkey();

Signer n'implémente que SolanaSigner. Accédez à une capacité via as_transaction_signer(), as_modifying_signer() ou as_sending_signer(), qui renvoient None lorsque le backend ne prend pas en charge ce mode :

if let Some(transaction_signer) = signer.as_transaction_signer() {
let result = transaction_signer.sign_transaction(&mut tx).await?;
}

Ou laissez sign_and_send router selon le backend. La crate n'inclut aucun client RPC, c'est donc à vous de fournir la diffusion ; pour un backend d'envoi, la closure n'est jamais appelée :

let signature = signer
.sign_and_send(&mut tx, |encoded| async move { broadcast(encoded).await })
.await?;

Gestion des erreurs

SignerError::BroadcastUnconfirmed { provider_tx_id, idempotency_key, transaction_signature, .. } (provenant de Crossmint, de Fordefi natif automatique ou d'une closure sign_and_send en échec) signifie que la transaction a peut-être été enregistrée. Ne réessayez pas à l'aveugle : rapprochez d'abord provider_tx_id ou transaction_signature, ou renvoyez uniquement la transaction identique octet pour octet. Les match exhaustifs sur SignerError doivent inclure cette branche. Consultez le modèle de sécurité pour connaître les garanties offertes par chaque mode de signature.

Ajout de signataires personnalisés

Consultez le guide d'ajout de signataires pour intégrer des services supplémentaires de gestion de clés.

Is this page helpful?

© 2026 Fondation Solana. Tous droits réservés.