Docs · Architecture▾
Build

Architecture

Winternitz is a monorepo with one on-chain layer, one shared TypeScript SDK, and three interfaces built on top of it.

Interfaces

Web wallet

web/, Next.js

Chrome extension

extension/, Manifest V3

CLI

cli/, Node.js

↓ all built on ↓

SDK in sdk/ (TypeScript and viem)

WOTS

key generation, signing, verification

Wallet client

key journal and rotation guard

Submitters

bundler or self-relay

↓ talks to ↓

On-chain in contracts/ (Solidity)

QuantumSafeAccount

ERC-4337 account

WOTS.sol

signature verifier

Account factory

CREATE2 clones

Repository layout#

winternitz/
├── contracts/   Solidity + Foundry: WOTS verifier, account, factory, tests, deploy script
├── sdk/         TypeScript: WOTS, wallet client, key journal, submitters, networks, tokens
├── cli/         Command-line wallet (Node 24 runs the TypeScript directly)
├── extension/   Chrome extension, Manifest V3 (esbuild + Tailwind)
├── web/         Next.js: landing page, web wallet (/wallet), docs (/docs)
└── docs/        Design notes and benchmark report
ChoiceUsed for
Solidity 0.8.28, FoundryContracts, unit, fuzz and differential tests
TypeScript, viemSDK, CLI, extension and web wallet
ERC-4337, EntryPoint v0.7Account standard (canonical 0x0000000071727De22E5E9d8BAf0edAc6f37da032)
Alto (local), Alchemy (Robinhood Chain)Bundlers
Robinhood Chain, Ethereum Sepolia, Base Sepolia, Arbitrum SepoliaNetworks
Next.js 16, React 19Web

On-chain#

  • WOTS.sol is a library that walks each hash chain from the signature to its end and hashes the 67 results into a public key hash. It is written in assembly to keep verification around 80k gas.
  • QuantumSafeAccount is the ERC-4337 account. It stores publicKeyHash and keyIndex, validates signatures with WOTS.sol, rotates the key on success, and executes single or batched calls. It has no owner and no direct-call path: only the EntryPoint (or the account itself) can make it act.
  • QuantumSafeAccountFactory deploys accounts as minimal proxies (EIP-1167) at CREATE2 addresses derived from the first public key hash. The address is known before deployment and cannot be claimed by anyone else.

Off-chain#

  • WOTS module: key derivation from the seed, signing, verification; byte-for-byte identical to the Solidity verifier (checked by differential tests).
  • Wallet client: builds UserOperations, reads the key index from the chain before every signature, and keeps a write-ahead journal of every signature so a key can never sign twice.
  • Submitters: send through an ERC-4337 bundler, or "self-relay" by calling handleOps from an ordinary account that is refunded by the EntryPoint.
  • Networks and tokens: chain registry (Robinhood Chain testnet and mainnet, Sepolia, local) and the verified token lists.

One transaction, end to end#

Your device

derives one-time key #n from the seed

signs↓

Bundler / relayer

ERC-4337

handleOps↓

EntryPoint v0.7

canonical

validate↓

Your account

checks key #n, then stores #n+1

Each transaction uses one key, and the account rotates to the next one by itself
  1. The interface asks the wallet client to send a set of calls.
  2. The client reads keyIndex and publicKeyHash from the account (or uses key #0 if it isn't deployed yet).
  3. It builds the UserOperation, gets a gas estimate, and on Arbitrum chains adds the L1 data fee.
  4. It records the operation in the journal, then signs with key #n while committing to key #n+1.
  5. The bundler or relayer submits it; the EntryPoint calls validateUserOp, which verifies and rotates the key; then the calls execute.
  6. The client confirms the new key index on-chain and marks the journal entry as confirmed.

Robinhood Chain specifics#

Robinhood Chain is an Arbitrum Orbit chain. The contracts need no changes, but three things are handled off-chain:

  • L1 data fee. Arbitrum charges the cost of posting data to Ethereum as extra gas on the bundle. The SDK queries Arbitrum's NodeInterface for it and includes it in preVerificationGas.
  • Incompressible estimate. The fee is based on compressed data, and a real WOTS signature doesn't compress. Gas estimates therefore use a pseudo-random placeholder signature; a constant one would under-estimate by roughly 89k gas.
  • Bundler pricing. Alchemy's bundler requires a minimum priority fee, which the SDK reads with rundler_maxPriorityFeePerGas.