leptos_solana/lib.rs
1//! Pure-Rust [Wallet Standard](https://github.com/wallet-standard/wallet-standard)
2//! bindings for [Leptos](https://leptos.dev). Discover, connect to, and sign
3//! with Phantom, Backpack, Solflare, Glow, Ledger Live, and any other Wallet
4//! Standard-compliant Solana wallet — no hand-written JavaScript shipped with
5//! the crate, no `npm`, no wallet-adapter wrappers.
6//!
7//! # Quick start
8//!
9//! Add the crate alongside Leptos:
10//!
11//! ```toml
12//! [dependencies]
13//! leptos-solana = "0.1"
14//! leptos = { version = "0.8", features = ["csr"] }
15//! ```
16//!
17//! Install the context once at the app root, then call [`use_wallet`](prelude::use_wallet)
18//! from any component:
19//!
20//! ```ignore
21//! use leptos::prelude::*;
22//! use leptos_solana::prelude::*;
23//! use leptos_solana::wallet::CHAIN_MAINNET;
24//!
25//! #[component]
26//! fn App() -> impl IntoView {
27//! provide_wallet_context(CHAIN_MAINNET);
28//! let wallet = use_wallet();
29//!
30//! view! {
31//! <ul>
32//! {move || wallet.wallets.get().0.into_iter().map(|w| {
33//! let name = w.name();
34//! let ctx = wallet.clone();
35//! view! {
36//! <li><button on:click=move |_| ctx.select(w.clone())>{name}</button></li>
37//! }
38//! }).collect_view()}
39//! </ul>
40//! }
41//! }
42//! ```
43//!
44//! Signing and submitting a transaction:
45//!
46//! ```ignore
47//! use leptos_solana::prelude::*;
48//!
49//! let ix = Instruction {
50//! program_id,
51//! accounts: vec![
52//! AccountMeta::new(from, true),
53//! AccountMeta::new(to, false),
54//! ],
55//! data,
56//! };
57//!
58//! let rpc = RpcClient::devnet();
59//! let blockhash = rpc.get_latest_blockhash(CommitmentConfig::confirmed()).await?;
60//!
61//! let msg = Message::new_with_blockhash(&[ix], Some(&from), &blockhash);
62//! let tx: VersionedTransaction = Transaction::new_unsigned(msg).into();
63//!
64//! let sig: Vec<u8> = wallet.sign_and_send(&tx).await?;
65//! ```
66//!
67//! # Design notes
68//!
69//! - **Wallet Standard is a JS spec.** Wallets register themselves via
70//! `CustomEvent`s on `window`. The crate reaches those objects with
71//! `wasm_bindgen`/`js_sys` — no `.js` shim is written by hand and no
72//! `npm`/`esbuild` is needed. [`discovery::start`] runs the spec-compliant
73//! `app-ready` + `register-wallet` handshake.
74//!
75//! - **VersionedTransaction only.** The wallet-signing API is
76//! [`VersionedTransaction`](solana_transaction::versioned::VersionedTransaction)-only.
77//! Legacy `Transaction` values can still be constructed and converted via
78//! `Into` — the Legacy variant of `VersionedMessage` serializes to the
79//! exact same wire bytes, so there is no behavior change for programs
80//! that do not need address lookup tables.
81//!
82//! - **Tiny dep closure.** No `solana-sdk`, no `solana-client`, no `tokio`.
83//! Pure Rust transaction construction via Anza's split crates
84//! (`solana-pubkey`, `solana-message`, `solana-transaction`, `solana-instruction`),
85//! bincode v1 for wire format, `gloo-net` for JSON-RPC.
86//!
87//! - **Reactive state.** [`WalletContext`](context::WalletContext) exposes
88//! `wallets`/`selected`/`account`/`chain` as Leptos signals. The context's
89//! `connect`/`disconnect`/`sign_*` methods are `async` and can be called
90//! from `spawn_local`.
91//!
92//! - **Auto-reconnect.** On successful `connect`, the wallet name is
93//! persisted to `localStorage`; on [`provide_wallet_context`](context::provide_wallet_context),
94//! if that wallet registers during discovery it is silently reconnected
95//! without prompting.
96//!
97//! - **Clean teardown.** [`discovery::start`] returns a [`DiscoveryHandle`](discovery::DiscoveryHandle)
98//! that removes the event listener in `Drop`. [`provide_wallet_context`](context::provide_wallet_context)
99//! ties the handle's lifetime to the Leptos owner.
100//!
101//! # Comparison with the JS stack
102//!
103//! The JS wallet ecosystem is layered:
104//! `@wallet-standard/app` → `@solana/wallet-adapter-base` → `@solana/wallet-adapter-react`
105//! → `@solana/wallet-adapter-react-ui`. This crate provides the first three
106//! layers for Leptos (discovery, signer primitives, and a reactive context).
107//! No UI components ship with the crate.
108//!
109//! # Modules
110//!
111//! - [`context`] Leptos `WalletContext`, signals, `connect`/`sign_*` methods.
112//! - [`discovery`] Wallet Standard event-based discovery.
113//! - [`features`] Typed wrappers over Solana feature methods.
114//! - [`rpc`] Minimal JSON-RPC client (`gloo-net`).
115//! - [`storage`] `localStorage` persistence for last-connected wallet.
116//! - [`tx`] `VersionedTransaction` bincode serialize/deserialize.
117//! - [`wallet`] `wasm_bindgen` extern types matching the Wallet Standard spec.
118//! - [`error`] Crate-wide error type.
119//! - [`prelude`] `use leptos_solana::prelude::*;` brings in everything callers need.
120
121pub mod context;
122pub mod discovery;
123pub mod error;
124pub mod features;
125pub mod rpc;
126pub mod storage;
127pub mod tx;
128pub mod wallet;
129
130pub use error::{Error, Result};
131
132/// One-stop import for common callers: `use leptos_solana::prelude::*;`.
133///
134/// Brings in the Leptos context functions, the wallet types, the RPC client,
135/// the tx serde helpers, and every Anza `solana-*` type you'd normally
136/// `use` by hand.
137pub mod prelude {
138 pub use crate::context::{provide_wallet_context, use_wallet, WalletContext};
139 pub use crate::discovery::WalletList;
140 pub use crate::error::{Error, Result};
141 pub use crate::wallet::{Wallet, WalletAccount};
142
143 pub use crate::rpc::RpcClient;
144 pub use crate::tx::{deserialize, serialize};
145
146 pub use solana_commitment_config::CommitmentConfig;
147 pub use solana_hash::Hash;
148 pub use solana_instruction::{AccountMeta, Instruction};
149 pub use solana_message::{
150 v0::Message as MessageV0, AddressLookupTableAccount, Message, VersionedMessage,
151 };
152 pub use solana_pubkey::Pubkey;
153 pub use solana_signature::Signature;
154 pub use solana_transaction::versioned::VersionedTransaction;
155 pub use solana_transaction::Transaction;
156}