Wallet integration

Integrate the Unlukey client SDK into a wallet to warn users when their recovery phrase was generated by a known weak seed-generation vulnerability.

The SDK is published in the coinspect/unlukey repository.

How the check works

  1. The wallet derives the BIP-39 entropy from the recovery phrase.
  2. The client SDK hashes the entropy with SHA-256 and takes a short prefix of the resulting hash.
  3. It fetches only the dataset bucket matching that prefix from static hosting.
  4. It compares the remaining hash bytes against the entries in that bucket, locally.

The prefix groups many possible keys into the same bucket (k-anonymity). The static hosting provider only sees which bucket was requested, never the full key being checked or whether it matched.

Datasets are split by entropy length: one dataset for 128-bit (12-word) entropy and one for 256-bit (24-word) entropy. The client selects the right one automatically.

Install

npm install @unlukey/client

(Until the package is published, install directly from the repo: npm install github:coinspect/unlukey#path:client.)

Basic usage

import { check } from '@unlukey/client'
import { mnemonicToEntropy } from '@scure/bip39'
import { wordlist } from '@scure/bip39/wordlists/english'

const entropy = mnemonicToEntropy(secretRecoveryPhrase, wordlist)

const { vulnerable } = await check(entropy)

if (vulnerable) {
  // Warn the user: this recovery phrase matches a known weak seed and
  // funds should be moved to a newly generated wallet.
}

The lookup logic runs client-side. The recovery phrase and entropy never leave the device; only a prefix of the entropy hash is sent to the dataset host. entropy is a Uint8Array and must be either:

  • 16 bytes, for 12-word mnemonics, or
  • 32 bytes, for 24-word mnemonics.

Handling the result

check() resolves to { vulnerable, key, prefix, bucketPath, reason? }:

  • vulnerable: true — the entropy matches a known weak-seed candidate. Prompt the user to move funds to a new wallet generated with secure randomness.

  • vulnerable: false — no match was found. This does not prove the recovery phrase is safe; it only means it does not match any candidate in Unlukey’s current datasets. Coverage is expanding; see the roadmap.

reason provides diagnostic information for debugging integrations. It does not change the meaning of vulnerable.

When to run the check

Run the check when a user imports an existing recovery phrase, once the wallet has derived the BIP-39 entropy.

Custom hosting

By default, the client fetches buckets from Unlukey’s public dataset host. Pass a different base URL to check() to use a different dataset host, such as your own CDN:

const result = await check(
  entropy,
  'https://your-cdn.example.com/unlukey-datasets'
)

For full control over how dataset buckets are fetched, use checkWith(entropy, readBucket) and supply your own readBucket(bits, prefixHex) implementation. See client/index.js in the SDK repo.

Testing locally

The SDK repo’s CLI is useful for testing the integration without wiring up UI:

node client/cli.js --remote 72039ecb02a3c880d4249e4b206933945a4f3a76ebaab26fa14e47a24e0f907e
# VULNERABLE

See the SDK repo’s README for building a local sample dataset and testing the client against it.