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
BrandedSiwe
Branded type for SIWE messages with type safety.
Overview
BrandedMessage provides type-safe SIWE message representation using TypeScript’s structural typing. All SIWE operations work with BrandedMessage type.
Type Definition
type BrandedMessage <
TDomain extends string = string ,
TAddress extends AddressType = AddressType ,
TUri extends string = string ,
TVersion extends string = string ,
TChainId extends number = number ,
> = {
domain : TDomain ;
address : TAddress ;
statement ?: string ;
uri : TUri ;
version : TVersion ;
chainId : TChainId ;
nonce : string ;
issuedAt : string ;
expirationTime ?: string ;
notBefore ?: string ;
requestId ?: string ;
resources ?: string [];
};
Generic Parameters
TDomain
Type: string literal type
Default: string
Purpose: Exact domain typing
Example: "example.com" vs string
TAddress
Type: AddressType
Default: AddressType
Purpose: Type-safe Ethereum address (20 bytes)
TUri
Type: string literal type
Default: string
Purpose: Exact URI typing
Example: "https://example.com" vs string
TVersion
Type: string literal type
Default: string
Purpose: Version typing (always “1” currently)
TChainId
Type: number literal type
Default: number
Purpose: Exact chain ID typing
Example: 1 vs number
Field Types
Required Fields
domain : TDomain ; // DNS authority
address : TAddress ; // 20-byte Ethereum address
uri : TUri ; // RFC 3986 URI
version : TVersion ; // Current version "1"
chainId : TChainId ; // EIP-155 Chain ID
nonce : string ; // Min 8 chars, replay protection
issuedAt : string ; // ISO 8601 timestamp
Optional Fields
statement ?: string ; // Human-readable assertion
expirationTime ?: string ; // ISO 8601 expiration
notBefore ?: string ; // ISO 8601 not-before
requestId ?: string ; // System identifier
resources ?: string []; // Resource URIs
Type Examples
Basic Usage
import type { BrandedMessage } from 'tevm' ;
const message : BrandedMessage = {
domain: "example.com" ,
address: Address ( "0x..." ),
uri: "https://example.com" ,
version: "1" ,
chainId: 1 ,
nonce: "abc123def" ,
issuedAt: "2021-09-30T16:25:24.000Z" ,
};
Literal Type Preservation
const message = Siwe . create ({
domain: "example.com" as const ,
address: specificAddress ,
uri: "https://example.com" as const ,
chainId: 1 as const ,
});
// message.domain type: "example.com" (literal)
// message.chainId type: 1 (literal)
// message.version type: "1" (always literal)
With Optional Fields
const message : BrandedMessage = {
domain: "example.com" ,
address: userAddress ,
uri: "https://example.com" ,
version: "1" ,
chainId: 1 ,
nonce: "abc123" ,
issuedAt: "2021-09-30T16:25:24.000Z" ,
statement: "Sign in to Example" ,
expirationTime: "2021-10-01T16:25:24.000Z" ,
resources: [ "https://example.com/api" ],
};
Type-Safe Functions
function authenticateUser ( message : BrandedMessage ) : void {
// TypeScript ensures all required fields present
console . log ( `Auth request from ${ message . domain } ` );
console . log ( `Address: ${ Address . toHex ( message . address ) } ` );
console . log ( `Chain: ${ message . chainId } ` );
}
// Type error if missing required fields
authenticateUser ({ domain: "example.com" }); // Error
Namespace Pattern
BrandedSiwe follows namespace pattern for tree-shakeable methods:
export const BrandedSiwe = {
create ,
format ,
generateNonce ,
getMessageHash ,
parse ,
validate ,
verify ,
verifyMessage ,
};
Usage
import { BrandedSiwe } from 'tevm' ;
// Namespace access
const message = BrandedSiwe . create ({ ... });
const text = BrandedSiwe . format ( message );
const valid = BrandedSiwe . verify ( message , signature );
// Individual imports (tree-shakeable)
import { create , format , verify } from 'tevm/BrandedSiwe' ;
const message = create ({ ... });
const text = format ( message );
const valid = verify ( message , signature );
Type Guards
Runtime Type Checking
function isBrandedMessage ( value : unknown ) : value is BrandedMessage {
return (
typeof value === 'object' &&
value !== null &&
'domain' in value &&
'address' in value &&
'uri' in value &&
'version' in value &&
'chainId' in value &&
'nonce' in value &&
'issuedAt' in value
);
}
// Usage
if ( isBrandedMessage ( unknownValue )) {
// TypeScript knows it's BrandedMessage
const text = Siwe . format ( unknownValue );
}
Validation Type Guard
function isValidMessage ( message : BrandedMessage ) : message is BrandedMessage {
const result = Siwe . validate ( message );
return result . valid ;
}
// Usage
if ( isValidMessage ( message )) {
// Message structure validated
Siwe . verify ( message , signature );
}
Signature
type Signature = Uint8Array ;
65-byte ECDSA signature (r + s + v)
ValidationResult
type ValidationResult =
| { valid : true }
| { valid : false ; error : ValidationError };
Discriminated union for validation results
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 };
Discriminated union for error types
Type Safety Benefits
Compile-Time Checks
const message : BrandedMessage = {
domain: "example.com" ,
address: userAddress ,
// Error: Missing required fields
};
const complete : BrandedMessage = {
domain: "example.com" ,
address: userAddress ,
uri: "https://example.com" ,
version: "1" ,
chainId: 1 ,
nonce: "abc123" ,
issuedAt: "2021-09-30T16:25:24.000Z" ,
}; // OK
Inference
// Type inferred from create
const message = Siwe . create ({
domain: "example.com" ,
address: userAddress ,
uri: "https://example.com" ,
chainId: 1 ,
});
// message type: BrandedMessage<string, AddressType, string, "1", number>
// Function parameters infer type
function processMessage ( msg : BrandedMessage ) {
// msg.domain: string
// msg.chainId: number
// msg.version: string
}
Optional Field Handling
function hasExpiration ( message : BrandedMessage ) : boolean {
// Type narrowing with optional chaining
return message . expirationTime !== undefined ;
}
function getExpiration ( message : BrandedMessage ) : Date | undefined {
// Safe optional field access
return message . expirationTime
? new Date ( message . expirationTime )
: undefined ;
}
Pattern Details
Data-Based Architecture
All Siwe code follows data-based pattern:
Data: TypeScript interfaces (BrandedMessage)
Methods: Namespace functions operating on data
No classes: Functions take data as first argument
// Data
const message : BrandedMessage = { ... };
// Methods operate on data
const text = BrandedSiwe . format ( message );
const hash = BrandedSiwe . getMessageHash ( message );
const valid = BrandedSiwe . verify ( message , signature );
Tree-Shakeable
Individual functions can be imported:
import { create , format } from 'tevm/BrandedSiwe' ;
// Only create and format code included in bundle
Type Conventions
BrandedMessage - Message type
Signature - Signature type
ValidationResult - Result type
ValidationError - Error type
All follow Branded* or *Result naming.
Implementation Notes
Plain objects: No class instantiation overhead
Immutable operations: Functions don’t mutate inputs
Type-safe: Full TypeScript type checking
Serializable: JSON-compatible data structures
No brand field: Uses structural typing, no runtime brand
See Also