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).
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//! - `all`: Enable all signer backends
14//!
15//! ## SDK Version Selection
16//! - `sdk-v2` (default): Use Solana SDK v2.3.x
17//! - `sdk-v3`: Use Solana SDK v3.x
18//!
19//! **Note**: Only one SDK version can be enabled at a time.
20
21pub mod error;
22mod sdk_adapter;
23#[cfg(test)]
24pub mod test_util;
25#[cfg(feature = "integration-tests")]
26pub mod tests;
27pub mod traits;
28pub mod transaction_util;
29
30#[cfg(feature = "memory")]
31pub mod memory;
32
33#[cfg(feature = "vault")]
34pub mod vault;
35
36#[cfg(feature = "privy")]
37pub mod privy;
38
39#[cfg(feature = "turnkey")]
40pub mod turnkey;
41
42// Re-export core types
43pub use error::SignerError;
44pub use traits::SolanaSigner;
45
46// Re-export signer types
47#[cfg(feature = "memory")]
48pub use memory::MemorySigner;
49
50#[cfg(feature = "vault")]
51pub use vault::VaultSigner;
52
53#[cfg(feature = "privy")]
54pub use privy::PrivySigner;
55
56#[cfg(feature = "turnkey")]
57pub use turnkey::TurnkeySigner;
58
59use crate::traits::SignedTransaction;
60
61// Ensure at least one signer backend is enabled
62#[cfg(not(any(
63    feature = "memory",
64    feature = "vault",
65    feature = "privy",
66    feature = "turnkey"
67)))]
68compile_error!(
69    "At least one signer backend feature must be enabled: memory, vault, privy, or turnkey"
70);
71
72/// Unified signer enum supporting multiple backends
73pub enum Signer {
74    #[cfg(feature = "memory")]
75    Memory(MemorySigner),
76
77    #[cfg(feature = "vault")]
78    Vault(VaultSigner),
79
80    #[cfg(feature = "privy")]
81    Privy(PrivySigner),
82
83    #[cfg(feature = "turnkey")]
84    Turnkey(TurnkeySigner),
85}
86
87impl Signer {
88    /// Create a memory signer from a private key string
89    #[cfg(feature = "memory")]
90    pub fn from_memory(private_key: &str) -> Result<Self, SignerError> {
91        Ok(Self::Memory(MemorySigner::from_private_key_string(
92            private_key,
93        )?))
94    }
95
96    /// Create a Vault signer
97    #[cfg(feature = "vault")]
98    pub fn from_vault(
99        vault_addr: String,
100        vault_token: String,
101        key_name: String,
102        pubkey: String,
103    ) -> Result<Self, SignerError> {
104        Ok(Self::Vault(VaultSigner::new(
105            vault_addr,
106            vault_token,
107            key_name,
108            pubkey,
109        )?))
110    }
111
112    /// Create a Privy signer (requires initialization)
113    #[cfg(feature = "privy")]
114    pub async fn from_privy(
115        app_id: String,
116        app_secret: String,
117        wallet_id: String,
118    ) -> Result<Self, SignerError> {
119        let mut signer = PrivySigner::new(app_id, app_secret, wallet_id);
120        signer.init().await?;
121        Ok(Self::Privy(signer))
122    }
123
124    /// Create a Turnkey signer
125    #[cfg(feature = "turnkey")]
126    pub fn from_turnkey(
127        api_public_key: String,
128        api_private_key: String,
129        organization_id: String,
130        private_key_id: String,
131        public_key: String,
132    ) -> Result<Self, SignerError> {
133        Ok(Self::Turnkey(TurnkeySigner::new(
134            api_public_key,
135            api_private_key,
136            organization_id,
137            private_key_id,
138            public_key,
139        )?))
140    }
141}
142
143#[async_trait::async_trait]
144impl SolanaSigner for Signer {
145    fn pubkey(&self) -> sdk_adapter::Pubkey {
146        match self {
147            #[cfg(feature = "memory")]
148            Signer::Memory(s) => s.pubkey(),
149
150            #[cfg(feature = "vault")]
151            Signer::Vault(s) => s.pubkey(),
152
153            #[cfg(feature = "privy")]
154            Signer::Privy(s) => s.pubkey(),
155
156            #[cfg(feature = "turnkey")]
157            Signer::Turnkey(s) => s.pubkey(),
158        }
159    }
160
161    async fn sign_transaction(
162        &self,
163        tx: &mut sdk_adapter::Transaction,
164    ) -> Result<SignedTransaction, SignerError> {
165        match self {
166            #[cfg(feature = "memory")]
167            Signer::Memory(s) => s.sign_transaction(tx).await,
168
169            #[cfg(feature = "vault")]
170            Signer::Vault(s) => s.sign_transaction(tx).await,
171
172            #[cfg(feature = "privy")]
173            Signer::Privy(s) => s.sign_transaction(tx).await,
174
175            #[cfg(feature = "turnkey")]
176            Signer::Turnkey(s) => s.sign_transaction(tx).await,
177        }
178    }
179
180    async fn sign_message(&self, message: &[u8]) -> Result<sdk_adapter::Signature, SignerError> {
181        match self {
182            #[cfg(feature = "memory")]
183            Signer::Memory(s) => s.sign_message(message).await,
184
185            #[cfg(feature = "vault")]
186            Signer::Vault(s) => s.sign_message(message).await,
187
188            #[cfg(feature = "privy")]
189            Signer::Privy(s) => s.sign_message(message).await,
190
191            #[cfg(feature = "turnkey")]
192            Signer::Turnkey(s) => s.sign_message(message).await,
193        }
194    }
195
196    async fn sign_partial_transaction(
197        &self,
198        tx: &mut sdk_adapter::Transaction,
199    ) -> Result<SignedTransaction, SignerError> {
200        match self {
201            #[cfg(feature = "memory")]
202            Signer::Memory(s) => s.sign_partial_transaction(tx).await,
203
204            #[cfg(feature = "vault")]
205            Signer::Vault(s) => s.sign_partial_transaction(tx).await,
206
207            #[cfg(feature = "privy")]
208            Signer::Privy(s) => s.sign_partial_transaction(tx).await,
209
210            #[cfg(feature = "turnkey")]
211            Signer::Turnkey(s) => s.sign_partial_transaction(tx).await,
212        }
213    }
214
215    async fn is_available(&self) -> bool {
216        match self {
217            #[cfg(feature = "memory")]
218            Signer::Memory(s) => s.is_available().await,
219
220            #[cfg(feature = "vault")]
221            Signer::Vault(s) => s.is_available().await,
222
223            #[cfg(feature = "privy")]
224            Signer::Privy(s) => s.is_available().await,
225
226            #[cfg(feature = "turnkey")]
227            Signer::Turnkey(s) => s.is_available().await,
228        }
229    }
230}