EIPs

EIP-4361 — Sign-In with Ethereum

eips.ethereum.org/EIPS/eip-4361

SIWE. An off-chain authentication message that binds a wallet signature to a specific domain, URI, chain, nonce, and timestamp. Used by apps that want “log in with your wallet” semantics without running an Ethereum tx.

import { build_siwe_message, generate_siwe_nonce } from "@ethernauta/eip/4361";

const message = build_siwe_message({
  domain: "example.com",
  address: signer_address,
  uri: "https://example.com",
  version: "1",
  chain_id: 1,
  nonce: generate_siwe_nonce(),
  issued_at: new Date().toISOString(),
});

Surface

ExportTypePurpose
build_siwe_message(fields) => stringCompose the canonical SIWE string.
parse_siwe_message(message: string) => SiweMessageParse a SIWE string back into fields.
is_siwe_message(message: string) => booleanPredicate.
generate_siwe_nonce() => stringCryptographically random nonce.
SiweMessagetypeParsed SIWE fields.
siweMessageSchemaValibot schemaValidate parsed input.

Signing and verifying

Signing is just personal_sign over the SIWE string:

import { personal_sign } from "@ethernauta/eip/191";

const signature = await personal_sign({
  account,
  message,
})(signer({ chain_id: eip155_1.chain_id }));

Verification uses the SIWE-specific verifier (it cross-checks the embedded fields):

import { verify_siwe_message } from "@ethernauta/crypto";

const result = await verify_siwe_message({
  message,
  signature,
  domain: "example.com",
  nonce: expected_nonce,
})(reader({ chain_id: eip155_1.chain_id }));

if (!result.valid) {
  switch (result.reason) {
    case "expired":
      // ...
    case "wrong_domain":
      // ...
  }
}

VerifySiweMessageFailureReason enumerates every typed rejection mode.

See also