Skip to main content

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 cellsPhoebe.create_from_config(...) derives the same deployment address from the same (owner, forgeton, atlas) tuple in either language. Test: test_address_parity cross-verifies against the live testnet + mainnet addresses.
  • PriceLeaf cells — same 200-bit packed layout, same cell.hash().
  • Merkle rootsPhoebeMerkleTree.root() matches TS PhoebeMerkleTree.root() byte-for-byte for the same leaf set.
  • BLS hash domaincompute_snapshot_hash(phoebe, ts, root) returns the same 68-byte buffer as TS; signatures from py_ecc.bls.G2ProofOfPossession.Sign verify under the same groupPk as @noble/curves/bls12-381 longSignatures.sign.
  • Pruned merkle proofstree.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. pytoniq doesn'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 estimate CLI; Python parity is a follow-up. Use python -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.tswirePhoebeScripted.ts), then read state from Python.
  • Operator-side daemon — production operators run the automaton TS 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

titon_network_phoebe_sdk-0.5.0.tar.gz (74.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

titon_network_phoebe_sdk-0.5.0-py3-none-any.whl (53.9 kB view details)

Uploaded Python 3

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

Hashes for titon_network_phoebe_sdk-0.5.0.tar.gz
Algorithm Hash digest
SHA256 7ec22526c6b9d5dbdc7e9484bca1366a4eda7f93966482f4da510f7b81107506
MD5 bfb33c0db4cd904260fd2afdfd98aff3
BLAKE2b-256 2b3726666b65d3130d755e7505264dfbe670f7500db86e8def3e05f74b3c1ea8

See more details on using hashes here.

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

Hashes for titon_network_phoebe_sdk-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5b1634f4822f38d7f8d74dfb2b73ae64b64eae06f555c43c7b67e2693234ce4e
MD5 71c2dae08fe9c793d8fdbaefc3cb4066
BLAKE2b-256 8d84dec2966ccb820cd71eeab17536497f5f68356c99caab88d442b7df5837ca

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page