Python SDK for interacting with the Xian blockchain
Project description
xian-py
xian-py is the Python SDK for talking to Xian nodes from applications,
workers, automation jobs, and operator tooling. It exposes a clean
sync / async client surface over node RPCs, indexed BDS queries, and the
CometBFT websocket, plus reusable projector primitives for building local
read models.
The published PyPI package is xian-tech-py. The import package remains
xian_py.
SDK Shape
flowchart LR
App["Application or worker"] --> Sync["Xian sync client"]
App --> Async["XianAsync client"]
Sync --> RPC["Node RPC"]
Async --> RPC
Async --> WS["CometBFT websocket"]
Sync --> BDS["Indexed BDS APIs"]
Async --> BDS
BDS --> Projector["EventProjector and cursors"]
Wallet["Wallet"] --> Sync
Wallet --> Async
Quick Start
Install the SDK:
uv add xian-tech-py
Read state and submit a transaction with the synchronous client:
import os
from xian_py import Wallet, Xian
wallet = Wallet(private_key=os.environ["XIAN_PRIVATE_KEY"])
with Xian("http://127.0.0.1:26657", wallet=wallet) as client:
balance = client.token().balance_of(wallet.public_key)
submission = client.token().transfer("bob", 5, mode="checktx")
print(balance, submission.accepted)
Watch indexed events with the async client:
import asyncio
from xian_py import XianAsync
async def main() -> None:
async with XianAsync("http://127.0.0.1:26657") as client:
async for event in client.token().transfers().watch(after_id=0):
print(event.id, event.data)
asyncio.run(main())
For low-latency delivery without BDS-backed cursoring, use raw live websocket events:
import asyncio
from xian_py import XianAsync
async def main() -> None:
async with XianAsync("http://127.0.0.1:26657") as client:
async for event in client.token().transfers().watch_live():
print(event.tx_hash, event.data)
asyncio.run(main())
The SDK derives the websocket endpoint from the RPC URL by default
(http://127.0.0.1:26657 → ws://127.0.0.1:26657/websocket). Override with
WatcherConfig(websocket_url="ws://rpc-host:26657/websocket").
For visibility into transport retries, attach a callback to
RetryPolicy(on_retry=...). The callback receives a typed RetryEvent with
the operation kind, attempt number, next backoff delay, and exception.
Optional extras:
uv add "xian-tech-py[app]" # FastAPI examples
uv add "xian-tech-py[eth]" # Ethereum-style key helpers
uv add "xian-tech-py[hd]" # HD-wallet derivation
SDK Cookbook
The examples below assume a running node at http://127.0.0.1:26657.
Use xian-stack or xian-cli to start a local node, then pass the same RPC
URL into the SDK.
Create or import a wallet:
import os
from xian_py import Wallet
from xian_py.wallet import HDWallet
# Generate a fresh Ed25519 account.
wallet = Wallet()
print(wallet.public_key)
# Restore an existing account from a hex private key.
wallet = Wallet(private_key=os.environ["XIAN_PRIVATE_KEY"])
# Optional HD-wallet derivation. Requires: uv add "xian-tech-py[hd]".
hd = HDWallet()
derived_wallet = hd.get_wallet([44, 734, 0, 0, 0])
Inspect node and indexer health:
from xian_py import Xian
with Xian("http://127.0.0.1:26657") as client:
status = client.get_node_status()
bds = client.get_bds_status()
print(status.network, status.latest_block_height, status.catching_up)
print(bds.indexed_height, bds.height_lag, bds.alerts)
Read state, simulate a call, and retrieve contract source:
from xian_py import Xian
address = "bob"
with Xian("http://127.0.0.1:26657") as client:
balance = client.token().balance_of(address)
raw_balance = client.state_key("currency", "balances", address).get()
simulated = client.contract("currency").simulate(
"balance_of",
address=address,
)
source = client.contract("currency").get_source()
vm_ir = client.contract("currency").get_ir()
print(balance, raw_balance, simulated["result"])
Estimate chi, submit a transaction, and wait for finality:
import os
from xian_py import Wallet, Xian
wallet = Wallet(private_key=os.environ["XIAN_PRIVATE_KEY"])
with Xian("http://127.0.0.1:26657", wallet=wallet) as client:
estimate = client.estimate_chi(
"currency",
"transfer",
{"to": "bob", "amount": 5},
)
submission = client.token().transfer(
"bob",
5,
chi=estimate["estimated"],
mode="checktx",
wait_for_tx=True,
)
print(submission.tx_hash, submission.accepted, submission.finalized)
if submission.receipt:
print(submission.receipt.success)
Deploy a contract from source:
import os
from xian_py import Wallet, Xian
wallet = Wallet(private_key=os.environ["XIAN_PRIVATE_KEY"])
source = """
counter = Variable()
@construct
def seed():
counter.set(0)
@export
def increment():
counter.set(counter.get() + 1)
return counter.get()
"""
with Xian("http://127.0.0.1:26657", wallet=wallet) as client:
deployed = client.deploy_contract(
"con_counter",
source,
mode="checktx",
wait_for_tx=True,
)
result = client.contract("con_counter").send(
"increment",
mode="checktx",
wait_for_tx=True,
)
print(deployed.tx_hash, result.tx_hash)
deploy_contract builds the Xian VM deployment artifacts locally before
submitting. To submit artifacts produced by another toolchain, such as
xian contract build-artifacts, call submit_contract(name, deployment_artifacts)
directly.
Query BDS-backed history:
from xian_py import Xian
address = "bob"
with Xian("http://127.0.0.1:26657") as client:
recent_blocks = client.list_blocks(limit=5)
token_balances = client.get_token_balances(address, include_zero=False)
currency_txs = client.list_txs_by_contract("currency", limit=20)
transfer_events = client.list_events("currency", "Transfer", after_id=0)
balance_history = client.get_state_history(f"currency.balances:{address}")
print(
len(recent_blocks),
token_balances.total,
len(currency_txs),
len(transfer_events),
len(balance_history),
)
Use the async client in a service or worker:
import asyncio
import os
from xian_py import Wallet, XianAsync
async def main() -> None:
wallet = Wallet(private_key=os.environ["XIAN_PRIVATE_KEY"])
async with XianAsync("http://127.0.0.1:26657", wallet=wallet) as client:
chain_id = await client.get_chain_id()
balance = await client.token().balance_of(wallet.public_key)
submission = await client.token().transfer(
"bob",
5,
mode="checktx",
wait_for_tx=True,
)
print(chain_id, balance, submission.tx_hash)
asyncio.run(main())
Build a resumable SQLite projection:
import asyncio
import sqlite3
from xian_py import (
EventProjector,
EventSource,
SQLiteProjectionState,
XianAsync,
merged_event_payload,
)
async def main() -> None:
connection = sqlite3.connect("projection.db")
connection.row_factory = sqlite3.Row
connection.execute(
"CREATE TABLE IF NOT EXISTS transfers "
"(event_id INTEGER PRIMARY KEY, tx_hash TEXT, amount TEXT)"
)
state = SQLiteProjectionState(connection)
state.init_schema()
source = EventSource("currency", "Transfer")
async def apply_transfer(event, _hydrated):
payload = merged_event_payload(event)
connection.execute(
"INSERT OR IGNORE INTO transfers VALUES (?, ?, ?)",
(event.id, event.tx_hash, str(payload.get("amount"))),
)
state.set_int(source.key, event.id or 0)
connection.commit()
return True
async with XianAsync("http://127.0.0.1:26657") as client:
projector = EventProjector(
client=client,
event_sources=[source],
get_cursor=lambda event_source: state.get_int(event_source.key),
apply_event=apply_transfer,
)
await projector.sync_once()
asyncio.run(main())
Tune retries, transaction defaults, and watcher behavior:
from xian_py import (
RetryEvent,
RetryPolicy,
SubmissionConfig,
WatcherConfig,
Xian,
XianClientConfig,
)
def log_retry(event: RetryEvent) -> None:
print(
event.operation,
event.attempt,
event.max_attempts,
event.next_delay_seconds,
event.error,
)
config = XianClientConfig(
retry=RetryPolicy(max_attempts=5, on_retry=log_retry),
submission=SubmissionConfig(mode="checktx", wait_for_tx=True),
watcher=WatcherConfig(mode="auto", batch_limit=250),
)
with Xian("http://127.0.0.1:26657", config=config) as client:
print(client.get_chain_id())
Talk to a shielded relayer:
from xian_py import ShieldedRelayerClient
with ShieldedRelayerClient("http://127.0.0.1:38480") as relayer:
info = relayer.get_info()
quote = relayer.get_quote(
kind="shielded_command",
contract="shielded_note_token",
target_contract="currency",
)
print(info.available, quote.relayer_fee, quote.expires_at)
Principles
- One mental model, two clients. Sync and async clients stay aligned, so the same concepts work in scripts, services, and workers.
- Explicit transaction submission. Choose a broadcast mode (
async,checktx,commit) deliberately. The SDK does not hide retry, blocking, or finality behavior. - Plumbing, not policy. Read models and projector loops belong in application code. The SDK owns the repetitive plumbing (websocket delivery, catch-up cursors, raw CometBFT decoding, typed event conversion).
- Reference apps live alongside, not inside, the wheel. Examples demonstrate how to integrate Xian into Python systems but are not part of the published package.
- No node orchestration. Operator workflow and node lifecycle live in
xian-cliandxian-stack.
Key Directories
src/xian_py/— clients, wallet helpers, transactions, models, projectors.xian.py,xian_async.py— primary sync and async clients.application_clients.py— thin helper clients (contract,token,events,state_key).transaction.py,wallet.py,crypto.py— signing, building, and submitting transactions.projectors.py— reusable polling, ordering, and checkpoint primitives.models.py— typed transaction, event, block, and status models.shielded_relayer.py— client for the private-submission relayer.x402.py— native-Xian x402 exact-payment helpers.
examples/— service, worker, and reference-app examples built on the SDK (credits_ledger/,registry_approval/,workflow_backend/,x402_exact/,fastapi_service.py,event_worker.py,admin_job.py).tests/— SDK transport, decoding, and integration-shape coverage.docs/— compatibility notes and SDK backlog items.
Capabilities
- read current state via ABCI query paths and simulate readonly contract calls
- retrieve canonical contract source and Xian VM IR separately
- create, sign, and broadcast transactions with explicit
async,checktx, andcommitmodes; wait for final receipts - query indexed blocks, transactions, events, state history, and developer reward aggregates from BDS-backed nodes
- watch indexed events with websocket live delivery plus resumable BDS cursors
- watch raw live websocket events without BDS when low-latency delivery matters more than replayable cursors
- use thin helper clients for common patterns: contract, token, event, and state-key access
- sign, verify, settle, and retry native-Xian x402 exact-payment requests
- build SQLite-backed read models with the shared projector primitives, using CometBFT websocket wakeups and BDS cursor reconciliation
Event watching uses the CometBFT websocket directly and expects a BDS-enabled
node for cursorable indexed catch-up and canonical event IDs. Use
watch_live_events() or .watch_live() when you explicitly want
websocket-only, non-resumable delivery.
Core API Layers
Xian/XianAsync— primary sync and async clientsclient.contract(...),client.token(...),client.events(...),client.state_key(...)— thin helper clients for common patterns- Typed models and error classes — predictable result handling instead of raw dictionaries
EventProjector,EventSource,SQLiteProjectionState— reusable polling / ordering / checkpoint primitives for local projectionsWallet— Ed25519 signing helper for Xian transactions
Typical Use Cases
- backend APIs that read state and submit transactions
- background workers that react to indexed events
- automation jobs that reconcile or administer contracts
- local projections that mirror chain activity into an application-owned SQLite read model
- operator or integration scripts that need a clean Python surface over node RPCs and indexed queries
Example Paths
- general SDK examples: examples/README.md
- credits-ledger reference app: examples/credits_ledger/README.md
- registry-approval reference app: examples/registry_approval/README.md
- workflow-backend reference app: examples/workflow_backend/README.md
- x402 exact-payment reference app: examples/x402_exact/README.md
Validation
uv sync --group dev
uv run ruff check .
uv run ruff format --check .
uv run pytest
Related Docs
- AGENTS.md — repo-specific guidance for AI agents and contributors
- docs/README.md — index of internal docs
- docs/ARCHITECTURE.md — major components and dependency direction
- docs/BACKLOG.md — open work and follow-ups
- docs/API_COMPATIBILITY.md — compatibility surface and stability guarantees
- docs/SDK_REVIEW_BACKLOG.md — SDK review queue
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 xian_tech_py-0.4.16.tar.gz.
File metadata
- Download URL: xian_tech_py-0.4.16.tar.gz
- Upload date:
- Size: 53.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68d83fa57621ea642257256ec6cd14078d6d326b02249bf32d71b6e6e4ac9947
|
|
| MD5 |
984b05080c29eec5ac3178d6d5c8dccf
|
|
| BLAKE2b-256 |
603efa631b999b6537f478d9a923fc82121cefd013ab4962604c645deb0d7f7f
|
Provenance
The following attestation bundles were made for xian_tech_py-0.4.16.tar.gz:
Publisher:
release.yml on xian-technology/xian-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xian_tech_py-0.4.16.tar.gz -
Subject digest:
68d83fa57621ea642257256ec6cd14078d6d326b02249bf32d71b6e6e4ac9947 - Sigstore transparency entry: 1541739448
- Sigstore integration time:
-
Permalink:
xian-technology/xian-py@0071a0d67eb8f6c75e45ceba64b90f4dc1881366 -
Branch / Tag:
refs/tags/v0.4.16 - Owner: https://github.com/xian-technology
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0071a0d67eb8f6c75e45ceba64b90f4dc1881366 -
Trigger Event:
push
-
Statement type:
File details
Details for the file xian_tech_py-0.4.16-py3-none-any.whl.
File metadata
- Download URL: xian_tech_py-0.4.16-py3-none-any.whl
- Upload date:
- Size: 53.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b8d692eadb6356b79cb6ecdb9bf4db91fc889eb271db4553c2b37c93bd733a9
|
|
| MD5 |
6152852876c9a634d74be6cbb0e36322
|
|
| BLAKE2b-256 |
bfaa6cd4f894f5e910b9afcc239becdadde9fbf496c66ac1dcf0c3870cd1cccf
|
Provenance
The following attestation bundles were made for xian_tech_py-0.4.16-py3-none-any.whl:
Publisher:
release.yml on xian-technology/xian-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xian_tech_py-0.4.16-py3-none-any.whl -
Subject digest:
7b8d692eadb6356b79cb6ecdb9bf4db91fc889eb271db4553c2b37c93bd733a9 - Sigstore transparency entry: 1541739545
- Sigstore integration time:
-
Permalink:
xian-technology/xian-py@0071a0d67eb8f6c75e45ceba64b90f4dc1881366 -
Branch / Tag:
refs/tags/v0.4.16 - Owner: https://github.com/xian-technology
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@0071a0d67eb8f6c75e45ceba64b90f4dc1881366 -
Trigger Event:
push
-
Statement type: