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
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.