Rust

Installazione

Aggiungi solana-keychain al tuo Cargo.toml con solo i backend di cui hai bisogno:

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"] }

Stai passando da 1.x? Consulta la guida alla migrazione a 2.0.

Flag di Funzionalità

FunzionalitàDescrizione
memoryFirma keypair locale (predefinita)
vaultMotore Transit di HashiCorp Vault
aws_kmsAWS KMS con Ed25519
gcp_kmsGCP Cloud KMS con Ed25519
privyWallet integrati Privy
turnkeyAPI Turnkey
fireblocksFireblocks MPC
cdpCoinbase Developer Platform
crossmintWallet gestiti Crossmint
dfnsInfrastruttura wallet Dfns
openfortWallet integrati Openfort
paraWallet MPC Para
utilaCustodia MPC Utila
fordefiCustodia MPC Fordefi
ledgerWallet hardware Ledger tramite USB-HID (solo Rust, opt-in, non incluso in all)
allAbilita ogni backend tranne ledger
sdk-v2Solana SDK 2.x (predefinito)
sdk-v3Solana SDK 3.x
sdk-v4Solana SDK 4.x (richiesto per le transazioni v1)
unsafe-debugRegistra nei log le risposte di errore complete del provider; non abilitarlo mai in produzione

Deve essere abilitato esattamente uno tra sdk-v2, sdk-v3 e sdk-v4.

Utilizzo Base

Tutti i signer implementano il trait di base SolanaSigner (pubkey, sign_message, is_available). Ogni backend implementa inoltre esattamente un trait di capacità:

  • TransactionSigner: firma la transazione così com'è.
  • ModifyingSigner: il provider riscrive la transazione, quindi la firma.
  • SendingSigner: il provider la firma e la trasmette.
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(())
}

Converti una Transaction legacy con .into(). Le transazioni legacy e v0 funzionano con ogni feature SDK; le transazioni v1 richiedono sdk-v4.

Capacità dei signer

BackendTrait di capacitàsign_message
CrossmintSendingSignerNon supportato
Fordefi native autoSendingSignerSì
Fordefi native manualModifyingSignerSì
UtilaTransactionSignerNon supportato
CDPTransactionSignerSolo payload UTF-8
LedgerTransactionSignerSì, tramite un envelope off-chain
Tutti gli altri (incl. Fordefi black box)TransactionSignerSì

I costruttori Signer::from_* eseguono init() per te. Se costruisci direttamente i tipi di signer Privy, Fireblocks, Dfns, Crossmint, Para, Openfort o Utila, chiama init().await prima dell'uso.

Configurazione Backend

Firmatario in Memoria

Per sviluppo e test con keypair locali:

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

Per HSM self-hosted con motore 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

Per firma nativa nel cloud con chiavi Ed25519 di 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

Per firma con wallet integrato:

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

Per firma 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

Per la firma MPC istituzionale:

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

Per la firma cloud-native con chiavi Ed25519 di 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)

Per la firma con wallet gestito da 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 di CDP accetta solo payload UTF-8.

Crossmint

Crossmint supporta solo l'invio: la sua API esegue ogni transazione approvata lato server, quindi non esiste un percorso di sola firma e sign_message fallisce. Chiama sign_and_send_transaction, che lascia invariata la tua transazione e restituisce la firma della transazione effettivamente registrata on-chain. Tale transazione può differire da la tua se Crossmint sponsorizza il 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

Per la firma con infrastruttura wallet 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

Per la firma con wallet 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

Per la firma con wallet 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

Per la firma con custodia MPC Utila. sign_message non è supportato e la transazione firmata devi trasmetterla tu.

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

Per la firma con custodia MPC Fordefi. Le richieste API sono autenticate con una firma della richiesta ECDSA P-256. Fornisci esattamente uno tra private_key_pem o request_signer (un Arc<dyn FordefiRequestSigner>, usato per mantenere quella chiave in un KMS/HSM).

Fordefi è disponibile in tre tipi di signer, stabiliti alla costruzione da chain e push_mode:

chainpush_modeTipoCapacitàComportamento
NonequalsiasiFordefiBlackBoxSignerTransactionSignerFirma i byte grezzi; la trasmissione spetta a te
Some(..)None o AutoFordefiNativeAutoSignerSendingSignerFordefi può riscrivere blockhash e commissioni, firma e trasmette
Some(..)ManualFordefiNativeManualSignerModifyingSignerFordefi riscrive e firma; tu trasmetti la transazione restituita

La modalità native manual sostituisce la tua transazione con quella firmata da Fordefi. Procedi sempre da quest'ultima, mai da quella che hai creato. Deve essere eseguita prima di qualsiasi altro signer richiesto.

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

Per la firma con wallet hardware tramite USB-HID (solo Rust). Richiede la feature ledger, un dispositivo sbloccato con l'app Solana in esecuzione e, su Linux, le regole udev di Ledger. Ledger non è coperto dall'audit attuale; consulta lo stato dell'audit.

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

sign_message firma un envelope off-chain che racchiude il tuo payload, non i byte grezzi; ricostruiscilo con solana_keychain::ledger::ledger_offchain_envelope per verificarlo. Imposta auto_open_app: false per l'uso non presidiato. Una richiesta rifiutata sul dispositivo restituisce SignerError::UserRejected.

Enum Signer Unificato

Utilizza l'enum Signer per selezionare i backend in runtime:

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 implementa solo SolanaSigner. Accedi a una capacità tramite as_transaction_signer(), as_modifying_signer() o as_sending_signer(), che restituiscono None quando il backend non ha quella forma:

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

Oppure lascia che sign_and_send instradi in base al backend. Il crate non ha un client RPC, quindi la trasmissione la fornisci tu; per un backend di invio la closure non viene mai chiamata:

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

Gestione degli errori

SignerError::BroadcastUnconfirmed { provider_tx_id, idempotency_key, transaction_signature, .. } (da Crossmint, Fordefi native auto o da una closure sign_and_send che fallisce) significa che la transazione potrebbe essere stata registrata on-chain. Non riprovare alla cieca: riconcilia prima provider_tx_id o transaction_signature, oppure reinvia solo la transazione identica byte per byte. I match esaustivi su SignerError richiedono questo ramo. Consulta il modello di sicurezza per le garanzie offerte da ciascun tipo di firma.

Aggiungere Signer Personalizzati

Consulta la guida Aggiungere Signer per integrare ulteriori servizi di gestione delle chiavi.

Is this page helpful?

© 2026 Solana Foundation. Tutti i diritti riservati.