wallet_adapter/wallet_ser_der/
wallet_account.rs1use std::borrow::Cow;
2
3use wallet_adapter_common::{
4 chains::ChainSupport,
5 clusters::Cluster,
6 feature_support::FeatureSupport,
7 standardized_events::{
8 SOLANA_SIGN_AND_SEND_TRANSACTION_IDENTIFIER, SOLANA_SIGN_IN_IDENTIFIER,
9 SOLANA_SIGN_MESSAGE_IDENTIFIER, SOLANA_SIGN_TRANSACTION_IDENTIFIER,
10 STANDARD_CONNECT_IDENTIFIER, STANDARD_DISCONNECT_IDENTIFIER, STANDARD_EVENTS_IDENTIFIER,
11 },
12 WalletAccountData, WalletCommonUtils,
13};
14use web_sys::wasm_bindgen::JsValue;
15
16use crate::{Reflection, WalletError, WalletIcon, WalletResult};
17
18#[derive(Clone, Default, PartialEq)]
24pub struct WalletAccount {
25 pub(crate) account: WalletAccountData,
26 pub(crate) js_value: JsValue,
29}
30
31impl WalletAccount {
32 pub fn address(&self) -> &str {
34 self.account.address.as_str()
35 }
36
37 pub fn public_key(&self) -> [u8; 32] {
39 self.account.public_key
40 }
41
42 pub fn chains(&self) -> &[String] {
45 self.account.chains.as_slice()
46 }
47
48 pub fn features(&self) -> &[String] {
51 self.account.features.as_slice()
52 }
53
54 pub fn label(&self) -> Option<&String> {
56 self.account.label.as_ref()
57 }
58
59 pub fn icon(&self) -> Option<&String> {
61 self.account.icon.as_ref()
62 }
63
64 pub fn shorten_address<'a>(&'a self) -> WalletResult<Cow<'a, str>> {
69 Ok(WalletCommonUtils::shorten_base58(&self.account.address)?)
70 }
71
72 pub fn custom_shorten_address<'a>(&'a self, take: usize) -> WalletResult<Cow<'a, str>> {
76 Ok(WalletCommonUtils::custom_shorten_base58(
77 &self.account.address,
78 take,
79 )?)
80 }
81
82 pub fn custom_shorten_address_rl<'a>(
86 &'a self,
87 left: usize,
88 right: usize,
89 ) -> WalletResult<Cow<'a, str>> {
90 Ok(WalletCommonUtils::custom_shorten_address_rl(
91 self.account.address(),
92 left,
93 right,
94 )?)
95 }
96
97 pub(crate) fn parse(reflection: Reflection) -> WalletResult<Self> {
99 let address = reflection.string("address")?;
100 let public_key = reflection.byte32array("publicKey")?;
101 let chains = reflection.vec_string_accept_undefined("chains")?;
102 let features = reflection.vec_string_accept_undefined("features")?;
103
104 let mut supported_chains = ChainSupport::default();
105
106 chains.iter().try_for_each(|chain| {
107 if chain.as_str() == Cluster::MainNet.chain() {
108 supported_chains.mainnet = true;
109 } else if chain.as_str() == Cluster::DevNet.chain() {
110 supported_chains.devnet = true;
111 } else if chain.as_str() == Cluster::TestNet.chain() {
112 supported_chains.testnet = true;
113 } else if chain.as_str() == Cluster::LocalNet.chain() {
114 supported_chains.localnet = true;
115 } else {
116 return Err(WalletError::UnsupportedChain(chain.to_owned()));
117 }
118
119 Ok(())
120 })?;
121
122 let mut supported_features = FeatureSupport::default();
123
124 features.iter().try_for_each(|feature| {
125 if feature.as_str() == STANDARD_CONNECT_IDENTIFIER {
126 supported_features.connect = true;
127 } else if feature.as_str() == STANDARD_DISCONNECT_IDENTIFIER {
128 supported_features.disconnect = true;
129 } else if feature.as_str() == STANDARD_EVENTS_IDENTIFIER {
130 supported_features.events = true;
131 } else if feature.as_str() == SOLANA_SIGN_IN_IDENTIFIER {
132 supported_features.sign_in = true;
133 } else if feature.as_str() == SOLANA_SIGN_AND_SEND_TRANSACTION_IDENTIFIER {
134 supported_features.sign_and_send_tx = true;
135 } else if feature.as_str() == SOLANA_SIGN_TRANSACTION_IDENTIFIER {
136 supported_features.sign_tx = true;
137 } else if feature.as_str() == SOLANA_SIGN_MESSAGE_IDENTIFIER {
138 supported_features.sign_message = true;
139 } else {
140 return Err(WalletError::UnsupportedWalletFeature(feature.to_owned()));
141 }
142
143 Ok(())
144 })?;
145
146 let icon = WalletIcon::from_jsvalue(&reflection)?;
147
148 let label = match reflection.string("label") {
149 Ok(value) => Some(value),
150 Err(error) => match error {
151 WalletError::InternalError(_) => Option::None,
152 _ => {
153 return Err(error);
154 }
155 },
156 };
157
158 let account = WalletAccountData {
159 address,
160 public_key,
161 chains,
162 features,
163 label,
164 icon,
165 supported_chains,
166 supported_features,
167 };
168
169 Ok(Self {
170 account,
171 js_value: reflection.take(),
172 })
173 }
174
175 pub fn mainnet(&self) -> bool {
177 self.account.supported_chains.mainnet
178 }
179
180 pub fn devnet(&self) -> bool {
182 self.account.supported_chains.devnet
183 }
184
185 pub fn testnet(&self) -> bool {
187 self.account.supported_chains.testnet
188 }
189
190 pub fn localnet(&self) -> bool {
192 self.account.supported_chains.localnet
193 }
194
195 pub fn standard_connect(&self) -> bool {
197 self.account.supported_features.connect
198 }
199
200 pub fn standard_disconnect(&self) -> bool {
202 self.account.supported_features.disconnect
203 }
204
205 pub fn standard_events(&self) -> bool {
207 self.account.supported_features.events
208 }
209
210 pub fn solana_signin(&self) -> bool {
212 self.account.supported_features.sign_in
213 }
214
215 pub fn solana_sign_message(&self) -> bool {
217 self.account.supported_features.sign_message
218 }
219
220 pub fn solana_sign_and_send_transaction(&self) -> bool {
222 self.account.supported_features.sign_and_send_tx
223 }
224
225 pub fn solana_sign_transaction(&self) -> bool {
227 self.account.supported_features.sign_tx
228 }
229}
230
231impl core::fmt::Debug for WalletAccount {
232 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233 f.debug_struct("WalletAccount")
234 .field("address", &self.account.address)
235 .field("public_key", &self.account.public_key)
236 .field("chains", &self.account.chains)
237 .field("features", &self.account.features)
238 .field("label", &self.account.label)
239 .field("icon", &self.account.icon)
240 .finish()
241 }
242}
243
244impl PartialOrd for WalletAccount {
245 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
246 Some(self.cmp(other))
247 }
248}
249
250impl Ord for WalletAccount {
251 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
252 let inner_self: InnerWalletAccount = self.into();
253 let inner_other: InnerWalletAccount = other.into();
254
255 inner_self.cmp(&inner_other)
256 }
257}
258
259impl core::cmp::Eq for WalletAccount {}
260
261impl core::hash::Hash for WalletAccount {
262 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
263 let inner_self: InnerWalletAccount = self.into();
264
265 inner_self.hash(state);
266 }
267}
268
269#[derive(Eq, PartialEq, PartialOrd, Ord, Hash)]
271struct InnerWalletAccount<'a> {
272 pub address: &'a str,
273 pub public_key: &'a [u8; 32],
274 pub chains: &'a [String],
275 pub features: &'a [String],
276 pub label: Option<&'a String>,
277 pub icon: Option<&'a String>,
278}
279
280impl<'a> From<&'a WalletAccount> for InnerWalletAccount<'a> {
281 fn from(value: &'a WalletAccount) -> Self {
282 Self {
283 address: value.account.address.as_str(),
284 public_key: &value.account.public_key,
285 chains: value.account.chains.as_slice(),
286 features: &value.account.features,
287 label: value.account.label.as_ref(),
288 icon: value.account.icon.as_ref(),
289 }
290 }
291}