Basics
Inside a key: how MERKI signs
How MERKI builds one-time keys from hash chains, signs an ERC-4337 operation, retires the key on chain and recovers from a stuck operation.
Every MERKI operation carries a signature from a key built out of hash chains. In a single step the keyhold accepts it, retires the signer and records its successor, and the address never moves. A key counts as spent even when the operation's calls fail afterwards.
1. A key is 67 hash chains
Start from a secret 32-byte value and hash it fifteen times. Position 0 is secret, position 15 is public, and anybody can step forward along it, never back. A MERKI key has 67 such chains plus a public 32-byte seed. Each step hashes the seed, the chain number and the step number together with the value, so no two steps anywhere ever hash the same input:
step(seed, i, j, x) = keccak256(seed ‖ uint256(i·256 + j) ‖ x)The key hash the keyhold stores is keccak256(seed ‖ end₀ ‖ … ‖ end₆₆).
2. Signing reveals one point per chain
The message is hashed to 32 bytes and read as 64 digits from 0 to 15. For each digit the signer publishes whatever value sits at that spot on its chain. Three more digits encode a checksum, the sum of 15 − digit over the 64 message digits. Raising any message digit lowers the checksum, and lowering a checksum digit would mean walking a chain backwards, so published values can never be stretched to cover some other message.
| Parameter | Value |
|---|---|
| Hash | Keccak-256 |
| Digit base | 16, one hex digit per chain |
| Chains in one key | 67: 64 carry the message, 3 the checksum |
| Normal signature | 2,209 bytes: mode byte, next key hash, seed, 67 values |
| Recovery signature | 2,241 bytes: adds the next recovery key hash |
| Worst-case hashing to check | 1,005 calls (67 × 15) |
3. One key, one signature
Each signature gives away points on every chain. Given two signatures from one key, someone could mix the higher points of both and possibly produce a valid signature for something else. Hence one signature per key, and the wallet derives an unending line of keys from your phrase: key(phrase, chain id, role, index). The chain id is part of it, so the same phrase never produces the same keys on two networks.
4. Each acceptance advances the key
- The wallet reads the keyhold's current key index and nonce from the chain and compares them with its own journal of used keys.
- It builds the operation and signs
keccak256(tag, userOpHash, next key hash)with key #n. The userOpHash already covers the chain id, the EntryPoint and every field of the operation. - The EntryPoint calls the keyhold's validation. The keyhold rebuilds a key hash out of the signature and checks it against the one on record.
- When they agree it records key #n+1, bumps the index and logs
KeyRotated; key #n is finished for good.
5. Replays fail
| Attempt | Result |
|---|---|
| Send an accepted operation again | Nonce already consumed: AA25 |
| Reuse an old signature on a new operation | Digest mismatch: AA24 |
| Sign with a retired key | Stored key has moved on: AA24 |
| Swap the next key named in a signature | The digest covers it (AA24) |
| Flip any bit of a signature | Refused; fuzz-tested over every byte |
6. When an operation gets stuck
Suppose a signed operation never makes it into a block. Signing it once more would mean a second signature from that key. So your phrase also yields a second line of keys used only for rescue: one of them authorises a single substitute operation that reuses the stuck nonce. Just one of the pair can ever be included. Once the substitute is in, the keyhold jumps past the possibly exposed main key, records fresh keys on both lines and logs KeyRecovered. The two signature kinds differ in length and domain tag, so neither can impersonate the other.
7. The first operation deploys the keyhold
CREATE2 fixes your address from the forge plus the hashes of your opening key pair, so the address exists before any contract does. Fund it whenever you like. Your first operation carries the deployment in its initCode and spends key #0 within that same transaction.
The exact byte layout is on Contracts & signer.