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

@titon-network/fortuna-sdk

v0.6.0

Published

TypeScript SDK for Fortuna — TON-native threshold-BLS VRF. Request randomness, fulfill as an operator, decode events, scaffold a consumer. **TSA AUDITED — zero findings** (https://github.com/titon-network/fortuna/blob/main/AUDIT_REPORT.md): 5 symbolic-exe

Readme

@titon-network/fortuna-sdk

TypeScript SDK for FortunaTON's threshold-BLS VRF. Consumers request randomness, operators sign + aggregate off-chain, and Fortuna delivers a uniform beta: uint256 via VrfCallback — verifiable against the group public key cached from Atlas.

Audit status

🛡️ TSA-audited — zero findings. Full report: AUDIT_REPORT.md.

Fortuna consumes:

  • Atlas — group public key + rotation (via GroupKeySync at 0x51).
  • ForgeTON — operator stake + mirror (via AutomatonSync at 0x1A).

Fortuna launches without slashing (D-011). Positive-incentive economics only: operators compete for submitterReward via first-message-wins. Miss-slashing is deferred to a post-shadow-data revision.

Live deployments

| Network | Fortuna | Atlas | ForgeTON pool | |---------|---------|-------|---------------| | Mainnet (live 2026-05-18, multi-op n=2 / threshold=2 / epoch=1) | UQAao6PxnFWUAPfPRPbWwe6ZZxMPrBhEhp4ISmwP1rIp8OLc | UQClahzd17lho1xb8v-ev-2ZHITcAZyKQgtRqseSaQOciimT | UQDqNyNHJ4ulbZ1jCi_zUT-QrvVBmPcrIeyDdtpkwQMJO8TV | | Testnet | 0QAHAhzkfTlT0xW4jL2tCkaOJRBPaWvASmdv8oVNsOHnkeYT | 0QCsCVyRSgq_wDHb0txeZ7rxJ2y0si9_udTsaSbAQdPgCewx | 0QD-9fxQ8k1f23xNFyWI-edoh-WnAIDMUrwvodnkSWYuz1rF |

Both networks run the same bytecode (code hash ad8ce25d…). The SDK exports FORTUNA_TESTNET and FORTUNA_MAINNET so you can target either with the same import path.

Install

npm install @titon-network/fortuna-sdk @ton/core

@ton/core is a peer dependency — bring your own version (>= 0.63.0). @titon-network/atlas-sdk + @titon-network/forgeton-sdk are peer deps for test fixtures.

Connect — testnet vs mainnet (the only difference)

The SDK is network-agnostic. You pick the network in two lines: the TonClient endpoint and which deployment constant you import.

// ── testnet ──────────────────────────────────────────────────────────
import { TonClient } from '@ton/ton';
import { Fortuna, FORTUNA_TESTNET } from '@titon-network/fortuna-sdk';

const tonClient = new TonClient({
    endpoint: 'https://testnet.toncenter.com/api/v2/jsonRPC',
});
const fortuna = tonClient.open(Fortuna.createFromAddress(FORTUNA_TESTNET.fortuna));

const health = await fortuna.getContractHealth();
console.log(`connected to testnet — paused=${health.paused} feeAccumulated=${health.config.feeAccumulated}`);

For mainnet, change exactly two lines:

// ── mainnet ──────────────────────────────────────────────────────────
import { TonClient } from '@ton/ton';
import { Fortuna, FORTUNA_MAINNET } from '@titon-network/fortuna-sdk';

const tonClient = new TonClient({
    endpoint: 'https://toncenter.com/api/v2/jsonRPC',                  // ← no `testnet.`
});
const fortuna = tonClient.open(
    Fortuna.createFromAddress(FORTUNA_MAINNET.fortuna),                // ← was FORTUNA_TESTNET
);

That's it. The same npm package, same bytecode, same code paths work on either network — only the TonClient endpoint and the deployment constant change. Pointing at a custom deployment (your own fork, a private chain) is just Fortuna.createFromAddress(Address.parse('EQ...')) — no constant required.

Prefer the safe-by-default helper for scripts: assertDeployment('testnet' | 'mainnet') returns the populated FortunaDeployment or throws an actionable error if the requested network isn't shipped.

30-second integration — request randomness

Fortuna is live on both networks. This block calls the deployed VRF directly — copy, paste, run.

import { TonClient } from '@ton/ton';
import { toNano } from '@ton/core';
import {
    Fortuna, FORTUNA_TESTNET, QueryIdStream,
} from '@titon-network/fortuna-sdk';

const tonClient = new TonClient({
    endpoint: 'https://testnet.toncenter.com/api/v2/jsonRPC',
});
const fortuna = tonClient.open(Fortuna.createFromAddress(FORTUNA_TESTNET.fortuna));

const queryIds = new QueryIdStream();
await fortuna.sendRequestRandomness(consumerVia, {
    value:       toNano('0.25'),               // baseFee + submitterReward + callbackGas + reserve + slack
    queryId:     queryIds.next(),              // 64-bit collision-free id
    seed:        0xdeadbeefn,                  // 256-bit consumer-supplied entropy
    callbackGas: toNano('0.05'),
});

Operators sign alpha off-chain, aggregate, and race to submit. The first valid FulfillRandomness triggers VrfCallback { queryId, beta } on your consumer at opcode 0x50 with bounce: false and callbackGas attached.

Mainnet diff — two lines:

- import { Fortuna, FORTUNA_TESTNET, QueryIdStream } from '@titon-network/fortuna-sdk';
+ import { Fortuna, FORTUNA_MAINNET, QueryIdStream } from '@titon-network/fortuna-sdk';
- const tonClient = new TonClient({ endpoint: 'https://testnet.toncenter.com/api/v2/jsonRPC' });
+ const tonClient = new TonClient({ endpoint: 'https://toncenter.com/api/v2/jsonRPC' });
- const fortuna = tonClient.open(Fortuna.createFromAddress(FORTUNA_TESTNET.fortuna));
+ const fortuna = tonClient.open(Fortuna.createFromAddress(FORTUNA_MAINNET.fortuna));

Audit detail

| Surface | Result | |---------|--------| | TSA static analysis (5 checkers) | 3 single-contract (drain-check / bounce-check / replay-attack) + 2 inter-contract (ic-fortuna-forgeton-drain / ic-fortuna-atlas-drain) — 0 findings, 0 outstanding | | Manual review | Per-receiver walk + closed prior findings F1–F4 + cross-border FF-1 (ForgeTON gas-floor) / FA-1 (Atlas SyncRequest re-push) + 3-way triangle T-1..T-3 — every probe reproduced as a regression test under tests/audit/ | | Contract tests | 188/188 passing (16 suites); 1000-iter property fuzz green; gas regressions guarded at 20% threshold | | SDK smoke tests | 67/67 passing (DocsSurface + Surface) | | ABI byte-shape pin | Pinned via tests/IntegrationFullStack.spec.ts — real ForgeTON + real Atlas + Fortuna in one sandbox; any drift in AutomatonSync (0x1A), Slash (0x14), or GroupKeySync (0x51) fails CI | | TSA coverage caveat | TSA v0.5.3 has unimplemented-instruction gaps (SHA256U, BLS_G1_INGROUP, certain STIR variants). Dropped symbolic states cover the deep state of RequestRandomness / FulfillRandomness / Reclaim / GroupKeySync past the BLS subgroup check — covered by manual review + tests/VrfLifecycle.spec.ts + tests/Fuzz.spec.ts + sender-pin regression tests. See report §"TSA limitations observed" | | Foundation | Built on TSA-audited @titon-network/forgeton-sdk + @titon-network/atlas-sdk |

Surfaces

┌─────────────────────────────────────────────────────────────┐
│ explainError  FortunaError  summarizeTx  formatTxSummary    │  diagnostics
├─────────────────────────────────────────────────────────────┤
│ decodeEvent   decodeEvents  tryDecodeEvent                  │  events
├─────────────────────────────────────────────────────────────┤
│ Fortuna       newFortuna    FORTUNA_DEFAULTS                │  contract + factory
├─────────────────────────────────────────────────────────────┤
│ computeAlpha  computeBeta   signAlpha  aggregateSignatures  │  VRF primitives
├─────────────────────────────────────────────────────────────┤
│ QueryIdStream submissionJitter                              │  off-chain ops
├─────────────────────────────────────────────────────────────┤
│ OP  ERR  loadFortunaCode  FORTUNA_TESTNET                   │  constants + artifacts
└─────────────────────────────────────────────────────────────┘

No façade, no fluent builder — the surface is small by design. Fortuna has one role (threshold-BLS VRF); integrate at the ABI level.

Make your LLM Fortuna-aware (one-time, 5 seconds)

If you're building with Claude Code / Cursor / Aider / Cline / any AI coding assistant, run:

npx fortuna init

It writes FORTUNA.md to your repo root — a dense, AI-optimised brief covering the integration pattern, critical constants, VrfCallback receiver checklist, and common-error table. Reference it from your existing CLAUDE.md / .cursorrules / AGENTS.md and your assistant will generate correct Fortuna-consumer code on the first try.

The same content powers llms.txt (the llmstxt.org entry point) and AGENTS.md (full surface map). Need deeper integration context? Load llms-full.txt — the canonical end-to-end AI brief covering Tolk patterns + TS request/decode + sandbox tests + every revert code in one file.

Want a clone-and-run dapp?

examples/coin-flip/ is a complete fair-coin VRF dapp in 4 files (Tolk + Blueprint compile descriptor + TS wrapper + sandbox test). Drop them into your Blueprint project, run npx blueprint test, and you have a fully-tested Fortuna integration. More examples (raffle, dice, mystery-box) coming in examples/.

The consumer playbook

You're building a product that needs verifiable randomness. Here's the shape:

  1. Design your consumer contract with a receiver at opcode 0x50 (VrfCallback). Scaffold via npx fortuna scaffold consumer → drops templates/consumer.tolk into your cwd as a starter. Your receiver uses the 256-bit beta for whatever randomness-consuming logic you need.
  2. Deploy your contract + send RequestRandomness to Fortuna:
    await fortuna.sendRequestRandomness(consumerVia, {
        value: baseRequestFee + submitterReward + callbackGas + minForwardReserve + slack,
        queryId: nextQueryId,   // use QueryIdStream to avoid collisions
        seed: appEntropy,       // 256-bit consumer-supplied entropy
        callbackGas: toNano('0.05'),
    });
  3. Wait for the callback. Operators sign alpha off-chain, aggregate, and race to submit. The first valid submission triggers VrfCallback { queryId, beta } to your consumer with bounce: false and callbackGas attached.
  4. Consume beta safely. beta is uniform uint256 — modulo into your app's range (beta % 100, beta % gridSize, etc). The full 256 bits is cryptographically secure.
  5. Handle stuck requests. If operators don't fulfill within requestTtl (default 1h), call sendReclaimRequest — consumer gets the fee back. Stale-epoch requests (rotation happened mid-flight) auto-abort on fulfill attempts; reclaim is the recovery path.

That's it. Key management, rotation, operator set drift, BLS aggregation — all handled off-chain or by Atlas + ForgeTON.

What does a request cost?

Defaults — tunables drift from defaults via UpdateConfig; read live values with fortuna.getConfig().

| Item | Default | Paid for | |------|---------|----------| | baseRequestFee | 0.1 TON | Per-request protocol fee; accrues to feeAccumulated, owner withdraws | | submitterReward | 0.05 TON | Winner-takes-all payout to the first valid FulfillRandomness submitter | | callbackGas | consumer chooses | Forwarded with VrfCallback — your callback's gas budget | | minForwardReserve | 0.02 TON | Reserve floor forwarded with callback so consumer can process it | | minStorageReserve | 0.1 TON | Fortuna's own rent floor (enforced before every outbound) | | requestTtl | 3600s (1h) | Operators have this window to fulfill; then reclaim kicks in |

On a RequestRandomness, attached value must be ≥ baseRequestFee + submitterReward + callbackGas + minForwardReserve + ~0.05 TON forward-fee slack.

CLI shortcut: npx fortuna estimate request --callback-gas 0.05.

Quickstart

Request randomness from a consumer contract

From your consumer's triggering flow:

struct (0x00000030) RequestRandomness {
    queryId:     uint64
    seed:        uint256
    callbackGas: coins
}

fun triggerVrf(queryId: uint64, seed: uint256) {
    val reqMsg = createMessage({
        bounce: BounceMode.NoBounce,
        value:  ton("0.25"),  // baseFee + submitterReward + callbackGas + forwardReserve + slack
        dest:   storage.fortuna,
        body:   RequestRandomness {
            queryId,
            seed,
            callbackGas: ton("0.05"),
        },
    });
    reqMsg.send(SEND_MODE_REGULAR);
}

Receive + verify the callback

struct (0x00000050) VrfCallback {
    queryId: uint64
    beta:    uint256   // uniform 256-bit randomness
}

fun handleVrfCallback(msg: VrfCallback, sender: address) {
    var storage = lazy ConsumerStorage.load();
    assert (sender == storage.fortuna) throw E_NOT_FORTUNA;
    // Your business logic here.
    doSomethingWithBeta(msg.queryId, msg.beta);
}

Off-chain: verify beta byte-identically

import { computeBeta } from '@titon-network/fortuna-sdk';

// After reading the VrfCallback event on-chain:
const expectedBeta = computeBeta(aggSig, consumerAddr, queryId, seed);
// expectedBeta is the same 32 bytes the consumer stored.

Solo-operator mode (dev / demo)

For local testing with a single operator (t=n=1):

import { signAlpha, computeAlpha, randomBlsSecret, blsPublicKey } from '@titon-network/fortuna-sdk';

const sk = randomBlsSecret();
const pk = blsPublicKey(sk);   // 48 bytes — register at Atlas as the sole pkShare

const alpha = computeAlpha(consumer, queryId, seed);
const sig = signAlpha(sk, alpha);   // 96 bytes — submit as aggSig

Not for mainnet value — a single signer means a single point of failure. OK for dev, demos, low-stakes deploys.

Sandbox test

import { Blockchain } from '@ton/sandbox';
import { deployFortunaFixture } from '@titon-network/fortuna-sdk/testing';

const blockchain = await Blockchain.create();
const { fortuna, operator, requestRandomness, fulfillRandomness } =
    await deployFortunaFixture(blockchain);

// Your consumer sends a request (through whatever path it normally does)…
// …then the fixture's one-liner completes the round-trip:
await fulfillRandomness({ consumer: myConsumer.address, queryId: 1n, seed: 0xdeadbeefn });

See skills/fortuna-integrate-consumer.md for the full walkthrough.

CLI

# Diagnostics (no network)
npx fortuna explain 161           # describe an exit code
npx fortuna decode <hex|base64>   # decode an event body
npx fortuna hash                  # bundled artifact code hash
npx fortuna schema                # SDK-expected schema versions
npx fortuna estimate request --callback-gas 0.05

# Live-state (needs @ton/ton)
npx fortuna info <addr> --testnet
npx fortuna request <consumer> --queryId 42 --addr <fortuna> --testnet
npx fortuna health --addr <fortuna> --testnet
npx fortuna verify --testnet

# Scaffolding
npx fortuna init                  # write FORTUNA.md (agent context) to cwd
npx fortuna scaffold consumer     # drop a Tolk VRF consumer template

All commands accept --json for machine-readable output.

Links

License

MIT. See LICENSE.

🛡️ TSA-audited — zero findings (report). Live on TON mainnet (2026-05-18) and testnet.