Encodes and decodes SIWX messages and SIGN-IN-WITH-X header values.
SIWX messages follow the SIWE (EIP-4361) textual format and use CAIP-2
chain identifiers for EVM networks (eip155:*).
Summary
Header Encoding
Decodes a SIWX SIGN-IN-WITH-X header.
Encodes a SIWX header payload with message and signature fields.
Returns the canonical SIWX header name.
Types
SIWX message payload fields.
Functions
Decodes an EIP-4361 SIWX message into payload fields.
Encodes a SIWX payload into an EIP-4361 message.
Header Encoding
@spec decode_header(String.t()) :: {:ok, map()} | {:error, header_decode_error()}
Decodes a SIWX SIGN-IN-WITH-X header.
Examples
iex> {:ok, encoded} = X402.Extensions.SIWX.encode_header(%{message: "hello", signature: "0xabc"})
iex> X402.Extensions.SIWX.decode_header(encoded)
{:ok, %{"message" => "hello", "signature" => "0xabc"}}
iex> X402.Extensions.SIWX.decode_header("%%")
{:error, :invalid_base64}
@spec encode_header(map()) :: {:ok, String.t()} | {:error, header_encode_error()}
Encodes a SIWX header payload with message and signature fields.
Examples
iex> {:ok, header} = X402.Extensions.SIWX.encode_header(%{message: "hello", signature: "0xabc"})
iex> {:ok, decoded} = X402.Extensions.SIWX.decode_header(header)
iex> decoded["message"]
"hello"
@spec header_name() :: String.t()
Returns the canonical SIWX header name.
Examples
iex> X402.Extensions.SIWX.header_name()
"SIGN-IN-WITH-X"
Types
@type decode_error() :: :invalid_message | {:invalid_field, atom()}
@type header_decode_error() :: :invalid_base64 | :invalid_json | :invalid_payload
@type header_encode_error() :: :invalid_payload | :invalid_json
@type message_payload() :: %{ domain: String.t(), address: String.t(), statement: String.t(), uri: String.t(), version: String.t(), chain_id: String.t(), nonce: String.t(), issued_at: String.t(), expiration_time: String.t() }
SIWX message payload fields.
Functions
@spec decode(String.t()) :: {:ok, message_payload()} | {:error, decode_error()}
Decodes an EIP-4361 SIWX message into payload fields.
Examples
iex> payload = %{
...> domain: "example.com",
...> address: "0x1111111111111111111111111111111111111111",
...> statement: "Access purchased content",
...> uri: "https://example.com/protected",
...> version: "1",
...> chain_id: "eip155:1",
...> nonce: "abc12345",
...> issued_at: "2026-02-16T12:00:00Z",
...> expiration_time: "2026-02-16T13:00:00Z"
...> }
iex> {:ok, message} = X402.Extensions.SIWX.encode(payload)
iex> X402.Extensions.SIWX.decode(message)
{:ok, payload}
@spec encode(map()) :: {:ok, String.t()} | {:error, encode_error()}
Encodes a SIWX payload into an EIP-4361 message.
:chain_id accepts either "eip155:<id>" or a positive integer.
Examples
iex> payload = %{
...> domain: "example.com",
...> address: "0x1111111111111111111111111111111111111111",
...> statement: "Access purchased content",
...> uri: "https://example.com/protected",
...> version: "1",
...> chain_id: "eip155:1",
...> nonce: "abc12345",
...> issued_at: "2026-02-16T12:00:00Z",
...> expiration_time: "2026-02-16T13:00:00Z"
...> }
iex> {:ok, message} = X402.Extensions.SIWX.encode(payload)
iex> is_binary(message)
true