Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 47 additions & 11 deletions wallet-extension/extension/js/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@
const RELAY_SOLANA_SIGN_IN = "relay:solana:signIn";
const SOLANA_SIGN_IN = "solana:signIn";

const RELAY_SIGN_MESSAGE = "relay:solana:signMessage";
const SOLANA_SIGN_MESSAGE = "solana:signMessage";

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 RELAY_SIGN_MESSAGE = "relay:solana:signMessage";
const SOLANA_SIGN_MESSAGE = "solana:signMessage";

const WALLET_REGISTER_EVENT = "wallet-standard:register-wallet";
const APP_READY_EVENT = "wallet-standard:app-ready";

Expand Down Expand Up @@ -167,13 +173,26 @@
return [{ signedTransaction: new Uint8Array([4, 5, 6]) }];
};
#signMessage = async (...inputs) => {
console.log("SIGN MSG", ...inputs);
return [
{
signedMessage: new Uint8Array([7, 8, 9]),
signature: new Uint8Array([10, 11, 12]),
},
];
const result = await new Promise((resolve, reject) => {
const listener = (event) => {
if (event.source !== window) return;
if (event.data.type === RELAY_SIGN_MESSAGE) {
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_MESSAGE, requestData: inputs[0], text: "" },
"*"
);
});

return result;
};
#signIn = async (...inputs) => {
const result = await new Promise((resolve, reject) => {
Expand Down Expand Up @@ -226,7 +245,6 @@
extension.runtime.sendMessage(
{ resource: event.data.type, data: window.location.origin },
(response) => {
console.log("Content script got response from background:", response);
// Relay back under RELAY_STANDARD_CONNECT
window.postMessage(
{
Expand All @@ -242,14 +260,11 @@
});

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(
{
Expand All @@ -263,4 +278,25 @@
);
}
});

window.addEventListener("message", (event) => {
if (event.source !== window) return;
if (event.data.type === SOLANA_SIGN_MESSAGE) {
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_SIGN_MESSAGE,
success: response.success,
failure: response.failure,
},
"*"
);
}
);
}
});
})();
1 change: 1 addition & 0 deletions wallet-extension/src/impl_solana/interface/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod sign_in;
mod sign_message;
mod standard_connect;
60 changes: 0 additions & 60 deletions wallet-extension/src/impl_solana/interface/sign_in.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,63 +335,3 @@ impl<'wa> SignInInputParser<'wa> {
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<Cow<'wa, str>>,
/// 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<Cow<'wa, str>>,
/// 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<Cow<'wa, str>>,
/// 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<Cow<'wa, str>>,
/// Optional EIP-4361 version.
/// If not provided, the wallet does not include Version in the message.
version: Option<Cow<'wa, str>>,
/// 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<Cow<'wa, str>>,
/// 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<Cow<'wa, str>>,
/// 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<SystemTime>,
/// 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<SystemTime>,
/// 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<SystemTime>,
/// 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<Cow<'wa, str>>,
/// 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]>,
} */
69 changes: 69 additions & 0 deletions wallet-extension/src/impl_solana/interface/sign_message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use wasm_bindgen::{JsCast, JsValue};
use web_sys::js_sys::{self, Array, Uint8Array};

use crate::{
App, AtollWalletError, AtollWalletResult, Reflection, SolanaConstants, app_console_log,
};

impl App {
pub fn solana_sign_message(&mut self, data: JsValue) -> AtollWalletResult<JsValue> {
app_console_log(SolanaConstants::SIGN_MESSAGE, &data);
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()),
)?;

let data = Reflection::new_object_from_js_value(data)?;
let account_js_value = data.get_object(
"account",
AtollWalletError::JsCast(
"`account` object was not found in the `requestData` for `solana:signMessage`"
.to_string(),
),
)?;
let account_object = Reflection::new_object_from_js_value(account_js_value)?;
let public_key: [u8;32] = account_object.get_object(
"publicKey",
AtollWalletError::JsCast(
"`publicKey` was not found in the `requestData.accounts` for `solana:signMessage`"
.to_string(),
),
)?.dyn_into::<js_sys::Uint8Array>().or(
Err(AtollWalletError::JsCast("`publicKey` in `requestData` for `solana:signMessage` not a byte array.".to_string()))
)?.to_vec().try_into().or(Err(AtollWalletError::Input("`publicKey` in `requestData` for `solana:signMessage` not an byte array of 32 in length.".to_string())))?;
let message_js_value = data.get_object(
"message",
AtollWalletError::JsCast(
"`message` was not found in the `requestData` for `solana:signMessage`".to_string(),
),
)?;

let message_bytes = message_js_value
.dyn_into::<js_sys::Uint8Array>()
.or(Err(AtollWalletError::JsCast(
"`message` in `requestData` for `solana:signMessage` not a byte array.".to_string(),
)))?
.to_vec();

let signature = self
.active_keypair()?
.sign_message(&public_key, &message_bytes);

let sign_in_output = Reflection::new_object();

let signed_message = Uint8Array::new_from_slice(message_bytes.as_slice());
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())
}
}
9 changes: 9 additions & 0 deletions wallet-extension/src/impl_solana/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,18 @@ impl<'wa> SolanaAccountKeypair {
pub fn sign_in(&'wa mut self, formatted_input: &str) -> (SolanaWalletAccount<'wa>, [u8; 64]) {
let signature = self.keypair.sign_message(formatted_input.as_bytes());

// TODO add to active dapps and add dapp icon too

(self.get_wallet_account(), *signature.as_array())
}

// TODO type checks to see if a dapp is currently authorized to perform an operation
pub fn sign_message(&mut self, public_key: &[u8; 32], message: &[u8]) -> [u8; 64] {
let signature = self.keypair.sign_message(message);

*signature.as_array()
}

pub fn get_wallet_account(&'wa self) -> SolanaWalletAccount<'wa> {
let public_key = self.pubkey().to_bytes();

Expand Down
3 changes: 3 additions & 0 deletions wallet-extension/src/message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,15 @@ fn match_message(message: JsValue, app: AppManager) -> AtollWalletResult<JsValue
match resource {
ExtensionMessage::StandardConnect => app.borrow_mut().standard_connect(&data),
ExtensionMessage::SolanaSignIn => app.borrow_mut().solana_sign_in(data),
ExtensionMessage::SolanaSignMessage => app.borrow_mut().solana_sign_message(data),
}
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum ExtensionMessage {
StandardConnect,
SolanaSignIn,
SolanaSignMessage,
}

impl TryFrom<&JsValue> for ExtensionMessage {
Expand All @@ -112,6 +114,7 @@ impl TryFrom<&JsValue> for ExtensionMessage {
let matched = match parsed_js_value.as_str() {
SolanaConstants::STANDARD_CONNECT => Self::StandardConnect,
SolanaConstants::SIGN_IN => Self::SolanaSignIn,
SolanaConstants::SIGN_MESSAGE => Self::SolanaSignMessage,
_ => {
return Err(AtollWalletError::UnsupportedExtensionMessage(
parsed_js_value,
Expand Down
Loading