titon-network-phoebe-sdk
Python SDK for Phoebe — TON's
BLS-attested Merkle-root price oracle. Operators heartbeat a signed
snapshot root every ~30s; consumers pull individual feeds via cell-merkle
proofs. One BLS_VERIFY + one cell-walk per pull, for any feed in a
256-feed snapshot.
Version-matched companion to @titon-network/phoebe-sdk@0.5.0 — byte-identical
init cells, Merkle roots, BLS hash domain, and PriceLeaf wire codec. Pick
your language; deploys at the same address.
Install
pip install titon-network-phoebe-sdk
Requires Python ≥ 3.11.
The one-call dapp surface — fetch_verified_price
The shortest path from "I need a price" to "I have a verified leaf + proof ready for my consumer contract":
import asyncio
from pytoniq import LiteBalancer
from phoebe_sdk import Phoebe, assert_deployment, fetch_verified_price
async def main():
client = LiteBalancer.from_mainnet_config(trust_level=2)
await client.start_up()
dep = assert_deployment("mainnet")
phoebe = Phoebe.create_from_address(dep.phoebe, client=client)
# One call: fetches leaves from every operator in parallel, reconstructs
# the merkle root locally, compares to phoebe.lastRoot on-chain, returns
# the verified leaf + proof. Lying / stale operators caught by hash
# mismatch; helper falls through to the next in list.
quote = await fetch_verified_price(phoebe, feed_id=0, operators=dep.operators)
print(f"TON/USD = {quote.mantissa} × 10^{quote.expo}")
print(f"snapshot age: {quote.age_sec}s")
# quote.proof + quote.leaf are now ready to submit to your consumer
# contract via Phoebe.send_request_price.
await client.close_all()
asyncio.run(main())
Trust model: NO trust in any operator. The reconstructed merkle root is
compared against phoebe.lastRoot on-chain; mismatched / stale / buggy
operators are skipped. A malicious operator can't forge a matching root
because they don't hold the threshold BLS secret that signed it on-chain.
Three surfaces
1. Contract wrapper — Phoebe
from phoebe_sdk import Phoebe, new_phoebe
# Existing deploy:
phoebe = Phoebe.create_from_address(addr, client=client)
config = await phoebe.get_config()
snapshot = await phoebe.get_snapshot()
# Fresh deploy:
phoebe = new_phoebe(
owner=owner_addr, forgeton=forgeton_addr, atlas=atlas_addr, client=client
)
await phoebe.send_deploy(wallet, value=1_000_000_000) # 1 TON seed
Every on-chain receiver has a send_* method:
send_push_snapshot, send_request_price, send_claim_reward,
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_gc_operator, plus cross-contract sandbox
helpers send_automaton_sync / send_group_key_sync.
Every getter has a get_*: get_ownership, get_paused, get_config,
get_schema_versions, get_operator, get_group_key, get_snapshot,
get_last_submitter, get_unclaimed_reward, get_pending_upgrade.
2. Off-chain Merkle tree + BLS primitives
from phoebe_sdk import (
PhoebeMerkleTree, PriceLeaf,
compute_snapshot_hash, sign_message, aggregate_signatures,
)
# Build the 256-leaf merkle tree (sparse → placeholders fill the rest).
tree = PhoebeMerkleTree.from_sparse_leaves({
0: PriceLeaf(feed_id=0, mantissa=3_500_000_000, expo=-9,
conf_bps=50, pub_time=1_700_000_000),
})
root = tree.root_as_int() # int form for PushSnapshot.root
# Threshold-BLS-sign (phoebeHash, timestamp, root) under groupPk.
ts = int(time.time())
msg = compute_snapshot_hash(phoebe.address, ts, root)
sig = sign_message(operator_sk, msg)
agg_sig = aggregate_signatures([sig]) # solo or aggregated partials
await phoebe.send_push_snapshot(wallet, value=100_000_000,
timestamp=ts, root=root, agg_sig=agg_sig)
3. Event decoding + error explaining
from phoebe_sdk import decode_event, explain_error, PhoebeError
ev = decode_event(external_out_body)
if ev.kind == "SnapshotPushed":
print(f"root 0x{ev.root:x} from {ev.submitter} at ts={ev.timestamp}")
# Structured explanation for any exit code:
e = explain_error(161)
# ErrorExplanation(code=161, origin='phoebe', name='InvalidBlsSignature',
# message='...', hint='Most likely causes: wrong DST...')
Cross-language byte parity
The TS and Python SDKs ship at the same version and produce byte-identical:
- Init cells —
Phoebe.create_from_config(...)derives the same deployment address from the same(owner, forgeton, atlas)tuple in either language. Test:test_address_paritycross-verifies against the live testnet + mainnet addresses. PriceLeafcells — same 200-bit packed layout, samecell.hash().- Merkle roots —
PhoebeMerkleTree.root()matches TSPhoebeMerkleTree.root()byte-for-byte for the same leaf set. - BLS hash domain —
compute_snapshot_hash(phoebe, ts, root)returns the same 68-byte buffer as TS; signatures frompy_ecc.bls.G2ProofOfPossession.Signverify under the samegroupPkas@noble/curves/bls12-381longSignatures.sign. - Pruned merkle proofs —
tree.proof(feed_id)produces a level-0 merkle_proof exotic cell that hashes identically to the TS output and validates on-chain.
Live deployments
from phoebe_sdk import PHOEBE_TESTNET, PHOEBE_MAINNET, assert_deployment
- Testnet:
0QDpuv4PEKctvS4Oj1bZ8doILQbC1iM7TNzTz4OIHVXPB-Mm - Mainnet:
UQA0puv5JEGFvyRYW_XEveKiI0--XdVKsJv8wk0CmhFlc0-u(multi-op n=2, threshold=2, epoch=1)
Both have operators=[...] populated on mainnet — fetch_verified_price
will work out of the box.
What's not here
- Sandbox testing helper (
testing/) — TS-only today.pytoniqdoesn't ship a parallel@ton/sandbox. Run sandbox tests against the TS SDK; use this Python SDK for production consumers + live monitoring. - CLI — phoebe TS ships a
phoebe describe/phoebe estimateCLI; Python parity is a follow-up. Usepython -c 'from phoebe_sdk import …'one-liners for now. - Owner-side deploy + wire-up scripts — Atlas SetVerifier + ForgeTON
SetConsumer require the sibling TS SDKs. Run TS deploy
(
phoebe/scripts/deployPhoebeScripted.ts→wirePhoebeScripted.ts), then read state from Python. - Operator-side daemon — production operators run the
automatonTS daemon. Python has the BLS + sign primitives but no daemon harness.
Mirror checklist
When editing this Python SDK, mirror to the TS sibling:
| Python change | TS mirror |
|---|---|
Add a row to errors.py:_PHOEBE_MESSAGES |
Add to sdks/typescript/src/errors.ts:PHOEBE_MESSAGES |
Bump PHOEBE_STORAGE_VERSION / PHOEBE_CONFIG_BLOB_VERSION |
Update sdks/typescript/src/opcodes.ts + contracts/Phoebe.ts init cell layout |
Add a new send_* / get_* |
Add the same method to sdks/typescript/src/contracts/Phoebe.ts |
New OperatorEndpoint entry on a deployment |
Update both deployments.py and sdks/typescript/src/deployments.ts |
License
MIT
Metadata
Release files for titon-network-phoebe-sdk 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| titon_network_phoebe_sdk-0.5.0.tar.gz | 74.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| titon_network_phoebe_sdk-0.5.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 128.2 kB
Release files / titon_network_phoebe_sdk-0.5.0.tar.gz
| Download URL | titon_network_phoebe_sdk-0.5.0.tar.gz |
|---|---|
| Size | 74.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7ec22526c6b9d5dbdc7e9484bca1366a4eda7f93966482f4da510f7b81107506
|
|
BLAKE2b-256 checksum How to use checksums |
2b3726666b65d3130d755e7505264dfbe670f7500db86e8def3e05f74b3c1ea8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / titon_network_phoebe_sdk-0.5.0-py3-none-any.whl
| Download URL | titon_network_phoebe_sdk-0.5.0-py3-none-any.whl |
|---|---|
| Size | 53.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5b1634f4822f38d7f8d74dfb2b73ae64b64eae06f555c43c7b67e2693234ce4e
|
|
BLAKE2b-256 checksum How to use checksums |
8d84dec2966ccb820cd71eeab17536497f5f68356c99caab88d442b7df5837ca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|