Python SDK for Phoebe — TON-native BLS-attested price oracle. Push BLS-signed Merkle-root snapshots, pull individual prices, verify off-chain. Byte-parity with @titon-network/phoebe-sdk.
Project description
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
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_phoebe_sdk-0.5.0.tar.gz.
File metadata
- Download URL: titon_network_phoebe_sdk-0.5.0.tar.gz
- Upload date:
- Size: 74.3 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ec22526c6b9d5dbdc7e9484bca1366a4eda7f93966482f4da510f7b81107506
|
|
| MD5 |
bfb33c0db4cd904260fd2afdfd98aff3
|
|
| BLAKE2b-256 |
2b3726666b65d3130d755e7505264dfbe670f7500db86e8def3e05f74b3c1ea8
|
File details
Details for the file titon_network_phoebe_sdk-0.5.0-py3-none-any.whl.
File metadata
- Download URL: titon_network_phoebe_sdk-0.5.0-py3-none-any.whl
- Upload date:
- Size: 53.9 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b1634f4822f38d7f8d74dfb2b73ae64b64eae06f555c43c7b67e2693234ce4e
|
|
| MD5 |
71c2dae08fe9c793d8fdbaefc3cb4066
|
|
| BLAKE2b-256 |
8d84dec2966ccb820cd71eeab17536497f5f68356c99caab88d442b7df5837ca
|