use ed25519_dalek::{Signature, VerifyingKey};
use wallet_adapter_common::WalletCommonUtils;
use web_sys::{js_sys, wasm_bindgen::JsValue};
use core::str;
use crate::{
InnerUtils, Reflection, SemverVersion, StandardFunction, WalletAccount, WalletError,
WalletResult,
};
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SignMessage(pub(crate) StandardFunction);
impl SignMessage {
pub(crate) fn new(reflection: &Reflection, version: SemverVersion) -> WalletResult<Self> {
Ok(Self(StandardFunction::new(
reflection,
version,
"signMessage",
"solana",
)?))
}
pub(crate) async fn call_sign_message<'a>(
&self,
wallet_account: &WalletAccount,
message: &'a [u8],
) -> WalletResult<SignedMessageOutput<'a>> {
let message_value: js_sys::Uint8Array = message.into();
let mut message_object = Reflection::new_object();
message_object.set_object(&"account".into(), &wallet_account.js_value)?;
message_object.set_object(&"message".into(), &message_value)?;
let outcome = self
.0
.callback
.call1(&JsValue::null(), message_object.get_inner())?;
let outcome = js_sys::Promise::resolve(&outcome);
let signed_message_result = wasm_bindgen_futures::JsFuture::from(outcome).await?;
let incase_of_error = Err(WalletError::InternalError(format!(
"solana:signedMessage -> SignedMessageOutput: Casting `{signed_message_result:?}` did not yield a Uini8Array"
)));
let signed_message_result = Reflection::new(signed_message_result)?
.into_array()
.or(incase_of_error)?
.to_vec();
if let Some(inner) = signed_message_result.first() {
let reflect_outcome = Reflection::new(inner.clone())?;
let signed_message = reflect_outcome.reflect_inner("signedMessage")?;
let signature_value = reflect_outcome.reflect_inner("signature")?;
let incase_of_error = Err(WalletError::InternalError(format!(
"solana:signedMessage -> SignedMessageOutput::signedMessage: Cast `{signed_message:?}` did not yield a JsValue"
)));
let signed_message = Reflection::new(signed_message)?
.into_bytes()
.or(incase_of_error)?
.to_vec();
if signed_message != message {
return Err(WalletError::SignedMessageMismatch);
}
let signature = InnerUtils::jsvalue_to_signature(
signature_value,
"solana::signMessage -> SignedMessageOutput::signature",
)?;
let public_key = WalletCommonUtils::public_key(&wallet_account.account.public_key)?;
WalletCommonUtils::verify_signature(public_key, message, signature)?;
Ok(SignedMessageOutput {
message,
public_key: wallet_account.account.public_key,
signature: signature.to_bytes(),
})
} else {
Err(WalletError::ReceivedAnEmptySignedMessagesArray)
}
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
pub struct SignedMessageOutput<'a> {
message: &'a [u8],
public_key: [u8; 32],
signature: [u8; 64],
}
impl SignedMessageOutput<'_> {
pub fn message(&self) -> &str {
str::from_utf8(self.message).unwrap()
}
pub fn public_key(&self) -> WalletResult<VerifyingKey> {
Ok(WalletCommonUtils::public_key(&self.public_key)?)
}
pub fn address(&self) -> WalletResult<String> {
Ok(WalletCommonUtils::address(self.public_key()?))
}
pub fn signature(&self) -> Signature {
WalletCommonUtils::signature(&self.signature)
}
pub fn base58_signature(&self) -> WalletResult<String> {
Ok(WalletCommonUtils::base58_signature(self.signature()))
}
}
impl Default for SignedMessageOutput<'_> {
fn default() -> Self {
Self {
message: &[],
public_key: [0u8; 32],
signature: [0u8; 64],
}
}
}