Getting Started
Why this exists
The Bitcoin puzzle transaction. The WarpWallet challenges. Peter Todd's hash collision bounties. Zden's image puzzles. Each one has a thread somewhere with the addresses, the prizes, who solved what and when. And every scanner, tracker and bot re-types that data from the thread. Slightly differently each time.
So it lives here once, as code. A puzzle is a TypeScript record built by a factory for its chain. A collection is a list of them. The type checker reads the data before a test does. The library, the CLI, the MCP server and the Pi and OMP extensions read one registry. Fix a record once and it lands everywhere.
Install
pnpm add @agntn/puzzles
Node.js 24 or newer for the CLI. The library is neutral ESM on the Fetch API, so browsers and edge workers run it too. The playground is it, running in your tab.
First call
import { get, collectionKeys, hasCollection } from "@agntn/puzzles";
collectionKeys(); // ["arweave", "b1000", ...] straight from the manifest, nothing loaded
hasCollection("peter_todd"); // true, a historical alias for hash_collision
const puzzle = await get("b1000/71"); // imports the b1000 module and nothing else
puzzle?.address().value; // "1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU"
puzzle?.status(); // "unsolved"
puzzle?.prize(); // 7.100226
puzzle?.prizeCurrency(); // "BTC"
puzzle?.keyRange(); // [2n ** 70n, 2n ** 71n - 1n]
puzzle?.hasPubkey(); // false, the address has never spent
get(id) loads the one collection the identifier names. Anything it doesn't know comes back as undefined. Want an exception instead? requirePuzzle(id) throws PuzzleNotFoundError. Identifiers are collection/name. The two singletons, gsmg and bitaps, are just the key.
Importing the package evaluates no puzzle records. The registry is a manifest of ten keys. The aggregate views are asynchronous because they load on first use:
import { all, stats } from "@agntn/puzzles";
(await all()).length; // 332, every collection loaded once
(await stats()).unsolved; // 93
One collection
A collection is its own entry with its class and its canonical instance. Once you have it, everything on it is synchronous:
import { b1000 } from "@agntn/puzzles/collections/b1000";
b1000.count(); // 256
b1000.require(90).address().value; // NumericCollection takes the number
b1000.unsolved().length; // 77
b1000.author.name; // "saatoshi_rising"
What is on a puzzle
| Method | Answers |
|---|---|
id(), collection(), name() | b1000/71, b1000, 71 |
chain(), address() | The chain key and { value, kind, hash160?, redeem_script? } |
status() | unsolved, solved, claimed, swept or expired. Written down, never derived |
prize(), currency(), prizeCurrency() | The amount, the currency when it isn't the native token, and the one to print |
pubkey(), hasPubkey() | The public key once the address exposed one |
key(), keyData(), hasPrivateKey() | The key builder, its serialized record, and whether any private key representation is known |
keyRange() | [2^(bits-1), 2^bits - 1] when the record declares a search width |
transactions(), fundingTransaction(), claimTransaction() | What happened on chain, in order |
startedAt(), solvedAt(), solveTime(), formattedSolveTime() | When, and how long it stood: 11y 6mo 16d 14h 13m |
solver(), assets(), sourceUrl() | Who, what files, and where the puzzle was announced |
explorerUrl(), claimExplorerUrl(), assetUrl() | Links, built from @agntn/chains explorer bases |
toJSON() | The serialized record with every absent field left out |
balance(options) | The live balance through @agntn/explorers, the one method that touches a network |
Errors
Everything thrown here descends from PuzzlesError. Catch one type, read fields, skip the message parsing. PuzzleNotFoundError is requirePuzzle on an id nobody has. UnknownCollectionError is requireCollection on a key outside the manifest. InvalidArgumentError is a tool argument outside its limits. Balances have their own family under BalanceError, on the balances page.
Next
- Puzzle records: the
PuzzleSpecliteral, the builders, and why nothing is null. - Registry: the manifest, lazy loading, aliases,
registerCollection. - Lookups and views:
get,selectPuzzles,stats,dataset,dataVersion. - Verification: a published key against its address.
- Balances: the one network call.
- CLI: the
puzzlescommand. - Agents: six tools over MCP, Pi and OMP.
- Custom collections: your puzzles behind the same registry.
- Playground: the library, in the page.