Python SDK for Fortuna — TON-native threshold-BLS VRF. Request randomness, decode events, verify beta. TSA-audited zero findings.
Project description
titon-network-fortuna-sdk
Python SDK for Fortuna — TON's threshold-BLS VRF. Verifiable randomness for TON, operator-bonded via ForgeTON shared-security and group-key-rotated via Atlas.
1:1 surface parity with @titon-network/fortuna-sdk (TS),
so docs translate by transliteration. Contract wrapper, VRF primitives, event
decoder, error explainer, bundled compiled artifact, live-deployment constants.
Install
pip install titon-network-fortuna-sdk
Requires Python ≥ 3.11.
Connect — testnet vs mainnet (the only difference)
The SDK is network-agnostic. You pick the network in two lines: the LiteServer config and which deployment constant you import.
import asyncio
from pytoniq import LiteBalancer
from fortuna_sdk import Fortuna, FORTUNA_TESTNET
async def main():
# ── testnet ──────────────────────────────────────────────────────
client = LiteBalancer.from_testnet_config(trust_level=2)
await client.start_up()
fortuna = Fortuna.create_from_address(FORTUNA_TESTNET.fortuna, client=client)
health = await fortuna.get_contract_health()
print(f"connected to testnet — paused={health.paused} fee_accumulated={health.config.fee_accumulated}")
await client.close_all()
asyncio.run(main())
For mainnet, change exactly two lines (once Fortuna mainnet is deployed — see Live deployments below):
# ── mainnet ──────────────────────────────────────────────────────────
client = LiteBalancer.from_mainnet_config(trust_level=2) # ← was from_testnet_config
await client.start_up()
fortuna = Fortuna.create_from_address(FORTUNA_MAINNET.fortuna, # ← was FORTUNA_TESTNET
client=client)
That's it. The same wheel, same bytecode, same code paths work on either
network — only the LiteServer config and the deployment constant change.
Pointing at a custom deployment (your own fork, a private chain) is just
Fortuna.create_from_address("0Q...", client=client) — no constant required.
Prefer the safe-by-default helper for scripts: assert_deployment("testnet" | "mainnet") returns the populated FortunaDeployment or raises an actionable
error if the requested network isn't shipped yet (mainnet today).
Three surfaces
1. Contract wrapper
from fortuna_sdk import Fortuna, new_fortuna
# Existing deploy:
fortuna = Fortuna.create_from_address(addr, client=client)
config = await fortuna.get_config()
# Fresh deploy:
fortuna = new_fortuna(
owner=owner_addr, forgeton=forgeton_addr, atlas=atlas_addr, client=client
)
# fortuna.address is derived from the bundled code + init data.
await fortuna.send_deploy(wallet, value=200_000_000) # 0.2 TON
Every on-chain receiver has a send_* method on the wrapper:
send_request_randomness, send_reclaim_request, send_fulfill_randomness,
send_pause, send_unpause, send_update_config, send_withdraw_fees,
send_sync_atlas, send_propose_code_upgrade, send_execute_code_upgrade,
send_cancel_code_upgrade, send_prune_expired_request, send_fortuna_opt_in,
send_fortuna_opt_out. Every getter has a get_* method:
get_ownership, get_paused, get_config, get_schema_versions,
get_operator, get_group_key, get_request, get_pending_upgrade,
get_contract_health.
2. VRF primitives
from fortuna_sdk import compute_alpha, compute_beta, sign_alpha, aggregate_signatures
# Off-chain operator signing:
alpha = compute_alpha(consumer, query_id, seed, creation_lt)
sig = sign_alpha(operator_sk, alpha)
# Off-chain aggregate (threshold ceremony):
agg = aggregate_signatures([sig1, sig2, ...])
# Consumer-side beta verification (after VrfCallback):
beta = compute_beta(agg, consumer, query_id, seed, creation_lt)
assert beta == event_beta_bytes # byte-identical to on-chain
compute_alpha / compute_beta / compute_req_key are byte-identical to the
Tolk on-chain helpers. sign_alpha / aggregate_signatures use
py_ecc.bls.G2ProofOfPossession (min-pk + POP DST), interoperable with the
TS SDK's @noble/curves/bls12-381 longSignatures.
3. Event decoding + error explaining
from fortuna_sdk import decode_events, explain_error, FortunaError
# Decode external-out bodies into the typed FortunaEvent union:
for ev in decode_events(external_out_bodies):
if ev.kind == "RequestFulfilled":
print(f"req {ev.req_key:x} via {ev.submitter}, beta={ev.beta:x}")
# Explain an exit code with structured metadata:
e = explain_error(161)
# ErrorExplanation(code=161, origin='fortuna', name='InvalidBlsSignature', ...)
# Or catch the typed exception:
try:
await fortuna.send_fulfill_randomness(...)
except FortunaError as err:
print(err.code, err.origin, err.hint)
Common task → what to import (cheat sheet)
| Task | Import |
|---|---|
Send RequestRandomness |
from fortuna_sdk import Fortuna, FORTUNA_TESTNET, QueryIdStream |
| Decode events from a tx | from fortuna_sdk import decode_events, RequestCreatedEvent, RequestFulfilledEvent |
| Verify beta client-side | from fortuna_sdk import compute_beta |
| Compute alpha for off-chain signing | from fortuna_sdk import compute_alpha, sign_alpha, aggregate_signatures |
| Owner ops (pause / config / withdraw) | from fortuna_sdk import Fortuna (every receiver has a send_*) |
| Code-upgrade timelock | from fortuna_sdk import Fortuna, MIN_UPGRADE_DELAY_SECONDS |
| Look up an exit code | from fortuna_sdk import explain_error, format_error_explanation |
| Catch SDK-side reverts | from fortuna_sdk import FortunaError, SchemaDriftError |
| Schema drift pre-flight | await fortuna.validate_against_live() |
| Bundle a tx into success/failure/events | from fortuna_sdk import summarize_tx_from_parts, format_tx_summary |
| Live testnet address book | from fortuna_sdk import FORTUNA_TESTNET, FORTUNA_MAINNET, assert_deployment |
| Bundled compiled BoC for a fresh deploy | from fortuna_sdk import load_fortuna_code, fortuna_code_hash, new_fortuna |
⚠️ Deploy from Python is partial. new_fortuna(...) builds the init
cell + bundles the BoC, and you can drive send_deploy from any
pytoniq.BaseWallet. But admitting Fortuna at Atlas + ForgeTON
requires their TS SDKs (no Python ports yet) — finish the wire-up
from ../../scripts/deployFortunaScripted.ts + wireFortunaScripted.ts.
⚠️ Operator off-chain signer is TS-only. Python ships the crypto
primitives (sign_alpha, aggregate_signatures), but the production
operator daemon, keystore, metrics, and event drain live in
automaton (TypeScript). Don't roll your own in
Python unless you have a strong reason.
Surface parity with TypeScript
Same names (snake_case mirror), same shapes. Translate any TS docs/example by mechanical transliteration:
| TypeScript | Python |
|---|---|
Fortuna.createFromAddress(addr) |
Fortuna.create_from_address(addr, client=client) |
Fortuna.createFromConfig(cfg, code) |
Fortuna.create_from_config(cfg, code, client=client) |
fortuna.sendRequestRandomness(via, opts) |
await fortuna.send_request_randomness(wallet, **opts) |
fortuna.getRequest(consumer, queryId) |
await fortuna.get_request(consumer, query_id) |
computeAlpha(consumer, q, seed, lt) |
compute_alpha(consumer, q, seed, lt) |
decodeEvents(bodies) |
decode_events(bodies) |
explainError(161) |
explain_error(161) |
summarizeTx(tx) |
summarize_tx(tx) |
FORTUNA_TESTNET.fortuna |
FORTUNA_TESTNET.fortuna |
assertDeployment("testnet") |
assert_deployment("testnet") |
See AGENTS.md for the full surface map + skills/ for persona-grouped recipes (consumer / owner / debug).
Project links
- Fortuna contract source
- TS SDK — canonical, this is a port
fortuna/CLAUDE.md— repo-wide AI development guidefortuna/DESIGN.md— architecture decisions D-001..D-019
🛡️ Audit posture. Built on the Fortuna contract that cleared TSA static
analysis with zero findings — see
AUDIT_REPORT.md.
The SDK is a transliteration of the audited TS SDK; same byte-for-byte alpha /
beta / request-key derivation.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file titon_network_fortuna_sdk-0.3.2.tar.gz.
File metadata
- Download URL: titon_network_fortuna_sdk-0.3.2.tar.gz
- Upload date:
- Size: 102.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ecb34b5a5badc0f4233bedb47a803cd37920a3e37eb87512566cd515a553f19e
|
|
| MD5 |
29a9884997243a07a01233ad622f1037
|
|
| BLAKE2b-256 |
db06679a8f8290a20d126c18c78495b2fded2d24111739f877ed6bc3f202f8c1
|
File details
Details for the file titon_network_fortuna_sdk-0.3.2-py3-none-any.whl.
File metadata
- Download URL: titon_network_fortuna_sdk-0.3.2-py3-none-any.whl
- Upload date:
- Size: 41.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
df898d586fe100ada235b07785b88b60a1d5a6f84931e9d2c7b1a3e57092a206
|
|
| MD5 |
3a99488563816a57a887a5dac00006a4
|
|
| BLAKE2b-256 |
1c11e59d8895220c21af607211fb06dbc7e9267ed9b087c652d47af653d52026
|