wallet_adapter/wallet_ser_der/standard_features/
sign_message.rs1use ed25519_dalek::{Signature, VerifyingKey};
2use wallet_adapter_common::WalletCommonUtils;
3use web_sys::{js_sys, wasm_bindgen::JsValue};
4
5use core::str;
6
7use crate::{
8 InnerUtils, Reflection, SemverVersion, StandardFunction, WalletAccount, WalletError,
9 WalletResult,
10};
11
12#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct SignMessage(pub(crate) StandardFunction);
16
17impl SignMessage {
18 pub(crate) fn new(reflection: &Reflection, version: SemverVersion) -> WalletResult<Self> {
20 Ok(Self(StandardFunction::new(
21 reflection,
22 version,
23 "signMessage",
24 "solana",
25 )?))
26 }
27
28 pub(crate) async fn call_sign_message<'a>(
30 &self,
31 wallet_account: &WalletAccount,
32 message: &'a [u8],
33 ) -> WalletResult<SignedMessageOutput<'a>> {
34 let message_value: js_sys::Uint8Array = message.into();
35
36 let mut message_object = Reflection::new_object();
37 message_object.set_object(&"account".into(), &wallet_account.js_value)?;
38 message_object.set_object(&"message".into(), &message_value)?;
39
40 let outcome = self
42 .0
43 .callback
44 .call1(&JsValue::null(), message_object.get_inner())?;
45
46 let outcome = js_sys::Promise::resolve(&outcome);
47 let signed_message_result = wasm_bindgen_futures::JsFuture::from(outcome).await?;
48 let incase_of_error = Err(WalletError::InternalError(format!(
49 "solana:signedMessage -> SignedMessageOutput: Casting `{signed_message_result:?}` did not yield a Uini8Array"
50 )));
51
52 let signed_message_result = Reflection::new(signed_message_result)?
53 .into_array()
54 .or(incase_of_error)?
55 .to_vec();
56
57 if let Some(inner) = signed_message_result.first() {
58 let reflect_outcome = Reflection::new(inner.clone())?;
59 let signed_message = reflect_outcome.reflect_inner("signedMessage")?;
60 let signature_value = reflect_outcome.reflect_inner("signature")?;
61
62 let incase_of_error = Err(WalletError::InternalError(format!(
63 "solana:signedMessage -> SignedMessageOutput::signedMessage: Cast `{signed_message:?}` did not yield a JsValue"
64 )));
65
66 let signed_message = Reflection::new(signed_message)?
67 .into_bytes()
68 .or(incase_of_error)?
69 .to_vec();
70
71 if signed_message != message {
72 return Err(WalletError::SignedMessageMismatch);
73 }
74
75 let signature = InnerUtils::jsvalue_to_signature(
76 signature_value,
77 "solana::signMessage -> SignedMessageOutput::signature",
78 )?;
79
80 let public_key = WalletCommonUtils::public_key(&wallet_account.account.public_key)?;
81
82 WalletCommonUtils::verify_signature(public_key, message, signature)?;
83
84 Ok(SignedMessageOutput {
85 message,
86 public_key: wallet_account.account.public_key,
87 signature: signature.to_bytes(),
88 })
89 } else {
90 Err(WalletError::ReceivedAnEmptySignedMessagesArray)
91 }
92 }
93}
94
95#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
97pub struct SignedMessageOutput<'a> {
98 message: &'a [u8],
99 public_key: [u8; 32],
100 signature: [u8; 64],
101}
102
103impl SignedMessageOutput<'_> {
104 pub fn message(&self) -> &str {
106 str::from_utf8(self.message).unwrap()
109 }
110
111 pub fn public_key(&self) -> WalletResult<VerifyingKey> {
113 Ok(WalletCommonUtils::public_key(&self.public_key)?)
114 }
115
116 pub fn address(&self) -> WalletResult<String> {
118 Ok(WalletCommonUtils::address(self.public_key()?))
119 }
120
121 pub fn signature(&self) -> Signature {
124 WalletCommonUtils::signature(&self.signature)
125 }
126
127 pub fn base58_signature(&self) -> WalletResult<String> {
129 Ok(WalletCommonUtils::base58_signature(self.signature()))
130 }
131}
132
133impl Default for SignedMessageOutput<'_> {
134 fn default() -> Self {
135 Self {
136 message: &[],
137 public_key: [0u8; 32],
138 signature: [0u8; 64],
139 }
140 }
141}