Skip to main content

solana_wasi/
lib.rs

1//! Solana primitives that compile to `wasm32-wasip2`.
2//!
3//! `solana-sdk` and `solana-client` do not build inside a WebAssembly component
4//! without a fight, and the parts of them a tool plugin needs are small. This
5//! crate is those parts, written against the wire formats directly: pubkeys and
6//! PDAs, a typed JSON-RPC client over a swappable transport, SPL Token and
7//! Token-2022 account parsing including the full extension set, unsigned v0
8//! transaction construction, durable nonces, and the output-shaping helpers
9//! that keep a tool's answer inside a model's context budget.
10//!
11//! # Three properties, on purpose
12//!
13//! **It cannot sign.** There is no keypair type, no signer trait, and no path
14//! from an instruction to a signature. A plugin built on this crate can hold at
15//! most an RPC URL. Signing belongs to a wallet, a human, or a multisig.
16//!
17//! **It is host-testable.** Everything except [`transport::WakiTransport`] is
18//! pure Rust with no wasm dependency. A plugin's `cargo test` runs on the host
19//! against [`transport::MockTransport`], with no wasm toolchain and no live
20//! network, which is what ZeroClaw's plugin CI requires.
21//!
22//! **It treats the chain as hostile input.** Token names, symbols and metadata
23//! URIs are written by whoever deployed the mint. [`sanitize`] exists because
24//! those strings end up in a language model's context, and a tool that forwards
25//! them verbatim is an injection vector with an RPC bill.
26//!
27//! # Example
28//!
29//! ```
30//! use solana_wasi::prelude::*;
31//!
32//! # fn main() -> solana_wasi::Result<()> {
33//! let transport = MockTransport::new().on(
34//!     "getAccountInfo",
35//!     serde_json::json!({
36//!         "context": { "slot": 1 },
37//!         "value": {
38//!             "lamports": 1_000_000_000u64,
39//!             "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
40//!             // 82 zero bytes: a mint with no authorities.
41//!             "data": [base64_of_82_zero_bytes(), "base64"],
42//!             "executable": false,
43//!             "rentEpoch": 0
44//!         }
45//!     }),
46//! );
47//!
48//! let rpc = RpcClient::new("https://api.mainnet-beta.solana.com", transport);
49//! let mint_address = Pubkey::from_base58("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")?;
50//! let account = rpc.require_account(&mint_address)?;
51//! let state = MintState::parse(mint_address, &account)?;
52//!
53//! assert_eq!(state.program, TokenProgram::Legacy);
54//! assert!(state.mint.freeze_authority.is_none());
55//! # Ok(())
56//! # }
57//! # fn base64_of_82_zero_bytes() -> String {
58//! #     use base64::Engine;
59//! #     base64::engine::general_purpose::STANDARD.encode([0u8; 82])
60//! # }
61//! ```
62
63#![forbid(unsafe_code)]
64#![warn(missing_docs)]
65
66pub mod error;
67pub mod metadata;
68pub mod nonce;
69pub mod pubkey;
70pub mod rpc;
71pub mod sanitize;
72pub mod shape;
73pub mod token;
74pub mod transport;
75pub mod tx;
76
77pub use error::{Error, Result};
78
79/// Everything a plugin's pure core typically imports.
80///
81/// One caveat, and it costs an hour if you hit it blind: this re-exports the
82/// crate's `Result<T>` alias, which fixes the error type. Do not glob-import
83/// the prelude inside a `wit_bindgen::generate!` module — a WIT export returns
84/// `Result<ToolResult, String>`, and the alias shadows it with a "type alias
85/// takes 1 generic argument but 2 were supplied" error that points at the
86/// wrong line. Import what the shim needs by name instead.
87pub mod prelude {
88    pub use crate::error::{Error, Result};
89    pub use crate::pubkey::{ids, Pubkey};
90    pub use crate::rpc::{Account, Commitment, RpcClient};
91    pub use crate::sanitize::{untrusted_text, untrusted_uri, Sanitized};
92    pub use crate::shape::{parse_amount, percent_of, ui_amount, Budget};
93    pub use crate::token::{associated_token_address, MintState, TokenAccount, TokenProgram};
94    pub use crate::transport::{MockTransport, Transport};
95    #[cfg(target_family = "wasm")]
96    pub use crate::transport::WakiTransport;
97    pub use crate::tx::{instructions, AccountMeta, Instruction, Message, UnsignedTransaction};
98}