wallet_adapter/wallet_ser_der/
wallet.rs

1use std::borrow::Cow;
2
3use async_channel::Receiver;
4use ed25519_dalek::Signature;
5use wallet_adapter_common::{
6    chains::ChainSupport, clusters::Cluster, signin_standard::SignInOutput, WalletData,
7};
8use web_sys::wasm_bindgen::JsValue;
9
10use crate::{
11    ConnectionInfoInner, Features, Reflection, SemverVersion, SigninInput, WalletAccount,
12    WalletError, WalletEventSender, WalletIcon, WalletResult,
13};
14
15use super::{SendOptions, SignedMessageOutput};
16
17/// A wallet implementing wallet standard
18#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
19pub struct Wallet {
20    pub(crate) data: WalletData,
21    pub(crate) accounts: Vec<WalletAccount>,
22    pub(crate) features: Features,
23}
24
25impl Wallet {
26    /// Send a request to connect to a browser wallet
27    pub async fn connect(&self) -> WalletResult<WalletAccount> {
28        self.features.connect.call_connect().await
29    }
30
31    /// Send a request to the browser wallet to disconnect
32    pub async fn disconnect(&self) -> WalletResult<()> {
33        self.features.disconnect.call_disconnect().await
34    }
35
36    /// Send a signin request to the browser wallet
37    pub async fn sign_in(
38        &self,
39        signin_input: &SigninInput,
40        public_key: [u8; 32],
41    ) -> WalletResult<SignInOutput> {
42        if let Some(fn_exists) = self.features.sign_in.as_ref() {
43            fn_exists.call_signin(signin_input, public_key).await
44        } else {
45            Err(WalletError::MissingSignInFunction)
46        }
47    }
48
49    /// Send a sign message request to the browser wallet.
50    /// Message must be UTF-8 encoded
51    pub async fn sign_message<'a>(
52        &self,
53        message: &'a [u8],
54        account: &WalletAccount,
55    ) -> WalletResult<SignedMessageOutput<'a>> {
56        self.features
57            .sign_message
58            .call_sign_message(account, message)
59            .await
60    }
61
62    /// Send a sign transaction request to the browser wallet.
63    /// The transaction bytes expected are encoded using serde in byte form.
64    pub async fn sign_transaction(
65        &self,
66        transaction_bytes: &[u8],
67        cluster: Option<Cluster>,
68        account: &WalletAccount,
69    ) -> WalletResult<Vec<Vec<u8>>> {
70        self.features
71            .sign_tx
72            .call_sign_tx(account, transaction_bytes, cluster)
73            .await
74    }
75
76    /// Send a sign and send transaction request to the browser wallet.
77    pub async fn sign_and_send_transaction(
78        &self,
79        transaction_bytes: &[u8],
80        cluster: Cluster,
81        options: SendOptions,
82        account: &WalletAccount,
83    ) -> WalletResult<Signature> {
84        self.features
85            .sign_and_send_tx
86            .call_sign_and_send_transaction(account, transaction_bytes, cluster, options)
87            .await
88    }
89
90    /// Get the standard events [Function](web_sys::js_sys::Function) `[standard:events].on`
91    pub async fn call_on_event(
92        &self,
93        connection_info: ConnectionInfoInner,
94        wallet_name: String,
95        sender: WalletEventSender,
96        signal_receiver: Receiver<()>,
97    ) -> WalletResult<()> {
98        self.features
99            .events
100            .call_on_event(connection_info, wallet_name, sender, signal_receiver)
101            .await
102    }
103
104    /// Parse the Wallet details from a [JsValue]
105    pub fn from_jsvalue(value: JsValue) -> WalletResult<Self> {
106        let reflection = Reflection::new(value)?;
107
108        let mut supported_chains = ChainSupport::default();
109
110        let chains_raw = reflection.vec_string_and_filter("chains", "solana:")?;
111        let chains = chains_raw
112            .into_iter()
113            .map(|chain_raw| {
114                let cluster: Cluster = chain_raw.as_str().into();
115                if cluster == Cluster::MainNet {
116                    supported_chains.mainnet = true;
117                } else if cluster == Cluster::DevNet {
118                    supported_chains.devnet = true;
119                } else if cluster == Cluster::TestNet {
120                    supported_chains.testnet = true;
121                } else if cluster == Cluster::LocalNet {
122                    supported_chains.localnet = true;
123                }
124
125                cluster
126            })
127            .collect::<Vec<Cluster>>();
128
129        let name = reflection.string("name")?;
130        let version = SemverVersion::parse(&reflection.string("version")?)?;
131        let icon = WalletIcon::from_jsvalue(&reflection)?;
132        let accounts = Self::get_accounts(&reflection, "accounts")?;
133        let (features, supported_features) = Features::parse(&reflection)?;
134
135        let data = WalletData::new()
136            .set_name(&name)
137            .set_version(
138                wallet_adapter_common::SemverVersion::new()
139                    .set_major(version.major())
140                    .set_minor(version.minor())
141                    .set_patch(version.patch()),
142            )
143            .set_icon(icon.as_ref())
144            .replace_accounts(
145                accounts
146                    .iter()
147                    .map(|wallet_account| wallet_account.account.clone())
148                    .collect(),
149            )
150            .replace_chains(chains)
151            .set_supported_features(supported_features)
152            .set_supported_chains(supported_chains);
153
154        Ok(Self {
155            data,
156            accounts,
157            features,
158        })
159    }
160
161    fn get_accounts(reflection: &Reflection, key: &str) -> WalletResult<Vec<WalletAccount>> {
162        let accounts_raw = reflection.reflect_inner(key)?;
163
164        let accounts_array = Reflection::new(accounts_raw)?.into_array()?;
165
166        accounts_array
167            .iter()
168            .map(|account| WalletAccount::parse(Reflection::new(account)?))
169            .collect::<WalletResult<Vec<WalletAccount>>>()
170    }
171
172    /// Get the features of the wallet
173    pub fn features(&self) -> &Features {
174        &self.features
175    }
176
177    /// Get the accounts provided by the wallet
178    pub fn accounts(&self) -> &[WalletAccount] {
179        &self.accounts
180    }
181
182    /// Get the chains supported by the wallet
183    pub fn chains(&self) -> &[Cluster] {
184        self.data.chains()
185    }
186
187    /// Check whether the wallet supports mainnet cluster
188    pub fn mainnet(&self) -> bool {
189        self.data.mainnet()
190    }
191
192    /// Check whether the wallet supports devnet cluster
193    pub fn devnet(&self) -> bool {
194        self.data.devnet()
195    }
196
197    /// Check whether the wallet supports testnet cluster
198    pub fn testnet(&self) -> bool {
199        self.data.testnet()
200    }
201
202    /// Check whether the wallet supports localnet cluster
203    pub fn localnet(&self) -> bool {
204        self.data.localnet()
205    }
206
207    /// Check whether the wallet supports `standard:connect` feature
208    pub fn standard_connect(&self) -> bool {
209        self.data.standard_connect()
210    }
211
212    /// Check whether the wallet supports `standard:disconnect` feature
213    pub fn standard_disconnect(&self) -> bool {
214        self.data.standard_disconnect()
215    }
216
217    /// Check whether the wallet supports `standard:events` feature
218    pub fn standard_events(&self) -> bool {
219        self.data.standard_events()
220    }
221
222    /// Check whether the wallet supports `solana:signIn` feature
223    pub fn solana_signin(&self) -> bool {
224        self.data.solana_signin()
225    }
226
227    /// Check whether the wallet supports `solana:signMessage` feature
228    pub fn solana_sign_message(&self) -> bool {
229        self.data.solana_sign_message()
230    }
231
232    /// Check whether the wallet supports `solana:signAndSendTransaction` feature
233    pub fn solana_sign_and_send_transaction(&self) -> bool {
234        self.data.solana_sign_and_send_transaction()
235    }
236
237    /// Check whether the wallet supports `solana:signTransaction` feature
238    pub fn solana_sign_transaction(&self) -> bool {
239        self.data.solana_sign_transaction()
240    }
241
242    /// Get the optional wallet icon
243    pub fn icon(&self) -> Option<&Cow<'static, str>> {
244        self.data.icon()
245    }
246
247    /// Get the name of the wallet
248    pub fn name(&self) -> &str {
249        self.data.name()
250    }
251
252    /// Get the version of the wallet standard that the wallet supports
253    pub fn version(&self) -> SemverVersion {
254        let version = self.data.version();
255
256        SemverVersion(version.clone())
257    }
258}