Skip to main content

Documentation Index

Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt

Use this file to discover all available pages before exploring further.

Try it Live

Run SIWE examples in the interactive playground
New to SIWE? Start with Fundamentals for guided examples and authentication concepts.

Type Definition

Sign-In with Ethereum (EIP-4361) implementation for decentralized authentication. Creates structured messages users sign with private keys, proving address ownership without key exposure.

Authentication Flow

┌──────────────┐      ┌─────────────┐      ┌─────────────┐      ┌─────────────┐
│   Frontend   │      │   Wallet    │      │   Backend   │      │   Database  │
└──────────────┘      └─────────────┘      └─────────────┘      └─────────────┘
      │                     │                     │                     │
      │  1. Request Nonce   │                     │                     │
      ├────────────────────────────────────────>  │                     │
      │                     │                     │                     │
      │                     │             Generate nonce │              │
      │                     │             Store for 5min │──────────>  │
      │  2. Get nonce       │                     │                     │
      │  <────────────────────────────────────────┤                     │
      │                     │                     │                     │
      │ 3. Create message   │                     │                     │
      │    with nonce       │                     │                     │
      ├────────────────────>│                     │                     │
      │                     │                     │                     │
      │  4. Sign message    │                     │                     │
      │  <────────────────┤ │                     │                     │
      │                     │ signature           │                     │
      │  5. Verify message  │                     │                     │
      ├────────────────────────────────────────>  │                     │
      │                     │                     │                     │
      │                     │             Check nonce │                │
      │                     │             Verify sig   │──────────>   │
      │                     │             Check valid  │  <──────────┤
      │                     │             Create token │              │
      │  6. Session token   │                     │                     │
      │  <────────────────────────────────────────┤                     │
      │                     │                     │                     │
      └──────────────┘      └─────────────┘      └─────────────┘      └─────────────┘

Message Structure

A SIWE message is a human-readable text format combining user intent with cryptographic proof:
example.com wants you to sign in with your Ethereum account:
0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e

Sign in to Example App

URI: https://example.com/login
Version: 1
Chain ID: 1
Nonce: testnonce123
Issued At: 2024-11-06T12:00:00.000Z
Expiration Time: 2024-11-06T13:00:00.000Z
Not Before: 2024-11-06T11:00:00.000Z

Resources:
- https://example.com/api/user
- https://example.com/api/profile
Key components:
  • Domain (RFC 4501): Where the message is being signed for
  • Address: Which account is signing
  • URI: What resource is being accessed
  • Statement: Human-readable context
  • Chain ID: Which blockchain
  • Nonce: One-time value, prevents replay attacks
  • Timestamps: Validity window
  • Resources: Optional list of what’s being granted access to

Factory

Siwe(params: MessageParams): Siwe
Creates Siwe message instance from parameters. Parameters:
  • params.domain: RFC 4501 dns authority
  • params.address: AddressType performing signing
  • params.uri: RFC 3986 URI
  • params.chainId: EIP-155 Chain ID
  • params.statement?: Human-readable assertion (optional)
  • params.expirationTime?: ISO 8601 expiration (optional)
  • params.notBefore?: ISO 8601 valid-from time (optional)
  • params.requestId?: System identifier (optional)
  • params.resources?: Resource URIs (optional)
  • params.nonce?: Custom nonce (auto-generated if omitted)
  • params.issuedAt?: Custom timestamp (current time if omitted)
Returns: Siwe message instance

Static Constructors

Siwe.create(params)

create(params: MessageParams): BrandedMessage
Create message with defaults. Same as factory.

Siwe.parse(text)

parse(text: string): BrandedMessage
Parse EIP-4361 formatted string to message. Throws: Error if format invalid

Static Utilities

Formatting

Siwe.format(message)

format(message: BrandedMessage): string
Format message to EIP-4361 string for signing.

Validation

Siwe.validate(message, options?)

validate(message: BrandedMessage, options?: { now?: Date }): ValidationResult
Validate message structure and timestamps. Returns: { valid: true } or { valid: false, error: ValidationError }

Signing & Verification

Siwe.getMessageHash(message)

getMessageHash(message: BrandedMessage): Uint8Array
Get EIP-191 personal sign message hash (32 bytes).

Siwe.verify(message, signature)

verify(message: BrandedMessage, signature: Signature): boolean
Verify signature matches message address. Parameters:
  • signature: 65-byte signature (r + s + v)
Returns: true if valid

Siwe.verifyMessage(message, signature, options?)

verifyMessage(message: BrandedMessage, signature: Signature, options?: { now?: Date }): ValidationResult
Combined validation and verification.

Nonce Generation

Siwe.generateNonce(length?)

generateNonce(length?: number): string
Generate cryptographically secure random nonce. Parameters:
  • length: Nonce length (default 11, min 8)
Returns: Base62 alphanumeric string

Instance Methods

All static utilities available as instance methods:
const message = Siwe.create({
  domain: "example.com",
  address: userAddress,
  uri: "https://example.com",
  chainId: 1,
});

message.format()           // string
message.validate()         // ValidationResult
message.getMessageHash()   // Uint8Array
message.verify(signature)  // boolean
Instance methods delegate to BrandedSiwe namespace functions.

Types

type BrandedMessage = {
  domain: string;
  address: AddressType;
  uri: string;
  version: string;
  chainId: number;
  nonce: string;
  issuedAt: string;
  statement?: string;
  expirationTime?: string;
  notBefore?: string;
  requestId?: string;
  resources?: string[];
}

type Signature = Uint8Array;  // 65 bytes: r (32) + s (32) + v (1)

type ValidationResult =
  | { valid: true }
  | { valid: false; error: ValidationError };

type ValidationError =
  | { type: "invalid_domain"; message: string }
  | { type: "invalid_address"; message: string }
  | { type: "invalid_uri"; message: string }
  | { type: "invalid_version"; message: string }
  | { type: "invalid_chain_id"; message: string }
  | { type: "invalid_nonce"; message: string }
  | { type: "invalid_timestamp"; message: string }
  | { type: "expired"; message: string }
  | { type: "not_yet_valid"; message: string }
  | { type: "signature_mismatch"; message: string };
See BrandedSiwe for branded type details.

Implementation

  • Delegates to BrandedSiwe namespace
  • Extends Object.prototype
  • Structured message format per EIP-4361
  • Supports optional fields via conditional spreading

Quick Start

import { Siwe, Address } from 'tevm';

// Create message
const message = Siwe.create({
  domain: "example.com",
  address: Address("0x..."),
  uri: "https://example.com/login",
  chainId: 1,
  statement: "Sign in to Example App",
});

// Format for wallet signing
const text = message.format();

// User signs with wallet
const signature = await wallet.signMessage(text);

// Validate and verify
const result = Siwe.verifyMessage(message, signature);
if (result.valid) {
  // Authenticated
}

Common Patterns

Authentication Flow

// Backend: Generate nonce
const nonce = Siwe.generateNonce();
storeNonce(userId, nonce);

// Frontend: Create and sign message
const message = Siwe.create({
  domain: window.location.host,
  address: userAddress,
  uri: window.location.origin,
  chainId: await provider.getChainId(),
  nonce,
});
const signature = await wallet.signMessage(message.format());

// Backend: Verify
const parsed = Siwe.parse(messageText);
const result = Siwe.verifyMessage(parsed, signature);
if (result.valid && verifyNonce(parsed.nonce)) {
  createSession(parsed.address);
}

Session Management

const message = Siwe.create({
  domain: "example.com",
  address: userAddress,
  uri: "https://example.com",
  chainId: 1,
  expirationTime: new Date(Date.now() + 3600000).toISOString(),
});

// Validate at session use
const result = message.validate({ now: new Date() });
if (!result.valid) {
  requestReauth();
}

API Documentation

Constructors

Creating SIWE messages from parameters or text. View constructors →

Parsing

Parse EIP-4361 formatted strings to message objects. View parsing →

Validation

Validate message structure and timestamps. View validation →

Signing & Verification

Create hashes and verify signatures with addresses. View signing →

Utilities

Nonce generation and helper functions. View utilities →

Usage Patterns

Real-world authentication and session patterns. View patterns →

Quick Reference: Security Checklist

Always Verify:
  • Domain matches request origin
  • Nonce is fresh and single-use
  • Signature is valid
  • Message not expired
  • Address matches session
  • HTTPS in production
  • Siwe (Effect) - Effect.ts integration with Schema validation
  • Address - 20-byte Ethereum addresses used in messages
  • Signature - 65-byte signatures (r + s + v) for verification
  • Keccak256 - 32-byte hashes from message signing

References