Ethernauta

EIP-6492 — pre-deploy signature wrapping

A counterfactual smart account can sign messages before its contract exists on-chain. EIP-6492 wraps such signatures with the CREATE2-factory deploy plan so an on-chain verifier can simulate the deploy first, then hand the inner signature to the deployed account's isValidSignature.

The wrap is:

abi.encode(factory, factoryData, signature) || MAGIC_BYTES

MAGIC_BYTES is a unique 32-byte tail (0x649264...) that distinguishes a 6492-wrapped signature from any ordinary 1271 / EOA blob.

Magic bytes0x6492649264926492649264926492649264926492649264926492649264926492
factory0x000000000000000000000000000000000000FA01
factoryData0xdeadbeefcafebabe
signature (inner)0x4242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242424242
is_wrapped_signature(plain)false
View on GitHub

The code

import "./demo.css"
import {
  type Address,
  AddressSchema,
  type Bytes,
  BytesSchema,
} from "@ethernauta/core"
import {
  is_wrapped_signature,
  MAGIC_BYTES,
  unwrap_signature,
  wrap_signature,
} from "@ethernauta/eip/6492"
import { useMemo, useState } from "react"
import { parse } from "valibot"
import { Button } from "../../components/button"

// Sample inputs — values are illustrative; in practice
// `factory` + `factoryData` come from the SCA's
// counterfactual deploy plan and `signature` is whatever
// the 1271 verifier of that account would accept once
// deployed.
const FACTORY: Address = parse(
  AddressSchema,
  "0x000000000000000000000000000000000000FA01",
)
const FACTORY_DATA: Bytes = parse(
  BytesSchema,
  "0xdeadbeefcafebabe",
)
const INNER_SIGNATURE: Bytes = parse(
  BytesSchema,
  `0x${"42".repeat(65)}`,
)

export function Verify6492Demo() {
  const [wrapped, set_wrapped] = useState<Bytes | null>(
    null,
  )
  const [unwrapped, set_unwrapped] = useState<{
    factory: Address
    factoryData: Bytes
    signature: Bytes
  } | null>(null)
  const [error, set_error] = useState<string | null>(null)

  const plain_is_wrapped = useMemo(
    () => is_wrapped_signature(INNER_SIGNATURE),
    [],
  )

  function do_wrap() {
    set_error(null)
    try {
      const result = wrap_signature({
        factory: FACTORY,
        factoryData: FACTORY_DATA,
        signature: INNER_SIGNATURE,
      })
      set_wrapped(result)
      const back = unwrap_signature(result)
      if (!back) {
        set_error("unwrap_signature returned null")
        return
      }
      set_unwrapped({
        factory: back.factory,
        factoryData: back.factoryData,
        signature: back.signature,
      })
    } catch (e) {
      set_error(e instanceof Error ? e.message : String(e))
    }
  }

  return (
    <div className="verify-6492-root">
      <Row label="Magic bytes" value={MAGIC_BYTES} mono />
      <Row label="factory" value={FACTORY} mono />
      <Row label="factoryData" value={FACTORY_DATA} mono />
      <Row
        label="signature (inner)"
        value={INNER_SIGNATURE}
        mono
      />
      <Row
        label="is_wrapped_signature(plain)"
        value={plain_is_wrapped ? "true" : "false"}
      />
      {wrapped && (
        <>
          <Row
            label="wrap_signature → "
            value={wrapped}
            mono
          />
          <Row
            label="is_wrapped_signature(wrapped)"
            value={
              is_wrapped_signature(wrapped)
                ? "true"
                : "false"
            }
          />
        </>
      )}
      {unwrapped && (
        <>
          <Row
            label="unwrap → factory"
            value={unwrapped.factory}
            mono
          />
          <Row
            label="unwrap → factoryData"
            value={unwrapped.factoryData}
            mono
          />
          <Row
            label="unwrap → signature"
            value={unwrapped.signature}
            mono
          />
        </>
      )}
      {error && (
        <p className="verify-6492-error">{error}</p>
      )}
      <div>
        <Button onClick={do_wrap}>Wrap + unwrap</Button>
      </div>
    </div>
  )
}

function Row({
  label,
  value,
  mono,
}: {
  label: string
  value: string
  mono?: boolean
}) {
  return (
    <div className="verify-6492-row">
      <span className="verify-6492-row-label">{label}</span>
      <span
        className={
          mono
            ? "verify-6492-row-value is-mono"
            : "verify-6492-row-value"
        }
      >
        {value}
      </span>
    </div>
  )
}

When to wrap

You only need this when the signer account is counterfactual — i.e. deterministic via CREATE2 but not deployed yet. After deployment the wrapper becomes unnecessary; plain 1271 verification suffices.