npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ton-primes/agent

v0.4.0

Published

TypeScript SDK for TON PRIMES: every action a program-player can take, each gated by a live get-method read, with the key signing locally and never leaving the process.

Readme

@ton-primes/agent

npm types license

Play TON PRIMES from a program.

Integers are minted strictly sequentially as NFTs for a flat 1 TON. Composites need a submitted prime factorization, primes are verified on chain by Miller–Rabin, and the residual of every mint is injected into a ratchet whose internal floor price p_f = T / S is strictly increasing on every mint. That last one is a contract-verifiable invariant, not a promise — which is the point of this package.

Programs are welcome players (CONCEPT.md §9.10). Nothing here is a privilege: there is no API key, no allow-list, and no contract path with a cooldown or a per-wallet cap. This package is the published read endpoints, the same message builders the app signs with, and a wallet that signs locally.

Complete by construction. Every message a program-player can send has a method, and every route in the two published OpenAPI specs has a read. That is not a claim in this file — it is test/coverage.test.ts, which reads all three lists out of the repository and fails until a new builder or a new route is either wired or named as excluded with its reason. See What is deliberately not here.

Install

npm install @ton-primes/agent

Node 20+, ESM, TypeScript declarations included. Peer-free: @ton/core, @ton/crypto and @ton/ton are regular dependencies.

Read first, spend second

import { PrimesAgent } from "@ton-primes/agent";

const agent = new PrimesAgent({ network: "testnet" });   // read-only: no mnemonic

await agent.read.head();       // the next integer to be minted
await agent.read.floor();      // p_f as { numNanoton, denPrimes } — a ratio, never pre-divided
await agent.read.counters();   // T and S, and the two counters that are not deployed yet
await agent.read.number(7n);   // who owns 7, and what tribute stands to it
await agent.routeFor(1234n);   // "head" | "lot" | "past", read live off the chain

await agent.read.openLots();            // candidates from the index
await agent.read.openBuilds();          // builds still wanting a generator
await agent.read.lotPrice(5040n);       // what opening a lot would cost, and which lane
await agent.read.discoveryOwed(addr);   // unclaimed bounties, nanoPRIMES
await agent.read.contracts();           // which address holds which role

Every read-proxy response carries a provenance object naming the get-method and the contract address behind the figure, so anything the SDK returns can be recomputed by calling that method yourself. Verify before you spend; the design assumes you will.

Three things the read layer will not do to you:

  • No Number() on a money figure. Everything monetary decodes to bigint. A nanoTON balance past 2⁵³ is ordinary, and rounding one is not an option.
  • No fabricated zero. A route whose get-method is not on the deployed contract answers 501, and the matching method returns null. "The figure is nought" and "nothing published it" are different claims and stay different.
  • No index figure priced into a transaction. read.* is for browsing. Everything under chain() is a live runMethod, and that is what the act methods check themselves against.

Play

const agent = new PrimesAgent({
  mnemonic: process.env.PRIMES_MNEMONIC,  // 24 words, a DEDICATED wallet
  network: "testnet",
  referralKey: 42,                        // a number you own: it is paid on every mint
});

await agent.mint();                       // 1 TON for the number at the head
await agent.openLot({ n: 5040n });        // any number ahead of the head, prime or composite — opening IS the first bid
await agent.openHeadLot(9973n);           // a prime's descending lot
await agent.bid({ n: 9973n });            // on a descending lot, the bid IS the purchase
await agent.closeLot(5040n);              // permissionless, once an ascending clock has run
await agent.flush();                      // permissionless: TON has no scheduler

The built number line (CONCEPT.md §3.2.2)

Four messages, in order, and the third is the one people miss: a build closes only while the registrar holds its generator, and there is no send_to_build opcode — it is an ordinary NFT transfer with the build named in its forward payload.

await agent.openBuild({ target: 23n, op: 18, inputs: [11n, 12n] });
await agent.proveInput({ target: 23n, inputIndex: 0 });
await agent.sendGenerator({ generatorAddress: "EQ...", target: 23n });   // the generator is BURNED on close
await agent.closeBuild(23n);                                   // mints constellation 23

await agent.expireBuild(99n);             // permissionless, pays nothing, frees a target

Getting paid

await agent.claimTribute(7n);             // §4.1's divisor share, off 7's own item
await agent.claimDiscovery();             // D-107's bounties, off the bounty vault
await agent.annotate({ n: 30n, text: "the third primorial" });  // §4.7.1, an era trophy's free first engraving
await agent.transferTrophy({ primorial: 30n, newHolder: "EQ..." });   // D-14, holder only

The factorization and each ⌊√p⌋ are computed for you from the same modules the contract tests use, so a mint cannot carry a proof the ledger will reject.

The rules this enforces for you

  1. A number you are about to spend against is re-read from the chain. mint checks the live head, bid checks the live getLot(n). The HTTP index finds candidates; it never prices a transaction.
  2. Every refusal the contract would make is made first, off chain. Not the owner, a zero owed, an empty bounty line, an input index past inputCount, a build holding no generator, an expiry head the head has not reached, a naming right already spent, an empty pending, a flush inside its 24-hour cooldown. Each is a thrown Error before a wallet opens, where the contract's version of the same refusal is gas already paid.
  3. Nominal price plus a fee margin, never the exact price. The contract compares the value that arrives, after TON deducts the message's forward fee. Sending the exact nominal reverts.
  4. Throttled at the client. read-proxy allows 600 requests a minute per IP, read-index 120; both answer 429 with Retry-After. The default is 2 requests a second with one bounded retry, and it fails loudly rather than queueing on your behalf.
  5. Your key never leaves your process. No service in this project holds one.

API

new PrimesAgent(options)

| Option | Default | | | --- | --- | --- | | mnemonic | — | 24 words. Omit for a read-only agent: every action then refuses by name. | | network | "testnet" | Selects the default Toncenter endpoint. | | tonEndpoint, tonApiKey | Toncenter public | Your own RPC, if you have one. | | addresses | from /contracts | Skip the manifest fetch by passing the roles yourself. | | referralKey | 0 | §4.1's referral line: a number you own. 0 routes it to the pool, never to the team. | | readProxyUrl, readIndexUrl, eventsUrl | primes.live | The three services. | | rps | 2 | Client-side pacing, across all three. | | fetchImpl | globalThis.fetch | Injectable, for tests and proxies. |

Actions — every message a program-player can send

| Method | Sends to | Costs | | --- | --- | --- | | mint({ n?, beneficiary?, referralKey?, invites? }) | ledger | 1 TON | | bid({ n, bidNanoton?, beneficiary?, referralKey? }) | market | the bid | | openLot({ n, bidNanoton?, … }) | market | the opening bid | | openHeadLot(n) | market | gas | | closeLot(n) | market | gas | | openBuild({ target, op, inputs, inputIsCon? }) | registrar | gas | | proveInput({ target, inputIndex }) | registrar | gas | | sendGenerator({ generatorAddress, target }) | the generator item | gas — burns the generator | | closeBuild(target) | registrar | gas + the primality check | | expireBuild(target) | registrar | gas, pays nothing | | claimTribute(n) | n's own item | gas | | topUp(n, multiples?) | n's own item | multiples x the item endowment — permissionless on ANY number | | claimDiscovery() | bounty vault | gas | | annotate({ n, text }) | registrar | gas — once only, era trophies only (D-179) | | transferTrophy({ primorial, newHolder }) | registrar | gas — holder only (D-14) | | flush() | ratchet | gas | | swap({ side, amount, slippageBps? }) | DeDust's native vault (buy) or this wallet's own PRIMES jetton wallet (sell) | the amount + the venue's gas |

Reads

agent.read is a PrimesRead over the three published services, and agent.chain() is a PrimesChain of live runMethod calls. Grouped:

  • the ratchet and the ledger — head, floor, counters, pending, swapped, flushStatus, floorCache, staking, k, kMax, lastMintTime, seedT, eraLeader, bootstrap
  • one wallet — balance, seqno, primesBalance, ranks, earn, numbersOf, claimsOf, historyOf, contestOf, portfolioOf
  • numbers, lots and prices — number, numbers, isKnownPrime, itemAddress, name, numberTrophy, runway, tributeTotal, yieldOf, lot, lotPrice, lotFloor, score, trophy, trophyParams, isOffHead, feeSplit, openLots, lotHistory, cheapestLot, unclaimedOffers
  • invites, generators and the built line — inviteConfig, generator, generatorsOf, generatorRefusal, build, openBuilds, constellation, buildTotals, constellationsOf, recentConstellations, collections
  • the bounty vault — vault, discoveryOwed, discoveryTier, eraSchedule, rebate
  • the auditor's surfaces — supply, solvency, sink, venue, market, vesting, treasury, treasuryProposals, treasuryLedger, stake, stakePositions, stakeLedger, lpPositions, lpLocked, lpWallet, contracts
  • the index's own tables — indexStatus, board, feed

Anything not named: read.proxy<T>(path), read.proxyOptional<T>(path), read.index<T>(path) and read.events<T>(path) are the escape hatches, one per service.

Helpers

factorize(n), isqrt(v), isProbablePrime(n) — the contract's own arithmetic, so a proof you build with them is a proof the ledger accepts. NAME_MAX_CHARS, BID_FEE_MARGIN.

Errors

Everything throws a plain Error with a message written to be read by whoever has to fix it. Four kinds, and they are worth telling apart:

| Looks like | Means | | --- | --- | | this agent is read-only: … | constructed without a mnemonic. Thrown before any network call. | | n=99 is not the head (12); … | a pre-check refused what the contract would have refused. Nothing was sent, nothing was spent. | | GET …/ledger/head -> 429 twice; slow down | the rate-limit budget is genuinely spent. Lower rps; the SDK will not queue for you. | | wallet seqno did not advance within 45000ms | the transfer was signed and sent, and confirmation timed out. The message may still land — re-read state before retrying. |

null from a read is never an error: it is a route whose get-method is not on the deployed contract, and the methods that can return it say so in their own doc comments.

What the game rewards a program for

  • The constellation discovery bounties are finite, first-come windows over a deterministic graph (CONCEPT.md §3.2.2). They go to whoever enumerates it first.
  • A referral key is a bearer asset. rN pays the current owner of number N on every mint made under it, including your own.
  • flush() is permissionless. TON has no scheduler, so the ratchet relies on someone calling it.

What it does not reward: a loop. Every automated behaviour is priced at the floor in CONCEPT.md §8 — a mint loop returns less than it pays, self-referral is assumed universal rather than prevented, and a recruit costs a fresh address plus a full 1 TON mint. §8.1 names the one regime where that inverts and publishes the threshold. Read it before you model a return.

The reference agent

examples/prospector.ts enumerates every constellation target the numbers in a wallet can reach, rarest operation first, and filters out the ones someone is already building.

It lives in the repository rather than the tarball, because it imports the SDK's sources:

git clone https://github.com/KonnikPahoni/primes && cd primes/backend/agent
PRIMES_WALLET=EQ... npx tsx examples/prospector.ts

What is deliberately not here

The LP miner and the governed treasury (DECISIONS.md D-100 — eight message builders). A deposit has to leave from the depositor's DeDust LP jetton wallet, and read-proxy reports that address as null on every deployment because the DeDust master is not a configured role. Every position message needs a position, and a position needs a deposit. They are not wrapped as though they worked.

The paid annotation (§5.2 family 1) is absent for a smaller reason: it is a TEP-74 transfer out of your own PRIMES jetton wallet, priced at 1 TON of PRIMES at the live floor (getNamePriceTon() against the ledger's T/S, served as read.sink().namePricePrimes, D-179). Since D-179 that is how every prime is named, so an agent can name only an era trophy (free, once, through annotate()).

Ten read routes, each for a stated reason: a liveness probe, an SSE stream, rendered artwork, TEP-64 metadata served for a wallet's own fetcher, and four narrative surfaces the webapp computes for its own charts. test/coverage.test.ts carries the list with the reason per line, and a new route in either spec fails that test until somebody classifies it.

Reference