Instalación
Agrega solana-keychain a tu Cargo.toml con solo los backends que necesites:
[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"] }
¿Actualizas desde la versión 1.x? Consulta la guía de migración a la versión 2.0.
Indicadores de Características
| Característica | Descripción |
|---|---|
memory | Firma con keypair local (predeterminado) |
vault | Motor Transit de HashiCorp Vault |
aws_kms | AWS KMS con Ed25519 |
gcp_kms | GCP Cloud KMS con Ed25519 |
privy | Carteras integradas de Privy |
turnkey | API de Turnkey |
fireblocks | MPC de Fireblocks |
cdp | Plataforma para Desarrolladores de Coinbase |
crossmint | Carteras gestionadas de Crossmint |
dfns | Infraestructura de carteras de Dfns |
openfort | Carteras integradas de Openfort |
para | Carteras MPC de Para |
utila | Custodia MPC de Utila |
fordefi | Custodia MPC de Fordefi |
ledger | Cartera de hardware Ledger mediante USB-HID (solo Rust, opcional, no incluida en all) |
all | Habilita todos los backends excepto ledger |
sdk-v2 | Solana SDK 2.x (predeterminado) |
sdk-v3 | Solana SDK 3.x |
sdk-v4 | Solana SDK 4.x (obligatorio para transacciones v1) |
unsafe-debug | Registra las respuestas de error completas del proveedor; nunca lo habilites en producción |
Debe habilitarse exactamente una de las opciones sdk-v2, sdk-v3 y sdk-v4.
Uso Básico
Todos los firmantes implementan el
trait SolanaSigner
base (pubkey, sign_message, is_available). Además, cada backend implementa exactamente
un trait de capacidad:
TransactionSigner: firma tu transacción tal como se proporciona.ModifyingSigner: el proveedor reescribe la transacción y luego la firma.SendingSigner: el proveedor la firma y la transmite.
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(())}
Convierte una Transaction heredada con .into(). Las transacciones heredadas y v0 funcionan
con todas las características del SDK; las transacciones v1 requieren sdk-v4.
Capacidades del firmante
| Backend | Trait de capacidad | sign_message |
|---|---|---|
| Crossmint | SendingSigner | No compatible |
| Fordefi nativo automático | SendingSigner | Sí |
| Fordefi nativo manual | ModifyingSigner | Sí |
| Utila | TransactionSigner | No compatible |
| CDP | TransactionSigner | Solo cargas útiles UTF-8 |
| Ledger | TransactionSigner | Sí, mediante un sobre fuera de la cadena |
| Todos los demás (incl. Fordefi de caja negra) | TransactionSigner | Sí |
Los constructores Signer::from_* ejecutan init() por ti. Si construyes
directamente los tipos de firmante de Privy, Fireblocks, Dfns, Crossmint, Para, Openfort o Utila,
llama a init().await antes de usarlos.
Configuración de Backends
Firmante en Memoria
Para desarrollo y pruebas con keypairs locales:
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
Para HSM autohospedado con motor 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
Para firma nativa en la nube con claves Ed25519 de 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
Para firma con wallet embebida:
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
Para firma sin custodia:
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
Para firma MPC institucional:
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
Para firma nativa en la nube con claves 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)
Para firma de billetera administrada por 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})?;
El sign_message de CDP solo acepta cargas útiles UTF-8.
Crossmint
Crossmint solo admite envío: su API ejecuta cada transacción aprobada
en el servidor, por lo que no existe una vía de solo firma y sign_message falla. Llama a
sign_and_send_transaction, que deja tu transacción intacta y devuelve
la firma de la transacción que se confirmó. Esa transacción puede diferir de
la tuya si Crossmint patrocina el 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
Para firma de infraestructura de billetera 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
Para firma de billetera 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
Para la firma con carteras de backend de 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
Para la firma con custodia MPC de Utila. sign_message no es compatible y
transmites tú mismo la transacción firmada.
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
Para la firma con custodia MPC de Fordefi. Las solicitudes a la API se autentican con una firma de solicitud
ECDSA P-256. Proporciona exactamente uno de estos: private_key_pem o
request_signer (un Arc<dyn FordefiRequestSigner>, que se usa para mantener esa clave en un
KMS/HSM).
Fordefi ofrece tres tipos de firmante, que se fijan en la construcción mediante chain y
push_mode:
chain | push_mode | Tipo | Capacidad | Comportamiento |
|---|---|---|---|---|
None | cualquiera | FordefiBlackBoxSigner | TransactionSigner | Firma bytes sin procesar; tú los transmites |
Some(..) | None o Auto | FordefiNativeAutoSigner | SendingSigner | Fordefi puede reescribir el blockhash y las comisiones, firma y transmite |
Some(..) | Manual | FordefiNativeManualSigner | ModifyingSigner | Fordefi reescribe y firma; tú transmites la transacción devuelta |
El modo nativo manual sustituye tu transacción por la que firmó Fordefi. Continúa a partir de esta, nunca de la que construiste. Debe ejecutarse antes que cualquier otro firmante obligatorio.
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
Para la firma con cartera de hardware mediante USB-HID (solo Rust). Requiere la característica ledger,
un dispositivo desbloqueado con la aplicación de Solana y, en Linux, las reglas udev
de Ledger. Ledger no está cubierto por la auditoría actual; consulta el
estado de la auditoría.
use solana_keychain::{LedgerConfig, Signer};let signer = Signer::from_ledger(LedgerConfig {confirm_pubkey_on_device: true,..LedgerConfig::default()}).await?;
sign_message firma un sobre fuera de la cadena que envuelve tu carga útil, no los bytes
sin procesar; reconstrúyelo con solana_keychain::ledger::ledger_offchain_envelope para
verificarlo. Establece auto_open_app: false para uso desatendido. Si se rechaza una solicitud
en el dispositivo, se devuelve SignerError::UserRejected.
Enumeración de Firmante Unificado
Usa la enumeración Signer para seleccionar backends en tiempo de ejecución:
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 solo implementa SolanaSigner. Accede a una capacidad mediante
as_transaction_signer(), as_modifying_signer() o as_sending_signer(),
que devuelven None cuando el backend no tiene esa forma:
if let Some(transaction_signer) = signer.as_transaction_signer() {let result = transaction_signer.sign_transaction(&mut tx).await?;}
O deja que sign_and_send elija según el backend. El crate no incluye un cliente RPC, así que
tú proporcionas la transmisión; en un backend de envío, el closure nunca se invoca:
let signature = signer.sign_and_send(&mut tx, |encoded| async move { broadcast(encoded).await }).await?;
Gestión de errores
SignerError::BroadcastUnconfirmed { provider_tx_id, idempotency_key, transaction_signature, .. }
(de Crossmint, Fordefi nativo automático o un closure de sign_and_send que falla)
significa que la transacción pudo haberse confirmado. No reintentes a ciegas: concilia primero
provider_tx_id o transaction_signature, o reenvía únicamente la
transacción idéntica byte a byte. Los match exhaustivos sobre SignerError necesitan esta rama.
Consulta el
modelo de seguridad
para saber qué garantiza cada forma de firma.
Agregar Firmantes Personalizados
Consulta la guía de Agregar Firmantes para integrar servicios adicionales de gestión de claves.
Is this page helpful?