Your Ethereum address has a face. Catch wrong recipients before you sign.

When signing an Ethereum transaction on a Ledger hardware wallet, users are shown a long hex address — a string that is nearly impossible to verify at a glance. A single character swap from a clipboard hijack or address poisoning attack goes unnoticed.
This project adds a deterministic pixel-art avatar to the Ledger Flex/Stax confirmation screen. Every Ethereum address maps to a unique face: background tone, skin tone, hair style and colour, eye colour, eyebrows, nose, mouth, and optional facial hair are all derived from the address using an FNV-1a hash and a reproducible PRNG. The same address always produces the same face — a different address always looks different.
The avatar is generated entirely in C with no floating-point arithmetic, no heap allocation, and no external dependencies, making it suitable for the constrained BOLOS environment on Ledger devices. A companion WebAssembly build (compiled from the exact same C source) lets any web app render the matching avatar, so users can learn to recognise the face of their own wallet or a known counterparty before ever opening the hardware wallet.
The result is a second, human-readable layer of address verification that works with how people actually recognise things — faces, not hex strings.
The core is a pure C implementation (eth_face.c) that runs directly on the Ledger device under BOLOS, the bare-metal OS used by Ledger hardware wallets. It has zero heap allocation, zero floating-point, and only depends on <string.h> for memset — constraints imposed by the embedded environment.
Address-to-face mapping works in three stages. First, FNV-1a hashes the 40 lowercase hex characters of the address into a 32-bit seed. Second, an xorshift-multiply PRNG (seeded from that hash) drives all feature selection — skin tone, background level, hair style and colour, eye colour, eyebrow shape, nose type, mouth expression, and optional facial hair. Third, a 64×64 grayscale canvas is built using integer-only ellipse, rectangle, and line primitives. Oval rasterisation uses Newton's method for integer square roots — no <math.h> required.
The trickiest part was making the browser preview pixel-identical to the device. A naive JavaScript port introduced subtle divergences: floating-point rounding in the uniform integer sampler (Math.floor(rng() * N) vs C's 64-bit multiply-shift), off-by-one probability thresholds, and differing integer square root results. The solution was to compile eth_face.c directly to WebAssembly using clang's wasm32 target with a minimal libc shim (just memset), with no Emscripten runtime. The resulting WASM is ~11 KB, base64-encoded and embedded directly in a single self-contained HTML file — no server, no build step for the end user.

