Skip to main content

solana_keychain/
lib.rs

1//! Framework-agnostic Solana signing abstractions
2//!
3//! This crate provides a unified interface for signing Solana transactions
4//! with multiple backend implementations (memory, Vault, Privy, Turnkey, AWS KMS, Para).
5//!
6//! # Features
7//!
8//! ## Signer Backends
9//! - `memory` (default): Local keypair signing
10//! - `vault`: HashiCorp Vault integration
11//! - `privy`: Privy API integration
12//! - `turnkey`: Turnkey API integration
13//! - `aws_kms`: AWS KMS integration with EdDSA (Ed25519) signing
14//! - `fireblocks`: Fireblocks API integration
15//! - `gcp_kms`: GCP KMS integration with EdDSA (Ed25519) signing
16//! - `cdp`: Coinbase Developer Platform integration
17//! - `para`: Para MPC wallet integration
18//! - `dfns`: Dfns Wallet API integration
19//! - `all`: Enable all signer backends
20//!
21//! ## SDK Version Selection
22//! - `sdk-v2` (default): Use Solana SDK v2.3.x
23//! - `sdk-v3`: Use Solana SDK v3.x
24//!
25//! **Note**: Only one SDK version can be enabled at a time.
26
27pub mod error;
28mod sdk_adapter;
29pub mod signature_util;
30#[cfg(test)]
31pub mod test_util;
32#[cfg(feature = "integration-tests")]
33pub mod tests;
34pub mod traits;
35pub mod transaction_util;
36
37#[cfg(feature = "memory")]
38pub mod memory;
39
40#[cfg(feature = "vault")]
41pub mod vault;
42
43#[cfg(feature = "privy")]
44pub mod privy;
45
46#[cfg(feature = "turnkey")]
47pub mod turnkey;
48
49#[cfg(feature = "aws_kms")]
50pub mod aws_kms;
51
52#[cfg(feature = "fireblocks")]
53pub mod fireblocks;
54
55#[cfg(feature = "gcp_kms")]
56pub mod gcp_kms;
57
58#[cfg(feature = "cdp")]
59pub mod cdp;
60#[cfg(feature = "dfns")]
61pub mod dfns;
62#[cfg(feature = "para")]
63pub mod para;
64
65// Re-export core types
66pub use error::SignerError;
67pub use traits::SolanaSigner;
68
69// Re-export signer types
70#[cfg(feature = "memory")]
71pub use memory::MemorySigner;
72
73#[cfg(feature = "vault")]
74pub use vault::VaultSigner;
75
76#[cfg(feature = "privy")]
77pub use privy::PrivySigner;
78
79#[cfg(feature = "turnkey")]
80pub use turnkey::TurnkeySigner;
81
82#[cfg(feature = "aws_kms")]
83pub use aws_kms::AwsKmsSigner;
84
85#[cfg(feature = "fireblocks")]
86pub use fireblocks::{FireblocksSigner, FireblocksSignerConfig};
87
88#[cfg(feature = "gcp_kms")]
89pub use gcp_kms::GcpKmsSigner;
90
91#[cfg(feature = "cdp")]
92pub use cdp::CdpSigner;
93#[cfg(feature = "dfns")]
94pub use dfns::{DfnsSigner, DfnsSignerConfig};
95#[cfg(feature = "para")]
96pub use para::ParaSigner;
97
98use crate::traits::SignedTransaction;
99
100// Ensure at least one signer backend is enabled
101#[cfg(not(any(
102    feature = "memory",
103    feature = "vault",
104    feature = "privy",
105    feature = "turnkey",
106    feature = "aws_kms",
107    feature = "fireblocks",
108    feature = "gcp_kms",
109    feature = "cdp",
110    feature = "dfns",
111    feature = "para"
112)))]
113compile_error!(
114    "At least one signer backend feature must be enabled: memory, vault, privy, turnkey, aws_kms, fireblocks, gcp_kms, cdp, para, or dfns"
115);
116
117/// Unified signer enum supporting multiple backends
118pub enum Signer {
119    #[cfg(feature = "memory")]
120    Memory(MemorySigner),
121
122    #[cfg(feature = "vault")]
123    Vault(VaultSigner),
124
125    #[cfg(feature = "privy")]
126    Privy(PrivySigner),
127
128    #[cfg(feature = "turnkey")]
129    Turnkey(TurnkeySigner),
130
131    #[cfg(feature = "aws_kms")]
132    AwsKms(AwsKmsSigner),
133
134    #[cfg(feature = "fireblocks")]
135    Fireblocks(FireblocksSigner),
136
137    #[cfg(feature = "gcp_kms")]
138    GcpKms(GcpKmsSigner),
139
140    #[cfg(feature = "cdp")]
141    Cdp(CdpSigner),
142    #[cfg(feature = "dfns")]
143    Dfns(DfnsSigner),
144    #[cfg(feature = "para")]
145    Para(ParaSigner),
146}
147
148impl Signer {
149    /// Create a memory signer from a private key string
150    #[cfg(feature = "memory")]
151    pub fn from_memory(private_key: &str) -> Result<Self, SignerError> {
152        Ok(Self::Memory(MemorySigner::from_private_key_string(
153            private_key,
154        )?))
155    }
156
157    /// Create a Vault signer
158    #[cfg(feature = "vault")]
159    pub fn from_vault(
160        vault_addr: String,
161        vault_token: String,
162        key_name: String,
163        pubkey: String,
164    ) -> Result<Self, SignerError> {
165        Ok(Self::Vault(VaultSigner::new(
166            vault_addr,
167            vault_token,
168            key_name,
169            pubkey,
170        )?))
171    }
172
173    /// Create a Privy signer (requires initialization)
174    #[cfg(feature = "privy")]
175    pub async fn from_privy(
176        app_id: String,
177        app_secret: String,
178        wallet_id: String,
179    ) -> Result<Self, SignerError> {
180        let mut signer = PrivySigner::new(app_id, app_secret, wallet_id);
181        signer.init().await?;
182        Ok(Self::Privy(signer))
183    }
184
185    /// Create a Turnkey signer
186    #[cfg(feature = "turnkey")]
187    pub fn from_turnkey(
188        api_public_key: String,
189        api_private_key: String,
190        organization_id: String,
191        private_key_id: String,
192        public_key: String,
193    ) -> Result<Self, SignerError> {
194        Ok(Self::Turnkey(TurnkeySigner::new(
195            api_public_key,
196            api_private_key,
197            organization_id,
198            private_key_id,
199            public_key,
200        )?))
201    }
202
203    /// Create an AWS KMS signer (requires initialization)
204    #[cfg(feature = "aws_kms")]
205    pub async fn from_aws_kms(
206        key_id: String,
207        public_key: String,
208        region: Option<String>,
209    ) -> Result<Self, SignerError> {
210        Ok(Self::AwsKms(
211            AwsKmsSigner::new(key_id, public_key, region).await?,
212        ))
213    }
214
215    /// Create a Fireblocks signer (requires initialization)
216    #[cfg(feature = "fireblocks")]
217    pub async fn from_fireblocks(config: FireblocksSignerConfig) -> Result<Self, SignerError> {
218        let mut signer = FireblocksSigner::new(config);
219        signer.init().await?;
220        Ok(Self::Fireblocks(signer))
221    }
222
223    /// Create a GCP KMS signer (requires initialization)
224    #[cfg(feature = "gcp_kms")]
225    pub async fn from_gcp_kms(key_name: String, public_key: String) -> Result<Self, SignerError> {
226        Ok(Self::GcpKms(GcpKmsSigner::new(key_name, public_key).await?))
227    }
228
229    /// Create a Para signer (requires initialization)
230    #[cfg(feature = "para")]
231    pub async fn from_para(
232        api_key: String,
233        wallet_id: String,
234        api_base_url: Option<String>,
235    ) -> Result<Self, SignerError> {
236        let mut signer = ParaSigner::new(api_key, wallet_id, api_base_url)?;
237        signer.init().await?;
238        Ok(Self::Para(signer))
239    }
240
241    /// Create a CDP signer
242    #[cfg(feature = "cdp")]
243    pub fn from_cdp(
244        api_key_id: String,
245        api_key_secret: String,
246        wallet_secret: String,
247        address: String,
248    ) -> Result<Self, SignerError> {
249        Ok(Self::Cdp(CdpSigner::new(
250            api_key_id,
251            api_key_secret,
252            wallet_secret,
253            address,
254        )?))
255    }
256
257    /// Create a Dfns signer (requires initialization)
258    #[cfg(feature = "dfns")]
259    pub async fn from_dfns(config: DfnsSignerConfig) -> Result<Self, SignerError> {
260        let mut signer = DfnsSigner::new(config);
261        signer.init().await?;
262        Ok(Self::Dfns(signer))
263    }
264}
265
266#[async_trait::async_trait]
267impl SolanaSigner for Signer {
268    fn pubkey(&self) -> sdk_adapter::Pubkey {
269        match self {
270            #[cfg(feature = "memory")]
271            Signer::Memory(s) => s.pubkey(),
272
273            #[cfg(feature = "vault")]
274            Signer::Vault(s) => s.pubkey(),
275
276            #[cfg(feature = "privy")]
277            Signer::Privy(s) => s.pubkey(),
278
279            #[cfg(feature = "turnkey")]
280            Signer::Turnkey(s) => s.pubkey(),
281
282            #[cfg(feature = "aws_kms")]
283            Signer::AwsKms(s) => s.pubkey(),
284
285            #[cfg(feature = "fireblocks")]
286            Signer::Fireblocks(s) => s.pubkey(),
287
288            #[cfg(feature = "gcp_kms")]
289            Signer::GcpKms(s) => s.pubkey(),
290
291            #[cfg(feature = "cdp")]
292            Signer::Cdp(s) => s.pubkey(),
293            #[cfg(feature = "dfns")]
294            Signer::Dfns(s) => s.pubkey(),
295            #[cfg(feature = "para")]
296            Signer::Para(s) => s.pubkey(),
297        }
298    }
299
300    async fn sign_transaction(
301        &self,
302        tx: &mut sdk_adapter::Transaction,
303    ) -> Result<SignedTransaction, SignerError> {
304        match self {
305            #[cfg(feature = "memory")]
306            Signer::Memory(s) => s.sign_transaction(tx).await,
307
308            #[cfg(feature = "vault")]
309            Signer::Vault(s) => s.sign_transaction(tx).await,
310
311            #[cfg(feature = "privy")]
312            Signer::Privy(s) => s.sign_transaction(tx).await,
313
314            #[cfg(feature = "turnkey")]
315            Signer::Turnkey(s) => s.sign_transaction(tx).await,
316
317            #[cfg(feature = "aws_kms")]
318            Signer::AwsKms(s) => s.sign_transaction(tx).await,
319
320            #[cfg(feature = "fireblocks")]
321            Signer::Fireblocks(s) => s.sign_transaction(tx).await,
322
323            #[cfg(feature = "gcp_kms")]
324            Signer::GcpKms(s) => s.sign_transaction(tx).await,
325
326            #[cfg(feature = "cdp")]
327            Signer::Cdp(s) => s.sign_transaction(tx).await,
328            #[cfg(feature = "dfns")]
329            Signer::Dfns(s) => s.sign_transaction(tx).await,
330            #[cfg(feature = "para")]
331            Signer::Para(s) => s.sign_transaction(tx).await,
332        }
333    }
334
335    async fn sign_message(&self, message: &[u8]) -> Result<sdk_adapter::Signature, SignerError> {
336        match self {
337            #[cfg(feature = "memory")]
338            Signer::Memory(s) => s.sign_message(message).await,
339
340            #[cfg(feature = "vault")]
341            Signer::Vault(s) => s.sign_message(message).await,
342
343            #[cfg(feature = "privy")]
344            Signer::Privy(s) => s.sign_message(message).await,
345
346            #[cfg(feature = "turnkey")]
347            Signer::Turnkey(s) => s.sign_message(message).await,
348
349            #[cfg(feature = "aws_kms")]
350            Signer::AwsKms(s) => s.sign_message(message).await,
351
352            #[cfg(feature = "fireblocks")]
353            Signer::Fireblocks(s) => s.sign_message(message).await,
354
355            #[cfg(feature = "gcp_kms")]
356            Signer::GcpKms(s) => s.sign_message(message).await,
357
358            #[cfg(feature = "cdp")]
359            Signer::Cdp(s) => s.sign_message(message).await,
360            #[cfg(feature = "dfns")]
361            Signer::Dfns(s) => s.sign_message(message).await,
362            #[cfg(feature = "para")]
363            Signer::Para(s) => s.sign_message(message).await,
364        }
365    }
366
367    async fn sign_partial_transaction(
368        &self,
369        tx: &mut sdk_adapter::Transaction,
370    ) -> Result<SignedTransaction, SignerError> {
371        match self {
372            #[cfg(feature = "memory")]
373            Signer::Memory(s) => s.sign_partial_transaction(tx).await,
374
375            #[cfg(feature = "vault")]
376            Signer::Vault(s) => s.sign_partial_transaction(tx).await,
377
378            #[cfg(feature = "privy")]
379            Signer::Privy(s) => s.sign_partial_transaction(tx).await,
380
381            #[cfg(feature = "turnkey")]
382            Signer::Turnkey(s) => s.sign_partial_transaction(tx).await,
383
384            #[cfg(feature = "aws_kms")]
385            Signer::AwsKms(s) => s.sign_partial_transaction(tx).await,
386
387            #[cfg(feature = "fireblocks")]
388            Signer::Fireblocks(s) => s.sign_partial_transaction(tx).await,
389
390            #[cfg(feature = "gcp_kms")]
391            Signer::GcpKms(s) => s.sign_partial_transaction(tx).await,
392
393            #[cfg(feature = "cdp")]
394            Signer::Cdp(s) => s.sign_partial_transaction(tx).await,
395            #[cfg(feature = "dfns")]
396            Signer::Dfns(s) => s.sign_partial_transaction(tx).await,
397            #[cfg(feature = "para")]
398            Signer::Para(s) => s.sign_partial_transaction(tx).await,
399        }
400    }
401
402    async fn is_available(&self) -> bool {
403        match self {
404            #[cfg(feature = "memory")]
405            Signer::Memory(s) => s.is_available().await,
406
407            #[cfg(feature = "vault")]
408            Signer::Vault(s) => s.is_available().await,
409
410            #[cfg(feature = "privy")]
411            Signer::Privy(s) => s.is_available().await,
412
413            #[cfg(feature = "turnkey")]
414            Signer::Turnkey(s) => s.is_available().await,
415
416            #[cfg(feature = "aws_kms")]
417            Signer::AwsKms(s) => s.is_available().await,
418
419            #[cfg(feature = "fireblocks")]
420            Signer::Fireblocks(s) => s.is_available().await,
421
422            #[cfg(feature = "gcp_kms")]
423            Signer::GcpKms(s) => s.is_available().await,
424
425            #[cfg(feature = "cdp")]
426            Signer::Cdp(s) => s.is_available().await,
427            #[cfg(feature = "dfns")]
428            Signer::Dfns(s) => s.is_available().await,
429            #[cfg(feature = "para")]
430            Signer::Para(s) => s.is_available().await,
431        }
432    }
433}