kobe_sol/
derivation_style.rs1use alloc::{format, string::String};
7use core::fmt;
8use core::str::FromStr;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
36#[non_exhaustive]
37pub enum DerivationStyle {
38 #[default]
50 Standard,
51
52 Trust,
63
64 LedgerLive,
75
76 #[deprecated(
83 note = "Use Standard style for new wallets. Legacy is only for recovering old Phantom/Sollet wallets."
84 )]
85 Legacy,
86}
87
88impl DerivationStyle {
89 #[must_use]
99 #[allow(deprecated)]
100 pub fn path(self, index: u32) -> String {
101 match self {
102 Self::Standard => format!("m/44'/501'/{index}'/0'"),
103 Self::Trust => format!("m/44'/501'/{index}'"),
104 Self::LedgerLive => format!("m/44'/501'/{index}'/0'/0'"),
105 Self::Legacy => format!("m/501'/{index}'/0/0"),
106 }
107 }
108
109 #[must_use]
111 #[allow(deprecated)]
112 pub const fn name(self) -> &'static str {
113 match self {
114 Self::Standard => "Standard (Phantom/Backpack)",
115 Self::Trust => "Trust (Ledger/Keystone)",
116 Self::LedgerLive => "Ledger Live",
117 Self::Legacy => "Legacy (deprecated)",
118 }
119 }
120
121 #[must_use]
123 #[allow(deprecated)]
124 pub const fn id(self) -> &'static str {
125 match self {
126 Self::Standard => "standard",
127 Self::Trust => "trust",
128 Self::LedgerLive => "ledger-live",
129 Self::Legacy => "legacy",
130 }
131 }
132
133 #[must_use]
135 #[allow(deprecated)]
136 pub const fn all() -> &'static [Self] {
137 &[Self::Standard, Self::Trust, Self::LedgerLive, Self::Legacy]
138 }
139}
140
141impl fmt::Display for DerivationStyle {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 write!(f, "{}", self.name())
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, Eq)]
149pub struct ParseDerivationStyleError(pub(crate) String);
150
151impl fmt::Display for ParseDerivationStyleError {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(
154 f,
155 "invalid derivation style '{}', expected one of: standard, trust, ledger-live, legacy",
156 self.0
157 )
158 }
159}
160
161#[cfg(feature = "std")]
162impl std::error::Error for ParseDerivationStyleError {}
163
164#[allow(deprecated)]
165impl FromStr for DerivationStyle {
166 type Err = ParseDerivationStyleError;
167
168 fn from_str(s: &str) -> Result<Self, Self::Err> {
169 match s.to_lowercase().as_str() {
170 "standard" | "phantom" | "backpack" | "solflare" | "trezor" => Ok(Self::Standard),
172 "trust" | "trustwallet" | "ledger" | "ledger-native" | "ledgernative" | "keystone" => {
174 Ok(Self::Trust)
175 }
176 "ledger-live" | "ledgerlive" | "live" => Ok(Self::LedgerLive),
178 "legacy" | "old" | "sollet" => Ok(Self::Legacy),
180 _ => Err(ParseDerivationStyleError(s.into())),
181 }
182 }
183}
184
185#[cfg(test)]
186#[allow(deprecated)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_standard_paths() {
192 let style = DerivationStyle::Standard;
193 assert_eq!(style.path(0), "m/44'/501'/0'/0'");
194 assert_eq!(style.path(1), "m/44'/501'/1'/0'");
195 assert_eq!(style.path(10), "m/44'/501'/10'/0'");
196 }
197
198 #[test]
199 fn test_trust_paths() {
200 let style = DerivationStyle::Trust;
201 assert_eq!(style.path(0), "m/44'/501'/0'");
202 assert_eq!(style.path(1), "m/44'/501'/1'");
203 assert_eq!(style.path(10), "m/44'/501'/10'");
204 }
205
206 #[test]
207 fn test_ledger_live_paths() {
208 let style = DerivationStyle::LedgerLive;
209 assert_eq!(style.path(0), "m/44'/501'/0'/0'/0'");
210 assert_eq!(style.path(1), "m/44'/501'/1'/0'/0'");
211 assert_eq!(style.path(10), "m/44'/501'/10'/0'/0'");
212 }
213
214 #[test]
215 fn test_legacy_paths() {
216 let style = DerivationStyle::Legacy;
217 assert_eq!(style.path(0), "m/501'/0'/0/0");
218 assert_eq!(style.path(1), "m/501'/1'/0/0");
219 assert_eq!(style.path(10), "m/501'/10'/0/0");
220 }
221
222 #[test]
223 fn test_from_str() {
224 assert_eq!(
226 "standard".parse::<DerivationStyle>().unwrap(),
227 DerivationStyle::Standard
228 );
229 assert_eq!(
230 "phantom".parse::<DerivationStyle>().unwrap(),
231 DerivationStyle::Standard
232 );
233 assert_eq!(
234 "backpack".parse::<DerivationStyle>().unwrap(),
235 DerivationStyle::Standard
236 );
237
238 assert_eq!(
240 "trust".parse::<DerivationStyle>().unwrap(),
241 DerivationStyle::Trust
242 );
243 assert_eq!(
244 "ledger".parse::<DerivationStyle>().unwrap(),
245 DerivationStyle::Trust
246 );
247 assert_eq!(
248 "keystone".parse::<DerivationStyle>().unwrap(),
249 DerivationStyle::Trust
250 );
251
252 assert_eq!(
254 "ledger-live".parse::<DerivationStyle>().unwrap(),
255 DerivationStyle::LedgerLive
256 );
257
258 assert_eq!(
260 "legacy".parse::<DerivationStyle>().unwrap(),
261 DerivationStyle::Legacy
262 );
263 }
264
265 #[test]
266 fn test_from_str_invalid() {
267 assert!("invalid".parse::<DerivationStyle>().is_err());
268 }
269
270 #[test]
271 fn test_default() {
272 assert_eq!(DerivationStyle::default(), DerivationStyle::Standard);
273 }
274}