Chipset / what opeq is
A wallet that stays safe
opeq is a Solana program plus a client. Your funds sit in an address that has no private key at all. The program moves them only after it checks a hash-based signature.
- A normal address
- Receive SOL, hold SPL and Token-2022 tokens, share it like any other address. Other programs see an ordinary signer.
- Works with any app
- One signature authorizes a list of instructions to any programs. Tested with transfers, tokens, token accounts, Memo and Raydium swaps.
- Same address forever
- One key tree signs 1008 transactions. A rekey moves to a fresh tree. Your address, balances and token accounts never change.
- 24 recovery words
- Standard BIP39 words. Every key tree is derived from them, so the words stay valid after any number of rekeys.
- No protocol change
- Runs on Solana as it is today. No new chain, no fork, no waiting for a network upgrade.
- Quantum-safe upgrades
- The program's own upgrade key is an opeq wallet. Even the code can't be changed with a broken Ed25519 key.
CMOS / the one rule
The one rule: never reuse a key
Two different signatures from one key let an attacker forge a third. opeq is built around never letting that happen.
- Counter first
- The next key is reserved and saved before anything is signed. A crash just skips a key.
- One tab at a time
- A browser lock stops two tabs from signing with the same wallet at once.
- Tamper check
- The wallet record is authenticated with your password. Rolling back the counter or swapping the program address is detected.
- Failed means burned
- If a transaction fails, the client burns its key with a no-op so the failed signature can never run later.
- Expiry
- Every signature names a slot about a minute ahead. The program refuses anything later than 300 slots.
- Recovery skips ahead
- Restoring from words reads the on-chain counter and skips 64 keys, in case a lost device signed something that never landed.
TPM / security
What is protected
A clear threat model, the limits we know about, and what two internal reviews found.
- Protected
- Funds and tokens in your opeq wallet. Every action needs a hash-based signature over the exact instructions. The program's upgrade key is also an opeq wallet.
- Not protected by opeq
- The Ed25519 key that pays fees (keep little SOL in it). Solana's validators and consensus, which use classical cryptography and need their own migration.
Security level
| Classical security per one-time key | 128 bits |
| Quantum security (Grover), NIST category 1, like SLH-DSA-128 | 64 bits |
| Multi-target speedup (every hash call has a unique address tweak) | none |
| Signature expiry, enforced on chain | at most 300 slots |
| The one rule | Never sign twice with one key. The client enforces it, the chain refuses a reused key. |
Internal reviews
Two independent adversarial reviews looked at the program, the protocol, the cryptography and the client, and tried to prove every finding with working attack code. Everything they found is fixed and covered by tests.
| Severity | Finding | Fix |
| critical | Two client runs at the same time could sign with the same key | File and tab locks, counter saved before signing |
| high | The wallet locked forever once all 1024 keys were used | Last 16 keys reserved for rekey only |
| high | A tampered wallet file could roll back the counter | Whole-record authentication with the password |
| high | A retried rekey could orphan the new tree | Trees derived from the master seed, rekey state recovered from chain |
| high | Two paths to one wallet file could bypass the lock | Lock on the real path, not its spelling |
| medium | Rekey could return to the current tree | Rejected on chain (error 22) |
| medium | Message hash had no secret randomizer | 16 random bytes hashed first, as in SLH-DSA |
| medium | An interrupted send could run twice on retry | In-flight record kept until the chain settles it |
| low | A failed transaction could be replayed until it expired | Failed keys are burned |
Not externally audited. Internal reviews are not a substitute for a professional audit. Do not move real funds to opeq until an external audit is published.
RAM / specification
The details
Everything needed to write an independent client or verifier.
Parameters
- one-time signature
- WOTS+, n = 16 bytes, w = 256, 16 message + 2 checksum chains
- signature size
- 288 B (18 x 16)
- merkle tree
- height 10, 1024 leaves, auth path 160 Bleaves 0..1007 sign actions, 1008..1023 only rekeys
- tweakable hash
- F(x) = SHA256(pub_seed16 || ADRS8 || x)[0..16]
- ADRS (8 bytes)
- type u8 | a u8 | b u8 | 0 | index u32le
- hash types
- 0 chain (a = chain, b = position, index = leaf)1 leaf (index = leaf)2 node (a = height, index = node)3 secret (client only)
- checksum
- sum(255 - d) over 16 digits, as 2 base-256 digits (max 4080)
- tree seeds
- sk = SHA256("opeq/tree/sk/v1" || master || epoch u32le)pub = SHA256("opeq/tree/pub/v1" || master || epoch u32le)[0..16]
- master seed
- 32 bytes, shown as 24 BIP39 English words
- account id
- SHA256("opeq/id/v1" || pub_seed of tree 0 || root of tree 0)
- addresses (PDAs)
- state = ["s", account id], wallet = ["w", account id]
- expiry
- named by the signer, at most 300 slots ahead, checked on chain
Signed message
digest = SHA256(
R (16 random bytes)
|| "opeq/exec/v1" || program id || account id
|| epoch u32 || leaf u32 || expiry slot u64
|| count u8
|| for each inner instruction:
program id || meta count u8
|| for each account:
pubkey || flags u8
(flags: 1 = signer, 2 = writable)
|| data length u16 || data
|| salt u32
)[0..16]
Rekey uses "opeq/rekey/v1" and hashes the new public seed and new root instead of instructions. The client tries 217 salts and keeps the one with the fewest verification steps.
Instructions and state
0 init tag | pub_seed 16 | root 16 | lamports u64
payer (s, w), state (w), wallet, system
1 exec tag | leaf u32 | expiry u64 | salt u32 | R 16
| sig 288 | path 160 | count u8 | inner...
state (w), wallet (w), every account used
2 rekey tag | leaf u32 | expiry u64 | salt u32 | R 16
| sig 288 | path 160 | new pub_seed 16 | new root 16
state (w), wallet
state account, 88 bytes
0 magic "OPEQST01" 40 pub_seed (16)
8 account id (32) 56 root (16)
72 next leaf u32 76 epoch u32
80 state bump, wallet bump, 6 zero bytes
costs
SOL transfer about 160,000 CU
Raydium swap about 200,000 CU
base fee 5000 lamports
transfer ix data 502 bytes of 1232
Error codes
1bad instruction13bad signature2too many accounts14state not writable3state not owned by program15init address mismatch4bad state account16too many slices5wallet/state mismatch17account index out of range6signature expired18payer must sign7leaf already used19already initialized8leaf out of range20bad system program9self cpi21too many metas10bad meta flags22rekey to the current root11signer escalation23expiry too far in the future12malformed inner instructions
Power / built from scratch
Built from scratch
The program is written in sBPF v3 assembly through a hand-made Python assembler. There is no Rust, LLVM or Solana CLI anywhere. A custom sBPF emulator runs the program locally with Solana's compute-unit prices. Its count matched the live network to within 0.05%. Every change is checked against a reference model with differential fuzzing.
This website is plain JavaScript with no dependencies: SHA-256, WOTS+, BIP39, base58, program addresses and the transaction format are all written by hand. It loads nothing from third parties. The block hashes on this page are computed by the same SHA-256 the wallet uses.
Where this goes
done
- On-chain program
- Any-program CPI, tokens, Raydium swap
- Rekey, quantum-safe upgrades
- 24 recovery words
- Two internal security reviews
- Command line client
- Web app with tokens and swaps
- On-chain expiry cap (rev 1.1)
next
- External security audit
- Jupiter routing for mainnet swaps
- TypeScript SDK
- Upgrade governance: immutable or multisig with timelock
- Mainnet
later
- Integrations with existing wallets
- Falcon signatures once SIMD-0416 lands
- Mobile