From f40851411c5780f7e5bdbf1c4f8f3e9ea836eed3 Mon Sep 17 00:00:00 2001 From: 448 OG Date: Mon, 8 Sep 2025 19:27:54 +0300 Subject: [PATCH] Finish sign in with solana Complete the necessary data structures and methods required to complete SIWS. This lays the ground work for generic SIW code. --- wallet-extension/Cargo.toml | 3 +- wallet-extension/extension/js/background.js | 34 -- wallet-extension/extension/js/content.js | 57 ++- wallet-extension/src/errors.rs | 4 + wallet-extension/src/impl_solana/clusters.rs | 25 +- .../src/impl_solana/interface/mod.rs | 1 + .../src/impl_solana/interface/sign_in.rs | 397 ++++++++++++++++++ .../src/impl_solana/reflection.rs | 17 +- wallet-extension/src/impl_solana/wallet.rs | 12 +- wallet-extension/src/message_handler.rs | 3 + 10 files changed, 499 insertions(+), 54 deletions(-) create mode 100644 wallet-extension/src/impl_solana/interface/sign_in.rs diff --git a/wallet-extension/Cargo.toml b/wallet-extension/Cargo.toml index 4041d1c..a1856e7 100644 --- a/wallet-extension/Cargo.toml +++ b/wallet-extension/Cargo.toml @@ -13,7 +13,7 @@ bs58 = { version = "0.5.1", default-features = false } console_error_panic_hook = "0.1.7" getrandom = { version = "0.3.3", features = ["wasm_js"] } thiserror = "2.0.16" -wallet-standard-base = { version = "0.1.4", features = ["getrandom"] } +wallet-standard-base = { version = "0.1.7", features = ["getrandom"] } bincode = "1" solana-keypair = { version = "2", default-features = false, features = [ "seed-derivable", @@ -42,3 +42,4 @@ web-sys = { workspace = true, features = [ ] } zeroize = "1.8.1" blake3 = { version = "1.8.2", default-features = false } +humantime = "2.2.0" diff --git a/wallet-extension/extension/js/background.js b/wallet-extension/extension/js/background.js index b3a4867..487a6ee 100644 --- a/wallet-extension/extension/js/background.js +++ b/wallet-extension/extension/js/background.js @@ -1,37 +1,3 @@ -// const walletAccount = { -// address: "BtRL4ydcfGzRRZBUosHh6aQBg9DzLdNN6Nybhce6fqF3", -// publicKey: new Uint8Array([ -// 161, 192, 255, 200, 114, 43, 197, 135, 210, 81, 184, 46, 253, 220, 248, 75, -// 127, 99, 22, 137, 190, 13, 51, 164, 25, 226, 205, 215, 91, 235, 218, 14, -// ]), -// chains: ["solana:mainnet", "solana:devnet", "solana:testnet"], -// features: [ -// "standard:connect", -// "standard:disconnect", -// "standard:events", -// "solana:signIn", -// "solana:signAndSendTransaction", -// "solana:signTransaction", -// "solana:signMessage", -// ], -// icon: undefined, -// label: undefined, -// }; - -// (async () => { -// const extension = typeof browser !== "undefined" ? browser : chrome; - -// extension.runtime.onMessage.addListener((message, sender, sendResponse) => { -// console.log("Got message in background:", message); - -// setTimeout(() => { -// sendResponse(walletAccount); -// }, 500); - -// return true; // required since response is async -// }); -// })(); - (async () => { const extension = typeof browser !== "undefined" ? browser : chrome; diff --git a/wallet-extension/extension/js/content.js b/wallet-extension/extension/js/content.js index faea99d..3a037ff 100644 --- a/wallet-extension/extension/js/content.js +++ b/wallet-extension/extension/js/content.js @@ -10,10 +10,16 @@ const STANDARD_CONNECT = "standard:connect"; const RELAY_STANDARD_CONNECT = "relay:standard:connect"; + const RELAY_SOLANA_SIGN_IN = "relay:solana:signIn"; + const SOLANA_SIGN_IN = "solana:signIn"; + function injectPageWallet(walletInfo) { const STANDARD_CONNECT = "standard:connect"; const RELAY_STANDARD_CONNECT = "relay:standard:connect"; + const RELAY_SOLANA_SIGN_IN = "relay:solana:signIn"; + const SOLANA_SIGN_IN = "solana:signIn"; + const WALLET_REGISTER_EVENT = "wallet-standard:register-wallet"; const APP_READY_EVENT = "wallet-standard:app-ready"; @@ -170,14 +176,26 @@ ]; }; #signIn = async (...inputs) => { - console.log("SIGN IN", ...inputs); - return [ - { - account: walletAccount, - signedMessage: new Uint8Array([13, 14, 15]), - signature: new Uint8Array([16, 17, 18]), - }, - ]; + const result = await new Promise((resolve, reject) => { + const listener = (event) => { + if (event.source !== window) return; + if (event.data.type === RELAY_SOLANA_SIGN_IN) { + window.removeEventListener("message", listener); + + if (event.data.failure) reject(new Error(event.data.failure)); + else resolve(event.data.success); + } + }; + window.addEventListener("message", listener); + + // Send request → content.js + window.postMessage( + { type: SOLANA_SIGN_IN, requestData: inputs[0], text: "" }, + "*" + ); + }); + + return result; }; } @@ -222,4 +240,27 @@ ); } }); + + window.addEventListener("message", (event) => { + console.log("SignIn event:", event); + + if (event.source !== window) return; + if (event.data.type === SOLANA_SIGN_IN) { + extension.runtime.sendMessage( + { resource: event.data.type, data: event.data }, + (response) => { + console.log("Content script got response from background:", response); + // Relay back under RELAY_STANDARD_CONNECT + window.postMessage( + { + type: RELAY_SOLANA_SIGN_IN, + success: response.success, + failure: response.failure, + }, + "*" + ); + } + ); + } + }); })(); diff --git a/wallet-extension/src/errors.rs b/wallet-extension/src/errors.rs index 7000226..471a939 100644 --- a/wallet-extension/src/errors.rs +++ b/wallet-extension/src/errors.rs @@ -21,6 +21,8 @@ pub enum AtollWalletError { ExtensionRuntimeMessageAddListenerIsMissing, #[error("{0}")] JsCast(String), + #[error("{0}")] + Input(String), #[error("The message `{0}` from `extension.runtime.onMessage.addListener` is not supported")] UnsupportedExtensionMessage(String), #[error( @@ -35,6 +37,8 @@ pub enum AtollWalletError { UnableToRecoverSolanaKeypairFromMnemonic, #[error("A request was made to authorize a dapp but a keypair doesn't exist yet")] UnauthorizedKeypairRequest, + #[error("The `{0}` timestamp is not a valid ISO8601 timestamp.")] + InvalidIS08601Timestamp(String), } #[derive(Debug, PartialEq)] diff --git a/wallet-extension/src/impl_solana/clusters.rs b/wallet-extension/src/impl_solana/clusters.rs index 7fc7f2e..205fc50 100644 --- a/wallet-extension/src/impl_solana/clusters.rs +++ b/wallet-extension/src/impl_solana/clusters.rs @@ -37,7 +37,7 @@ impl Cluster for SolanaCluster { fn endpoint(&self) -> &str { match self { Self::Mainnet => SolanaConstants::MAINNET_ENDPOINT, - Self::Testnet => SolanaConstants::TESTNET_IDENTIFIER, + Self::Testnet => SolanaConstants::TESTNET_ENDPOINT, Self::Devnet => SolanaConstants::DEVNET_ENDPOINT, Self::Localnet => SolanaConstants::LOCALNET_ENDPOINT, } @@ -52,3 +52,26 @@ impl Cluster for SolanaCluster { ] } } + +impl From<&str> for SolanaCluster { + fn from(value: &str) -> Self { + match value { + SolanaConstants::MAINNET_IDENTIFIER => Self::Mainnet, + SolanaConstants::TESTNET_IDENTIFIER => Self::Testnet, + SolanaConstants::DEVNET_IDENTIFIER => Self::Devnet, + SolanaConstants::LOCALNET_IDENTIFIER => Self::Localnet, + + SolanaConstants::MAINNET_CHAIN => Self::Mainnet, + SolanaConstants::TESTNET_CHAIN => Self::Testnet, + SolanaConstants::DEVNET_CHAIN => Self::Devnet, + SolanaConstants::LOCALNET_CHAIN => Self::Localnet, + + SolanaConstants::MAINNET_ENDPOINT => Self::Mainnet, + SolanaConstants::TESTNET_ENDPOINT => Self::Testnet, + SolanaConstants::DEVNET_ENDPOINT => Self::Devnet, + SolanaConstants::LOCALNET_ENDPOINT => Self::Localnet, + + _ => Self::Devnet, + } + } +} diff --git a/wallet-extension/src/impl_solana/interface/mod.rs b/wallet-extension/src/impl_solana/interface/mod.rs index 68e821a..654fc13 100644 --- a/wallet-extension/src/impl_solana/interface/mod.rs +++ b/wallet-extension/src/impl_solana/interface/mod.rs @@ -1 +1,2 @@ +mod sign_in; mod standard_connect; diff --git a/wallet-extension/src/impl_solana/interface/sign_in.rs b/wallet-extension/src/impl_solana/interface/sign_in.rs new file mode 100644 index 0000000..db8363b --- /dev/null +++ b/wallet-extension/src/impl_solana/interface/sign_in.rs @@ -0,0 +1,397 @@ +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use wallet_standard_base::SignInInput; +use wasm_bindgen::JsValue; +use web_sys::js_sys::{self, Array, Uint8Array}; + +use crate::{ + App, AtollWalletError, AtollWalletResult, Reflection, SolanaCluster, SolanaConstants, + app_console_log, +}; + +impl App { + pub fn solana_sign_in(&mut self, data: JsValue) -> AtollWalletResult { + let data = Reflection::new_object_from_js_value(data)?; + let data = data.get_object( + "requestData", + AtollWalletError::JsCast("`requestData` not found in `message` object".to_string()), + )?; + + app_console_log(SolanaConstants::SIGN_IN, &data); + + let mut input = SignInInputParser::new(); + + let formatted_input = input.parse(data)?.format(); + + let (wallet_account, signature) = self.active_keypair()?.sign_in(&formatted_input); + + let sign_in_output = Reflection::new_object(); + + sign_in_output.set_object_secure("account", &wallet_account.to_js_value_object()); + + let signed_message = Uint8Array::new_from_slice(formatted_input.as_bytes()); + sign_in_output.set_object_secure("signedMessage", &signed_message); + + let signature = Uint8Array::new_from_slice(&signature); + sign_in_output.set_object_secure("signature", &signature); + + sign_in_output.set_object_secure("signatureType", &"ed25519".into()); + + let output_array = Array::new(); + output_array.push(&sign_in_output.take()); + + Ok(output_array.into()) + } +} + +#[derive(Debug)] +struct SignInInputParser<'wa>(SignInInput<'wa>); + +impl<'wa> SignInInputParser<'wa> { + pub fn new() -> Self { + Self(SignInInput::new()) + } + + pub fn parse(&'wa mut self, sign_in_input: JsValue) -> AtollWalletResult<&'wa mut Self> { + let reflection = Reflection::new_object_from_js_value(sign_in_input)?; + + self.domain(&reflection)? + .address(&reflection)? + .statement(&reflection)? + .uri(&reflection)? + .version(&reflection)? + .chain_id(&reflection)? + .nonce(&reflection)? + .issued_at(&reflection)? + .expiration_time(&reflection)? + .not_before(&reflection)? + .request_id(&reflection)? + .resources(&reflection) + } + + /* + ${domain} wants you to sign in with your Solana account: + ${address} + + ${statement} + + URI: ${uri} + Version: ${version} + Chain ID: ${chain-id} + Nonce: ${nonce} + Issued At: ${issued-at} + Expiration Time: ${expiration-time} + Not Before: ${not-before} + Request ID: ${request-id} + Resources: + - ${resources[0]} + - ${resources[1]} + ... + - ${resources[n]} + */ + pub fn format(&'wa self) -> String { + let mut formatted = String::default(); + + if let Some(domain) = self.0.domain().as_ref() { + formatted.push_str(domain); + formatted.push_str(" wants you to sign in with your Solana account: \n"); + } + if let Some(address) = self.0.address().as_ref() { + formatted.push_str(address); + formatted.push_str("\n\n"); + } + if let Some(statement) = self.0.statement().as_ref() { + formatted.push_str(statement); + formatted.push_str("\n\n"); + } + if let Some(uri) = self.0.uri().as_ref() { + formatted.push_str("URI: "); + formatted.push_str(uri); + formatted.push('\n'); + } + if let Some(version) = self.0.version().as_ref() { + formatted.push_str("Version: "); + formatted.push_str(version); + formatted.push('\n'); + } + if let Some(chain_id) = self.0.chain_id().as_ref() { + formatted.push_str("Chain ID: "); + formatted.push_str(chain_id); + formatted.push('\n'); + } + if let Some(nonce) = self.0.nonce().as_ref() { + formatted.push_str("Nonce: "); + formatted.push_str(nonce); + formatted.push('\n'); + } + if let Some(issued_at) = self.0.issued_at().as_ref() { + formatted.push_str("Issued At: "); + formatted.push_str(issued_at); + formatted.push('\n'); + } + if let Some(expiration_time) = self.0.expiration_time().as_ref() { + formatted.push_str("Expiration Time: "); + formatted.push_str(expiration_time); + formatted.push('\n'); + }; + if let Some(not_before) = self.0.not_before().as_ref() { + formatted.push_str("Not Before: "); + formatted.push_str(not_before); + formatted.push('\n'); + } + + if let Some(request_id) = self.0.request_id().as_ref() { + formatted.push_str("Request ID: "); + formatted.push_str(request_id); + formatted.push('\n'); + } + + if !self.0.resources().as_ref().is_empty() { + formatted.push_str("Resources:\n"); + self.0.resources().iter().for_each(|resource| { + formatted.push_str("- "); + formatted.push_str(resource); + formatted.push('\n'); + }); + } + + formatted + } + + // TODO + // pub fn checks(&self) -> AtollWalletResult<()> { + // self.outcome + // .statement() + // .map(|value| { + // if value.contains("\n") { + // Err(AtollWalletError::Input("The `statement` value in `SignInInput` for Sign In With should not have any line breaks".to_string())) + // } else { + // Ok(value) + // } + // }) + // .transpose()?; + + // self.outcome + // .nonce() + // .map(|value| { + // if value.len() < 8 { + // Err(AtollWalletError::Input("The `nonce` value in `SignInInput` for Sign In With should be 8 or more characters".to_string())) + // } else { + // Ok(value) + // } + // }) + // .transpose()?; + + // self.outcome + // .issued_at() + // .map(|value| { + // let system_time_issued = humantime::parse_rfc3339(value).or(Err( + + // )); + + // if value.len() < 8 { + // Err(AtollWalletError::Input("The `nonce` value in `SignInInput` for Sign In With should be 8 or more characters".to_string())) + // } else { + // Ok(value) + // } + // }) + // .transpose()?; + + // Ok(()) + // } + + /// Fetches the time from [JavaScript Date Now](js_sys::Date::now()) . + /// This is converted to [SystemTime] + pub fn time_now() -> AtollWalletResult { + let date_now = js_sys::Date::now() as u64; + + UNIX_EPOCH + .checked_add(Duration::from_millis(date_now)) + .ok_or(AtollWalletError::Input( + "Invalid addition of time. UNIX_EPOCH.checked_add(js_sys::Date::now()".to_string(), + )) + } + + pub fn domain(&'wa mut self, reflection: &Reflection) -> AtollWalletResult<&'wa mut Self> { + if let Some(domain_value) = reflection.reflect_string_or_undefined("domain") { + self.0.set_domain(domain_value.as_str().trim()); + } + + Ok(self) + } + + pub fn address(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(address) = reflection.reflect_string_or_undefined("address") { + self.0 + .set_address(address.as_str().trim()) + .map_err(|error| AtollWalletError::Input(error.to_string()))?; + } + + Ok(self) + } + + pub fn statement(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(statement) = reflection.reflect_string_or_undefined("statement") { + self.0.set_statement(statement.as_str().trim()); + } + + Ok(self) + } + + pub fn uri(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(uri) = reflection.reflect_string_or_undefined("uri") { + self.0.set_uri(uri.as_str().trim()); + } + + Ok(self) + } + + pub fn version(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(version) = reflection.reflect_string_or_undefined("version") { + self.0.set_version(version.as_str().trim()); + } + + Ok(self) + } + + pub fn chain_id(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(chain_id) = reflection.reflect_string_or_undefined("chainId") { + let cluster: SolanaCluster = chain_id.as_str().trim().into(); + + self.0.set_chain_id(cluster); + } + + Ok(self) + } + + pub fn nonce(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(nonce) = reflection.reflect_string_or_undefined("nonce") { + self.0 + .set_custom_nonce(nonce.as_str().trim()) + .map_err(|error| AtollWalletError::Input(error.to_string()))?; + } + + Ok(self) + } + + pub fn issued_at(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(issued_at) = reflection.reflect_string_or_undefined("issuedAt") { + let issued_at = humantime::parse_rfc3339(&issued_at) + .or(Err(AtollWalletError::InvalidIS08601Timestamp(issued_at)))?; + self.0.set_issued_at(issued_at); + } + + Ok(self) + } + + pub fn expiration_time(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(expiration_time) = reflection.reflect_string_or_undefined("expirationTime") { + let expiration_time = humantime::parse_rfc3339(&expiration_time).or(Err( + AtollWalletError::InvalidIS08601Timestamp(expiration_time), + ))?; + self.0 + .set_expiration_time(Self::time_now()?, expiration_time) + .map_err(|error| AtollWalletError::Input(error.to_string()))?; + } + + Ok(self) + } + + pub fn not_before(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(not_before) = reflection.reflect_string_or_undefined("notBefore") { + let not_before = humantime::parse_rfc3339(¬_before) + .or(Err(AtollWalletError::InvalidIS08601Timestamp(not_before)))?; + self.0 + .set_expiration_time(Self::time_now()?, not_before) + .map_err(|error| AtollWalletError::Input(error.to_string()))?; + } + + Ok(self) + } + + pub fn request_id(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + if let Some(request_id) = reflection.reflect_string_or_undefined("requestId") { + self.0.set_request_id(request_id.trim()); + } + + Ok(self) + } + + pub fn resources(&mut self, reflection: &Reflection) -> AtollWalletResult<&mut Self> { + let resource_raw = reflection + .reflect_string_or_undefined("resources") + .unwrap_or_default(); + + if !resource_raw.is_empty() { + let mut resources = Vec::<&str>::default(); + + resource_raw + .lines() + .for_each(|line| resources.push(line.trim())); + + self.0.add_resources(&resources); + } + + Ok(self) + } +} + +/* +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SignInInput<'wa> { + /// Optional EIP-4361 domain requesting the sign-in. + /// If not provided, the wallet must determine the domain to include in the message. + domain: Option>, + /// Optional Solana Base58 address performing the sign-in. + /// The address is case-sensitive. + /// If not provided, the wallet must determine the Address to include in the message. + address: Option>, + /// Optional EIP-4361 Statement. + /// The statement is a human readable string and should not have new-line characters (\n). + /// If not provided, the wallet does not include Statement in the message. + statement: Option>, + /// Optional EIP-4361 URI. + /// The URL that is requesting the sign-in. + /// If not provided, the wallet does not include URI in the message. + uri: Option>, + /// Optional EIP-4361 version. + /// If not provided, the wallet does not include Version in the message. + version: Option>, + /// Optional EIP-4361 Chain ID. + /// The chainId can be one of the following: + /// mainnet, testnet, devnet, localnet, solana:mainnet, solana:testnet, solana:devnet. + /// If not provided, the wallet does not include Chain ID in the message. + chain_id: Option>, + /// Optional EIP-4361 Nonce. + /// It should be an alphanumeric string containing a minimum of 8 characters. + /// If not provided, the wallet does not include Nonce in the message. + nonce: Option>, + /// Optional ISO 8601 datetime string. + /// This represents the time at which the sign-in request was issued to the wallet. + /// Note: For Phantom, issuedAt has a threshold and it should be + /// within +- 10 minutes from the timestamp at which verification is taking place. + /// If not provided, the wallet does not include Issued At in the message. + issued_at: Option, + /// Optional ISO 8601 datetime string. + /// This represents the time at which the sign-in request should expire. + /// If not provided, the wallet does not include Expiration Time in the message. + expiration_time: Option, + /// Optional ISO 8601 datetime string. + /// This represents the time at which the sign-in request becomes valid. + /// If not provided, the wallet does not include Not Before in the message. + not_before: Option, + /// Optional EIP-4361 Request ID. + /// In addition to using nonce to avoid replay attacks, + /// dapps can also choose to include a unique signature in the requestId . + /// Once the wallet returns the signed message, + /// dapps can then verify this signature against the state to add an additional, + /// strong layer of security. If not provided, the wallet does not include Request ID in the message. + request_id: Option>, + /// Optional EIP-4361 Resources. + /// Usually a list of references in the form of URIs that the + /// dapp wants the user to be aware of. + /// These URIs should be separated by \n-, ie, + /// URIs in new lines starting with the character -. + /// If not provided, the wallet does not include Resources in the message. + resources: Cow<'wa, [&'wa str]>, +} */ diff --git a/wallet-extension/src/impl_solana/reflection.rs b/wallet-extension/src/impl_solana/reflection.rs index bbad196..5554430 100644 --- a/wallet-extension/src/impl_solana/reflection.rs +++ b/wallet-extension/src/impl_solana/reflection.rs @@ -1,10 +1,3 @@ -// Reflect::set( -// &wallet_account_object, -// &"address".into(), -// &self.address.as_ref().into(), -// ) -// .or(Err(AtollWalletError::UnableToSetWalletAccountAddress))?; - use core::panic; use wasm_bindgen::{JsCast, JsValue}; @@ -109,6 +102,10 @@ impl Reflection { Reflect::get(&self.0, &key.into()).or(Err(error)) } + pub fn get_object_or_undefined(&self, key: &str) -> Option { + Reflect::get(&self.0, &key.into()).ok() + } + pub fn get_object_secure(&self, key: &str) -> JsValue { match Reflect::get(&self.0, &key.into()) { Ok(value) => value, @@ -128,6 +125,12 @@ impl Reflection { } } + pub fn reflect_string_or_undefined(&self, key: &str) -> Option { + let js_value_string = self.get_object_or_undefined(key); + + js_value_string.map(|value| value.as_string())? + } + pub fn take(self) -> JsValue { self.0 } diff --git a/wallet-extension/src/impl_solana/wallet.rs b/wallet-extension/src/impl_solana/wallet.rs index 3b50160..2b9f8dd 100644 --- a/wallet-extension/src/impl_solana/wallet.rs +++ b/wallet-extension/src/impl_solana/wallet.rs @@ -20,7 +20,7 @@ pub struct SolanaAccountKeypair { active_dapps: HashMap, } -impl SolanaAccountKeypair { +impl<'wa> SolanaAccountKeypair { pub(crate) fn new_test() -> AtollWalletResult { Self::new_from_mnemonic( Zeroizing::new(TEST_MNEMONIC.to_string()), @@ -74,7 +74,7 @@ impl SolanaAccountKeypair { self.keypair.pubkey() } - pub fn standard_connect<'wa>(&'wa mut self, uri: String) -> SolanaWalletAccount<'wa> { + pub fn standard_connect(&'wa mut self, uri: String) -> SolanaWalletAccount<'wa> { let (active_dapp, hash) = ActiveDapp::new(uri); self.active_dapps.insert(hash, active_dapp); @@ -83,7 +83,13 @@ impl SolanaAccountKeypair { SolanaWalletAccount::new(public_key) } - pub fn get_wallet_account<'wa>(&'wa self) -> SolanaWalletAccount<'wa> { + pub fn sign_in(&'wa mut self, formatted_input: &str) -> (SolanaWalletAccount<'wa>, [u8; 64]) { + let signature = self.keypair.sign_message(formatted_input.as_bytes()); + + (self.get_wallet_account(), *signature.as_array()) + } + + pub fn get_wallet_account(&'wa self) -> SolanaWalletAccount<'wa> { let public_key = self.pubkey().to_bytes(); SolanaWalletAccount::new(public_key) diff --git a/wallet-extension/src/message_handler.rs b/wallet-extension/src/message_handler.rs index fe1c268..61b3aaa 100644 --- a/wallet-extension/src/message_handler.rs +++ b/wallet-extension/src/message_handler.rs @@ -91,12 +91,14 @@ fn match_message(message: JsValue, app: AppManager) -> AtollWalletResult app.borrow_mut().standard_connect(&data), + ExtensionMessage::SolanaSignIn => app.borrow_mut().solana_sign_in(data), } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum ExtensionMessage { StandardConnect, + SolanaSignIn, } impl TryFrom<&JsValue> for ExtensionMessage { @@ -109,6 +111,7 @@ impl TryFrom<&JsValue> for ExtensionMessage { ))?; let matched = match parsed_js_value.as_str() { SolanaConstants::STANDARD_CONNECT => Self::StandardConnect, + SolanaConstants::SIGN_IN => Self::SolanaSignIn, _ => { return Err(AtollWalletError::UnsupportedExtensionMessage( parsed_js_value,