Phantom-pioneered Sign-In With Solana (solana:signIn) is the Solana analog of SIWE: dApp sends a structured SolanaSignInInput, wallet formats canonical text, signs Ed25519, returns { signedMessage, signature }. Phantom (extension + mobile), Solflare, Backpack already implement it on the wallet side, so requests will show up via WalletConnect. Production dApp adoption is still small (DRiP Haus, Phantom demos, Supabase-backed apps) but growing.
Scope (~300-400 LOC, mirrors gem_evm::siwe):
- New
gem_solana::siws module with parser + builder for the canonical text format
- New
SignDigestType::SolanaSignIn variant
- WC handler maps
solana:signIn -> SignMessage with the new type
MessageSigner::preview / payload_preview surface structured fields (domain, statement, expiration) the same way SIWE does
- Domain validation against WC session origin (reuse
validate_session_domain from crates/gem_wallet_connect/src/validator.rs)
Gotchas:
chainId is a string (mainnet/devnet/solana:<genesisHash>), not a u64 — don't reuse the SIWE parser
- Verify against
output.signedMessage bytes; Phantom mobile prepends an OCMS header before signing
- Enforce
issuedAt <= notBefore <= expirationTime with ~10min skew
- Ledger can't display SIWS messages today; fall back to plain signMessage gracefully
Spec: https://github.com/anza-xyz/wallet-standard/blob/master/packages/core/util/src/signIn.ts
Phantom blog: https://phantom.com/learn/developers/sign-in-with-solana
Phantom-pioneered Sign-In With Solana (
solana:signIn) is the Solana analog of SIWE: dApp sends a structuredSolanaSignInInput, wallet formats canonical text, signs Ed25519, returns{ signedMessage, signature }. Phantom (extension + mobile), Solflare, Backpack already implement it on the wallet side, so requests will show up via WalletConnect. Production dApp adoption is still small (DRiP Haus, Phantom demos, Supabase-backed apps) but growing.Scope (~300-400 LOC, mirrors
gem_evm::siwe):gem_solana::siwsmodule with parser + builder for the canonical text formatSignDigestType::SolanaSignInvariantsolana:signIn-> SignMessage with the new typeMessageSigner::preview/payload_previewsurface structured fields (domain, statement, expiration) the same way SIWE doesvalidate_session_domainfromcrates/gem_wallet_connect/src/validator.rs)Gotchas:
chainIdis a string (mainnet/devnet/solana:<genesisHash>), not a u64 — don't reuse the SIWE parseroutput.signedMessagebytes; Phantom mobile prepends an OCMS header before signingissuedAt <= notBefore <= expirationTimewith ~10min skewSpec: https://github.com/anza-xyz/wallet-standard/blob/master/packages/core/util/src/signIn.ts
Phantom blog: https://phantom.com/learn/developers/sign-in-with-solana