Chain-Agnostic Wallet Authentication for Rust
Type-safe Rust SDK for CAIP-122 Sign-In with X. Construct, parse, validate, and verify wallet authentication messages across any blockchain.
Quick Start | CLI | Architecture | API docs
| Crate | Description | |
|---|---|---|
siwx |
Core data model, parser, validator, Verifier trait |
|
siwx-evm |
EIP-191 + EIP-1271 verification — Ethereum, Polygon, Arbitrum, … | |
siwx-svm |
Ed25519 verification — Solana | |
siwx-cli |
CLI tool for message generation, parsing, and verification |
CAIP-122 standardises wallet-based authentication across blockchains — the chain-agnostic successor to EIP-4361 (SIWE). This SDK provides:
- Message construction — build CAIP-122 challenge messages with a builder API
- Message parsing — round-trip
FromStr/Displayfor the human-readable signing format - Temporal & domain validation — expiration, not-before, domain binding, nonce binding
- Signature verification — pluggable
Verifiertrait with built-in EVM and Solana support - CLI tool — generate, parse, and verify messages from the command line with JSON output
The core siwx crate is chain-agnostic; chain-specific logic is in companion crates.
use siwx::SiwxMessage;
let message = SiwxMessage::new(
"example.com", // domain
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // address
"https://example.com/login", // uri
"1", // version
"1", // chain_id (EIP-155)
)?
.with_statement("Sign in to Example")
.with_nonce(siwx::nonce::generate_default());
// Format as the human-readable signing string
let signing_input = siwx_evm::format_message(&message);
// → "example.com wants you to sign in with your Ethereum account:\n0xd8dA…"use siwx::{SiwxMessage, ValidateOpts, Verifier};
use siwx_evm::EvmVerifier;
// Parse the message returned from the frontend
let message: SiwxMessage = signing_input.parse()?;
// Validate fields & temporal constraints
message.validate(&ValidateOpts {
domain: Some("example.com".into()),
nonce: Some(expected_nonce),
..Default::default()
})?;
// Cryptographic verification (EIP-191 with EIP-1271 fallback)
let verifier = EvmVerifier::with_rpc("https://eth.llamarpc.com");
verifier.verify(&message, &signature_bytes).await?;Shell (macOS / Linux):
curl -fsSL https://sh.qntx.fun/siwx | shPowerShell (Windows):
irm https://sh.qntx.fun/siwx/ps | iexOr via Cargo:
cargo install siwx-clisiwx evm message \
--domain example.com \
--address 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 \
--uri https://example.com/login \
--chain-id 1 \
--statement "Sign in to Example"siwx svm message \
--domain example.com \
--address GwAF45zjfyGzUbd3i3hXxzGeuchzEZXwpRYHZM5912F1 \
--uri https://example.com/login \
--chain-id 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9dsiwx evm verify --message "..." --signature 0x...
siwx svm verify --message "..." --signature 0x...All commands support --json for programmatic / agent consumption:
siwx --json evm message --domain example.com --address 0x... --uri https://... --chain-id 1{
"chain": "ethereum",
"message": "example.com wants you to sign in with your Ethereum account:\n...",
"domain": "example.com",
"address": "0x...",
"uri": "https://...",
"version": "1",
"chain_id": "1",
"nonce": "L8s2Mf7kGxPQN9a4z",
"issued_at": "2024-01-01T00:00:00Z"
}sequenceDiagram
participant Frontend
participant Backend
participant Wallet
Backend->>Frontend: 1. Challenge (SiwxMessage as text)
Frontend->>Wallet: 2. personal_sign / signMessage
Wallet-->>Frontend: 3. Signature bytes
Frontend->>Backend: 4. Message text + Signature
Backend->>Backend: 5. Parse → Validate → Verify
example.com wants you to sign in with your Ethereum account:
0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
Sign in to Example
URI: https://example.com/login
Version: 1
Chain ID: 1
Nonce: L8s2Mf7kGxPQN9a4z
Issued At: 2024-01-01T00:00:00Z
Chain-specific crates implement the Verifier trait:
pub trait Verifier: Send + Sync {
fn verify(
&self,
message: &SiwxMessage,
signature: &[u8],
) -> impl Future<Output = Result<(), SiwxError>> + Send;
}| Verifier | Crate | Signature Type | Async |
|---|---|---|---|
Eip191Verifier |
siwx-evm |
ECDSA recovery (personal_sign) |
No |
Eip1271Verifier |
siwx-evm |
Smart contract isValidSignature (RPC) |
Yes |
EvmVerifier |
siwx-evm |
EIP-191 first, EIP-1271 fallback | Yes |
Ed25519Verifier |
siwx-svm |
Ed25519 | No |
Implement Verifier for your target chain:
use siwx::{SiwxError, SiwxMessage, Verifier};
pub struct MyChainVerifier;
impl Verifier for MyChainVerifier {
async fn verify(
&self,
message: &SiwxMessage,
signature: &[u8],
) -> Result<(), SiwxError> {
// Your chain-specific verification logic
todo!()
}
}| Feature | Crate | Description |
|---|---|---|
serde |
siwx |
Serialize / Deserialize for SiwxMessage |
| Standard | Relationship |
|---|---|
| CAIP-122 | Core specification — Sign-In with X abstract data model |
| CAIP-2 | Blockchain ID format (namespace:reference) |
| CAIP-10 | Account ID format (chain_id:account_address) |
| EIP-4361 | Sign-In with Ethereum — the EVM namespace profile |
| EIP-191 | Ethereum personal message signatures |
| EIP-1271 | Smart contract signature validation |
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project shall be dual-licensed as above, without any additional terms or conditions.