Add grindKeyPair and grindKeyPairSigner for mining vanity addresses#1534
Conversation
🦋 Changeset detectedLatest commit: 206f03e The changes in this PR will be included in the next version bump. This PR includes changesets to release 46 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
BundleMonFiles updated (13)
Unchanged files (129)
Total files change +3.45KB +0.71% Final result: ✅ View report in BundleMon website ➡️ |
|
Re:
None of these are blockers; the current implementation correctly rejects the common typos it's designed to catch. Just flagging for visibility. EDIT (Loris): Hmm I'm pretty sure the regex is greedy by default and therefore it will eat the whole group and everything inside of it. |
trevor-cortex
left a comment
There was a problem hiding this comment.
Summary
This PR adds grindKeyPair/grindKeyPairs in @solana/keys and grindKeyPairSigner/grindKeyPairSigners in @solana/signers for mining vanity Solana addresses. The implementation generates key pairs in parallel batches of concurrency (default 32), tests each candidate against a RegExp or predicate, and supports abortSignal-based cancellation via getAbortablePromise so the grind rejects immediately without waiting for in-flight generations. Regex matchers are statically validated against the base58 alphabet up front (after stripping escapes, character classes, quantifiers, and groups) to catch typos like /^sol0/, surfaced via a new SOLANA_ERROR__KEYS__INVALID_BASE58_IN_GRIND_REGEX error code. The signer variants are thin wrappers around the key-pair variants that pipe results through createSignerFromKeyPair.
Things to watch
- Regex validation soundness. The strip-then-check approach in
assertGrindRegexIsValidis pragmatic but lossy. I've flagged a few specific cases inline (notably nested groups and/\0/-style escapes whose semantics can never match base58). No hard correctness bugs that I can construct, but the comment "cannot be analyzed reliably without a full regex parser" is accurate — reviewers should decide if they're comfortable with the current trade-offs. - Redundant address derivation in the signer path.
grindKeyPairsexports + base58-decodes every candidate's public key during matching, and thencreateSignerFromKeyPaircallsgetAddressFromPublicKeyagain for each winning key pair. Minor; mentioned inline. - Test flakiness. The
grindKeyPairtests that use the realgenerateKeyPairwith/^1/are probabilistic (~1/58 per key). Withconcurrency32 this should virtually always finish within Jest's default timeout, but it's worth noting. concurrencyovershoot. A request foramount: 1with defaultconcurrency: 32always runs a full batch, so the"generates key pairs in batches of concurrency"test is correct — but callers targeting rare patterns with very smallamountpay for a full batch even after a match is found mid-batch. Documented behaviour, just flagging it.
Notes for subsequent reviewers
- Please double-check the regex-stripping logic against your own pathological inputs — especially nested groups, escaped brackets/parens (
/\[0\]/), and things like/\d/(currently skipped via the escape rule, which is the right call but worth confirming). - Confirm
crypto.subtle.exportKey('raw', publicKey)works for Ed25519 on all supported runtimes. Public keys are always extractable regardless of theextractableflag, so this should be fine, but the code path is new. - The new
@solana/promisesworkspace dep inpackages/keys/package.jsonshould be sanity-checked against the overall dependency graph policy for@solana/keys. - Docs look thorough and the examples match the API surface. Changeset is correct (minor bump on three packages).
|
Minor perf note on
For |
|
Re: The other tests correctly mock |
|
Documentation Preview: https://kit-docs-k0ngr7lmx-anza-tech.vercel.app |
mcintyre94
left a comment
There was a problem hiding this comment.
Looks good! Not sure if it'd just be worth a comment in the tests to keep matchers very simple to avoid flakiness there. Makes sense that WebCrypto can't be seeded and we shouldn't make the tests do something else just to make them deterministic.
| // - `[...]` character classes | ||
| // - `{...}` quantifiers | ||
| // - `(...)` groups | ||
| const STRIP_UNVALIDATED_REGEX_PARTS = /\\.|\[[^\]]*\]|\{[^}]*\}|\([^)]*\)/g; |
There was a problem hiding this comment.
Yo dawg, I heard you like regex 😅
There was a problem hiding this comment.
Imma regex ya regex! 😅
d0e164a to
ef02578
Compare
This PR adds `grindKeyPair`, `grindKeyPairs`, `grindKeyPairSigner`, and `grindKeyPairSigners` for mining vanity Solana addresses whose base58-encoded public key matches a `RegExp` or a custom predicate. Key pairs are generated in parallel batches of `concurrency` (defaulting to `32`), with each batch also exporting and base58-decoding the public key bytes concurrently so the matching phase is fully synchronous. Cancellation is handled via `getAbortablePromise` so that firing the `abortSignal` rejects the grind immediately without waiting for in-flight key generations to settle. Regex matchers have their literal characters validated against the base58 alphabet up front — after stripping escapes, character classes, quantifiers, and groups — to catch common typos like `/^sol0/` before an infinite grind starts, with the new `SOLANA_ERROR__KEYS__INVALID_BASE58_IN_GRIND_REGEX` error code. The signer variants are thin wrappers around the key-pair variants that pipe results through `createSignerFromKeyPair`.
ef02578 to
206f03e
Compare
| // Some tests in this file exercise the real `generateKeyPair` implementation, | ||
| // which uses WebCrypto under the hood and cannot be seeded. As a result, the | ||
| // addresses produced are genuinely random. To keep these tests fast and | ||
| // deterministic (i.e. non-flaky), any test that does NOT mock | ||
| // `generateKeyPair` must use an extremely permissive matcher — typically a | ||
| // single-character regex prefix like `/^1/` or a trivial `() => true` | ||
| // predicate — so that the grind loop converges within one batch. Do not | ||
| // tighten these matchers: if you need to assert more specific behaviour, | ||
| // mock `generateKeyPair` the same way the forwarding tests below do. |
Merge activity
|
|
🔎💬 Inkeep AI search and chat service is syncing content for source 'Solana Kit Docs' |
|
Because there has been no activity on this PR for 14 days since it was merged, it has been automatically locked. Please open a new issue if it requires a follow up. |

This PR adds
grindKeyPair,grindKeyPairs,grindKeyPairSigner, andgrindKeyPairSignersfor mining vanity Solana addresses whose base58-encoded public key matches aRegExpor a custom predicate. Key pairs are generated in parallel batches ofconcurrency(defaulting to32), with each batch also exporting and base58-decoding the public key bytes concurrently so the matching phase is fully synchronous. Cancellation is handled viagetAbortablePromiseso that firing theabortSignalrejects the grind immediately without waiting for in-flight key generations to settle. Regex matchers have their literal characters validated against the base58 alphabet up front — after stripping escapes, character classes, quantifiers, and groups — to catch common typos like/^sol0/before an infinite grind starts, with the newSOLANA_ERROR__KEYS__INVALID_BASE58_IN_GRIND_REGEXerror code. The signer variants are thin wrappers around the key-pair variants that pipe results throughcreateSignerFromKeyPair.