Crate wallet_standard_browser

Crate wallet_standard_browser 

Source
Expand description

§wallet_standard_browser


The WebAssembly/browser compatible Rust implementation of the Wallet Standard.


Crate Docs Status Unlicense codecov

§Overview

The wallet_standard_browser crate provides a WebAssembly-compatible implementation of the Wallet Standard for Solana. It enables Rust-based wallets to be used in browser environments and allows dApps to interact with these wallets through a consistent interface.

§Installation

To install you can use the following command:

cargo add wallet_standard_browser

Or directly add the following to your Cargo.toml:

[dependencies]
wallet_standard_browser = "0.5.0"

§Features

FeatureDescription
solanaEnables Solana-specific functionality

§Core Components

This crate provides several key components:

  1. BrowserWallet: A wrapper around JavaScript wallet implementations that conforms to the Wallet Standard
  2. BrowserWalletInfo: Represents wallet metadata for browser-based wallets
  3. BrowserWalletAccountInfo: Represents account information for browser-based wallets
  4. Feature wrappers: JavaScript bindings for wallet features like connect, disconnect, sign message, etc.

§Usage for dApp Developers

§Detecting and Connecting to Wallets

use wallet_standard_browser::prelude::*;
use wasm_bindgen_futures::spawn_local;
use web_sys::console;

// Function to detect and connect to available wallets
async fn connect_to_wallet() -> WalletResult<()> {
	// Get available wallets
	let wallets = get_wallets().await?;

	// Log the number of available wallets
	console::log_1(&format!("Found {} wallets", wallets.get().len()).into());

	// Find a specific wallet by name
	if let Some(wallet) = wallets.get().iter().find(|w| w.name() == "Phantom") {
		console::log_1(&format!("Found Phantom wallet").into());

		// Create a wallet instance
		let mut wallet_instance = BrowserWallet::from(wallet.clone());

		// Connect to the wallet
		let accounts = wallet_instance.connect().await?;
		console::log_1(&format!("Connected to {} accounts", accounts.len()).into());

		// Now you can use the wallet for operations
		if wallet_instance.connected() {
			// Example: Sign a message
			let message = b"Hello, Solana!";
			let signature = wallet_instance.sign_message_async(message).await?;
			console::log_1(&format!("Message signed successfully").into());
		}
	} else {
		console::log_1(&"Phantom wallet not found".into());
	}

	Ok(())
}

// Call this function from your application
fn initialize() {
	spawn_local(async {
		if let Err(err) = connect_to_wallet().await {
			console::error_1(&format!("Error: {:?}", err).into());
		}
	});
}

§Listening for Wallet Events

use wallet_standard_browser::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::spawn_local;

// Create a closure to handle wallet registration events
fn listen_for_wallets() -> WalletResult<()> {
	let wallets_callback = Closure::wrap(Box::new(move |wallet: BrowserWalletInfo| {
		// A new wallet has been registered
		let wallet_name = wallet.name();
		web_sys::console::log_1(&format!("New wallet registered: {}", wallet_name).into());

		// You can now use this wallet
		let wallet_instance = BrowserWallet::from(wallet);
		// Store the wallet instance for later use
	}) as Box<dyn FnMut(BrowserWalletInfo)>);

	// Get the wallets registry
	spawn_local(async move {
		match get_wallets().await {
			Ok(wallets) => {
				// Register the callback for new wallet registrations
				let _dispose = wallets.on_register(&wallets_callback);

				// Keep the callback alive
				wallets_callback.forget();
			}
			Err(err) => {
				web_sys::console::error_1(&format!("Error getting wallets: {:?}", err).into());
			}
		}
	});

	Ok(())
}

§Signing and Sending Transactions (Solana)

use solana_message::Message;
use solana_pubkey::Pubkey;
use solana_transaction::Transaction;
use solana_transaction::VersionedTransaction;
use wallet_standard_browser::prelude::*;
use wasm_bindgen_futures::spawn_local;

async fn send_transaction(wallet: &mut BrowserWallet) -> WalletResult<()> {
	// Ensure the wallet is connected
	if !wallet.connected() {
		wallet.connect().await?;
	}

	// Get the wallet's public key
	let pubkey = wallet.try_solana_pubkey()?;

	// Create a simple transaction (transfer 0.001 SOL to self)
	let instructions = vec![solana_system_interface::instructions::transfer(
		&pubkey, &pubkey, 1_000_000, // lamports (0.001 SOL)
	)];

	// Create a message
	let message = Message::new(&instructions, Some(&pubkey));

	// Create a transaction
	let transaction = Transaction::new_unsigned(message);

	// Convert to versioned transaction
	let versioned_transaction = VersionedTransaction::from(transaction);

	// Create transaction props
	let props = SolanaSignAndSendTransactionProps::builder()
		.transaction(versioned_transaction)
		.build();

	// Sign and send the transaction
	let result = wallet.sign_and_send_transaction(props).await?;

	// Get the signature
	let signature = result.signature();
	web_sys::console::log_1(&format!("Transaction sent with signature: {}", signature).into());

	Ok(())
}

§Usage for Wallet Developers

§Implementing a Browser Wallet

If you’re developing a wallet that needs to be compatible with the Wallet Standard in a browser environment, you’ll need to:

  1. Create a JavaScript wallet implementation that conforms to the Wallet Standard
  2. Register your wallet with the Wallet Standard

Here’s a simplified example of how to register your wallet:

use wallet_standard_browser::constants::*;
use wasm_bindgen::prelude::*;
use web_sys::CustomEvent;
use web_sys::CustomEventInit;
use web_sys::window;

#[wasm_bindgen]
pub fn register_wallet() {
	// Create your wallet implementation
	let wallet_info = create_wallet_info();

	// Register the wallet with the Wallet Standard
	let window = window().expect("no global window exists");
	let event_init = CustomEventInit::new();
	event_init.detail(&JsValue::from_serde(&wallet_info).unwrap());

	let event = CustomEvent::new_with_event_init_dict(REGISTER_WALLET_EVENT, &event_init)
		.expect("failed to create custom event");

	window
		.dispatch_event(&event)
		.expect("failed to dispatch event");
}

fn create_wallet_info() -> serde_json::Value {
	// Create a wallet info object that conforms to the Wallet Standard
	serde_json::json!({
		"name": "My Wallet",
		"icon": "data:image/svg+xml;base64,...", // Base64 encoded SVG
		"version": "1.0.0",
		"chains": ["solana:mainnet"],
		"features": [
			"standard:connect",
			"standard:disconnect",
			"solana:signMessage",
			"solana:signTransaction",
			"solana:signAndSendTransaction"
		],
		"accounts": []
	})
}

§Implementing Wallet Features

For each feature your wallet supports, you’ll need to implement the corresponding JavaScript functions. Here’s an example for the solana:signMessage feature:

// In your wallet's JavaScript code
window.myWallet = {
    // ... other wallet properties
    features: {
        // ... other features
        "solana:signMessage": {
            version: "1.0.0",
            signMessage: async function(accounts, messages) {
                // Implement message signing
                return messages.map(message => ({
                    signature: new Uint8Array(...), // The signature bytes
                    signedMessage: message, // The message that was signed
                    signatureType: "ed25519" // Optional signature type
                }));
            }
        }
    }
};

Then in your Rust code, you can use the wallet_standard_browser crate to interact with this JavaScript implementation:

use wallet_standard_browser::prelude::*;
use wasm_bindgen_futures::spawn_local;

fn use_wallet() {
	spawn_local(async {
		// Get available wallets
		let wallets = get_wallets().await.unwrap();

		// Find your wallet
		if let Some(wallet) = wallets.get().iter().find(|w| w.name() == "My Wallet") {
			// Create a wallet instance
			let mut wallet_instance = BrowserWallet::from(wallet.clone());

			// Connect to the wallet
			wallet_instance.connect().await.unwrap();

			// Sign a message
			let message = b"Hello, Solana!";
			let signature = wallet_instance.sign_message_async(message).await.unwrap();

			// Use the signature
			web_sys::console::log_1(&format!("Signature: {:?}", signature.signature()).into());
		}
	});
}

§Examples

For complete examples of how to use this crate, check out the examples directory in the repository.

§API Reference

For detailed API documentation, please refer to the API documentation.

Modules§

prelude

Structs§

BrowserExperimentalDecryptOutput
BrowserExperimentalEncryptOutput
BrowserStandardConnectOutput
BrowserStandardEventsProperties
BrowserWallet
BrowserWalletAccountInfo
Interface of a WalletAccount, also referred to as an Account.
BrowserWalletAccountInfoProps
BrowserWalletInfo
BrowserWalletInfoFeatures
BrowserWalletInfoProps
ExperimentalDecryptFeature
ExperimentalDecryptInput
ExperimentalDecryptProps
ExperimentalEncryptFeature
ExperimentalEncryptInput
ExperimentalEncryptProps
StandardConnectFeature
StandardConnectInput
Input options for connecting to a wallet.
StandardDisconnectFeature
StandardEventsFeature
Wallets

Enums§

WalletError

Constants§

APP_READY_EVENT
Event that will be dispatched by the app on the window when the app is ready to register {@link Wallet | Wallets}.
CIPHER_X25519_XSALSA20_POLY1305
Default encryption algorithm in NaCl. Curve25519 scalar multiplication, Salsa20 secret-key encryption, and Poly1305 one-time authentication.
EXPERIMENTAL_DECRYPT
EXPERIMENTAL_ENCRYPT
REGISTER_WALLET_EVENT
Event that will be dispatched on the window by each {@link Wallet | Wallet} when the Wallet is ready to be registered by the app.
STANDARD_CONNECT
Feature identifier for the standard connect feature.
STANDARD_DISCONNECT
Feature identifier for the standard disconnect feature.
STANDARD_EVENTS

Traits§

ConnectedWalletStandardEvents
ExperimentalDecryptOutput
ExperimentalEncryptOutput
FeatureFromJs
IntoWalletError
StandardConnectOutput
Output of a successful wallet connection.
StandardEventProperties
Wallet
The core trait for wallet implementations.
WalletAccountInfo
Interface of a WalletAccount, also referred to as an Account.
WalletExperimentalDecrypt
WalletExperimentalEncrypt
WalletInfo
Provides information about a wallet implementation.
WalletStandard
A trait that combines the core wallet functionality with standard connect and disconnect features.
WalletStandardConnect
Trait for wallets that support connecting to authorize accounts.
WalletStandardDisconnect
Trait for wallets that support disconnecting.

Functions§

get_wallets
register_wallet
Register a {@link “@wallet-standard/base”.Wallet} as a Standard Wallet with the app.

Type Aliases§

WalletResult