Installazione
Aggiungi solana-keychain al tuo Cargo.toml con solo i backend di cui hai
bisogno:
[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 linesolana-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 |
|---|---|
memory | Firma keypair locale (predefinita) |
vault | Motore Transit di HashiCorp Vault |
aws_kms | AWS KMS con Ed25519 |
gcp_kms | GCP Cloud KMS con Ed25519 |
privy | Wallet integrati Privy |
turnkey | API Turnkey |
fireblocks | Fireblocks MPC |
cdp | Coinbase Developer Platform |
crossmint | Wallet gestiti Crossmint |
dfns | Infrastruttura wallet Dfns |
openfort | Wallet integrati Openfort |
para | Wallet MPC Para |
utila | Custodia MPC Utila |
fordefi | Custodia MPC Fordefi |
ledger | Wallet hardware Ledger tramite USB-HID (solo Rust, opt-in, non incluso in all) |
all | Abilita ogni backend tranne ledger |
sdk-v2 | Solana SDK 2.x (predefinito) |
sdk-v3 | Solana SDK 3.x |
sdk-v4 | Solana SDK 4.x (richiesto per le transazioni v1) |
unsafe-debug | Registra 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 keylet 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 Partiallet 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
| Backend | Trait di capacità | sign_message |
|---|---|---|
| Crossmint | SendingSigner | Non supportato |
| Fordefi native auto | SendingSigner | Sì |
| Fordefi native manual | ModifyingSigner | Sì |
| Utila | TransactionSigner | Non supportato |
| CDP | TransactionSigner | Solo payload UTF-8 |
| Ledger | TransactionSigner | Sì, tramite un envelope off-chain |
| Tutti gli altri (incl. Fordefi black box) | TransactionSigner | Sì |
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 keylet signer = Signer::from_memory("base58_private_key")?;// From JSON keypair file pathlet signer = Signer::from_memory_file("/path/to/keypair.json")?;// From byte array stringlet 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 keyNone, // 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 keySome("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 IDNone, // 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 keyNone, // 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 devnetapi_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 IDapi_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 addressnetwork: Some(CDP_NETWORK_MAINNET.to_string()), // Required to sign transactions with address lookup tablesapi_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 IDNone, // 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:
chain | push_mode | Tipo | Capacità | Comportamento |
|---|---|---|---|---|
None | qualsiasi | FordefiBlackBoxSigner | TransactionSigner | Firma i byte grezzi; la trasmissione spetta a te |
Some(..) | None o Auto | FordefiNativeAutoSigner | SendingSigner | Fordefi può riscrivere blockhash e commissioni, firma e trasmette |
Some(..) | Manual | FordefiNativeManualSigner | ModifyingSigner | Fordefi 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 addresschain: Some(SolanaChainUniqueId::SolanaDevnet), // None for black box modefee: Some(FordefiSolanaFee::Priority {priority_level: FordefiPriorityLevel::Medium,}),push_mode: Some(FordefiPushMode::Auto), // Manual: sign without broadcastingapi_base_url: None, // Default: https://api.fordefi.compoll_interval_ms: None, // Default: 2000max_poll_attempts: None, // Default: 50http_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 genericallylet 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?