ERC-6492 signature wrapper parsing and building.
ERC-6492 lets a not-yet-deployed ("counterfactual") smart wallet produce a verifiable signature by wrapping the wallet's inner signature together with the factory call that would deploy it:
abi.encode((factory, factoryCalldata, innerSignature)) || magic_suffixwhere the magic suffix is the 32-byte value 0x6492...6492.
This module is pure binary handling — no cryptography and no RPC. Whether a
wrapped signature is actually valid is decided by X402.Verify.EVM:
a counterfactual signature is never accepted until an on-chain simulation
proves the deployed wallet would accept it (fail-closed, mirroring the
reference Go implementation).
Summary
Functions
Returns the 32-byte ERC-6492 magic suffix.
Parses a signature, unwrapping the ERC-6492 envelope when present.
Builds an ERC-6492 wrapped signature from its parts.
Returns true when the signature carries the ERC-6492 magic suffix.
Types
@type parsed() :: %{ wrapped?: boolean(), factory: String.t() | nil, factory_calldata: binary() | nil, inner_signature: binary() }
A parsed signature.
wrapped? is true when the ERC-6492 magic suffix was present. factory
and factory_calldata are nil unless the wrapper carried deployment
information (a non-zero factory address with non-empty calldata).
inner_signature is the raw signature bytes — the unwrapped inner
signature for wrapped input, the input itself otherwise.
Functions
@spec magic_suffix() :: <<_::256>>
Returns the 32-byte ERC-6492 magic suffix.
Examples
iex> byte_size(X402.ERC6492.magic_suffix())
32
Parses a signature, unwrapping the ERC-6492 envelope when present.
Accepts raw bytes or a 0x-prefixed hex string. Signatures without the
magic suffix parse as wrapped?: false with the bytes passed through as
inner_signature. A wrapper whose factory is the zero address or whose
calldata is empty parses with factory: nil (no deployment information),
matching the reference implementations.
Returns {:error, :invalid_erc6492_wrapper} when the magic suffix is
present but the ABI-encoded prefix is malformed, and
{:error, :invalid_signature} for non-hex string input.
Examples
iex> {:ok, parsed} = X402.ERC6492.parse(:binary.copy(<<0x01>>, 65))
iex> {parsed.wrapped?, parsed.factory, byte_size(parsed.inner_signature)}
{false, nil, 65}
iex> inner = :binary.copy(<<0x07>>, 65)
iex> {:ok, wrapped} =
...> X402.ERC6492.wrap("0x2222222222222222222222222222222222222222", <<0xAB, 0xCD>>, inner)
iex> {:ok, parsed} = X402.ERC6492.parse(wrapped)
iex> {parsed.wrapped?, parsed.factory, parsed.factory_calldata, parsed.inner_signature}
{true, "0x2222222222222222222222222222222222222222", <<0xAB, 0xCD>>, inner}
iex> X402.ERC6492.parse("0xzz")
{:error, :invalid_signature}
Builds an ERC-6492 wrapped signature from its parts.
factory is a 0x-prefixed EVM address; factory_calldata and
inner_signature are raw bytes. Returns the raw wrapped bytes
(abi.encode((address, bytes, bytes)) || magic_suffix).
Examples
iex> {:ok, wrapped} =
...> X402.ERC6492.wrap(
...> "0x2222222222222222222222222222222222222222",
...> <<0xAB>>,
...> :binary.copy(<<0x01>>, 65)
...> )
iex> X402.ERC6492.wrapped?(wrapped)
true
iex> X402.ERC6492.wrap("0x123", <<>>, <<>>)
{:error, :invalid_address}
Returns true when the signature carries the ERC-6492 magic suffix.
Accepts raw bytes or a 0x-prefixed hex string.
Examples
iex> X402.ERC6492.wrapped?(<<1, 2, 3>>)
false
iex> {:ok, wrapped} =
...> X402.ERC6492.wrap(
...> "0x2222222222222222222222222222222222222222",
...> <<0xAB>>,
...> :binary.copy(<<0x01>>, 65)
...> )
iex> X402.ERC6492.wrapped?(wrapped)
true