kobe_svm/
derivation_style.rs1use alloc::format;
12use alloc::string::String;
13use core::fmt;
14use core::str::FromStr;
15
16use kobe_primitives::ParseDerivationStyleError;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
29#[non_exhaustive]
30pub enum DerivationStyle {
31 #[default]
33 Standard,
34 Trust,
36 LedgerLive,
38 Legacy,
40}
41
42const ALL_STYLES: &[DerivationStyle] = &[
45 DerivationStyle::Standard,
46 DerivationStyle::Trust,
47 DerivationStyle::LedgerLive,
48 DerivationStyle::Legacy,
49];
50
51const ACCEPTED_TOKENS: &[&str] = &[
53 "standard",
54 "phantom",
55 "backpack",
56 "solflare",
57 "trezor",
58 "trust",
59 "trustwallet",
60 "ledger",
61 "ledger-native",
62 "ledgernative",
63 "keystone",
64 "ledger-live",
65 "ledgerlive",
66 "live",
67 "legacy",
68 "old",
69 "sollet",
70];
71
72impl DerivationStyle {
73 #[must_use]
79 pub const fn id(self) -> &'static str {
80 match self {
81 Self::Standard => "standard",
82 Self::Trust => "trust",
83 Self::LedgerLive => "ledger-live",
84 Self::Legacy => "legacy",
85 }
86 }
87}
88
89impl kobe_primitives::DerivationStyle for DerivationStyle {
90 fn path(self, index: u32) -> String {
91 match self {
92 Self::Standard => format!("m/44'/501'/{index}'/0'"),
93 Self::Trust => format!("m/44'/501'/{index}'"),
94 Self::LedgerLive => format!("m/44'/501'/{index}'/0'/0'"),
95 Self::Legacy => format!("m/501'/{index}'/0'/0'"),
96 }
97 }
98
99 fn name(self) -> &'static str {
100 match self {
101 Self::Standard => "Standard (Phantom/Backpack)",
102 Self::Trust => "Trust (Ledger/Keystone)",
103 Self::LedgerLive => "Ledger Live",
104 Self::Legacy => "Legacy (deprecated)",
105 }
106 }
107
108 fn all() -> &'static [Self] {
109 ALL_STYLES
110 }
111}
112
113impl fmt::Display for DerivationStyle {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 f.write_str(<Self as kobe_primitives::DerivationStyle>::name(*self))
116 }
117}
118
119impl FromStr for DerivationStyle {
120 type Err = ParseDerivationStyleError;
121
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 match s.to_lowercase().as_str() {
124 "standard" | "phantom" | "backpack" | "solflare" | "trezor" => Ok(Self::Standard),
125 "trust" | "trustwallet" | "ledger" | "ledger-native" | "ledgernative" | "keystone" => {
126 Ok(Self::Trust)
127 }
128 "ledger-live" | "ledgerlive" | "live" => Ok(Self::LedgerLive),
129 "legacy" | "old" | "sollet" => Ok(Self::Legacy),
130 _ => Err(ParseDerivationStyleError::new("solana", s, ACCEPTED_TOKENS)),
131 }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use kobe_primitives::DerivationStyle as _;
138
139 use super::*;
140
141 #[test]
142 fn test_standard_paths() {
143 let style = DerivationStyle::Standard;
144 assert_eq!(style.path(0), "m/44'/501'/0'/0'");
145 assert_eq!(style.path(1), "m/44'/501'/1'/0'");
146 assert_eq!(style.path(10), "m/44'/501'/10'/0'");
147 }
148
149 #[test]
150 fn test_trust_paths() {
151 let style = DerivationStyle::Trust;
152 assert_eq!(style.path(0), "m/44'/501'/0'");
153 assert_eq!(style.path(1), "m/44'/501'/1'");
154 assert_eq!(style.path(10), "m/44'/501'/10'");
155 }
156
157 #[test]
158 fn test_ledger_live_paths() {
159 let style = DerivationStyle::LedgerLive;
160 assert_eq!(style.path(0), "m/44'/501'/0'/0'/0'");
161 assert_eq!(style.path(1), "m/44'/501'/1'/0'/0'");
162 assert_eq!(style.path(10), "m/44'/501'/10'/0'/0'");
163 }
164
165 #[test]
166 fn test_legacy_paths() {
167 let style = DerivationStyle::Legacy;
168 assert_eq!(style.path(0), "m/501'/0'/0'/0'");
169 assert_eq!(style.path(1), "m/501'/1'/0'/0'");
170 assert_eq!(style.path(10), "m/501'/10'/0'/0'");
171 }
172
173 #[test]
174 fn test_from_str() {
175 assert_eq!(
177 "standard".parse::<DerivationStyle>().unwrap(),
178 DerivationStyle::Standard
179 );
180 assert_eq!(
181 "phantom".parse::<DerivationStyle>().unwrap(),
182 DerivationStyle::Standard
183 );
184 assert_eq!(
185 "backpack".parse::<DerivationStyle>().unwrap(),
186 DerivationStyle::Standard
187 );
188
189 assert_eq!(
191 "trust".parse::<DerivationStyle>().unwrap(),
192 DerivationStyle::Trust
193 );
194 assert_eq!(
195 "ledger".parse::<DerivationStyle>().unwrap(),
196 DerivationStyle::Trust
197 );
198 assert_eq!(
199 "keystone".parse::<DerivationStyle>().unwrap(),
200 DerivationStyle::Trust
201 );
202
203 assert_eq!(
205 "ledger-live".parse::<DerivationStyle>().unwrap(),
206 DerivationStyle::LedgerLive
207 );
208
209 assert_eq!(
211 "legacy".parse::<DerivationStyle>().unwrap(),
212 DerivationStyle::Legacy
213 );
214 }
215
216 #[test]
217 fn test_from_str_invalid() {
218 assert!("invalid".parse::<DerivationStyle>().is_err());
219 }
220
221 #[test]
222 fn test_default() {
223 assert_eq!(DerivationStyle::default(), DerivationStyle::Standard);
224 }
225}