Skip to main content

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:26657ws://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[compile]" # Offline source-to-artifact utilities
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])

# Browser/mobile wallet recovery. Also requires [hd].
wallet = Wallet.from_mnemonic(os.environ["XIAN_RECOVERY_PHRASE"])
second_account = Wallet.from_mnemonic(
    os.environ["XIAN_RECOVERY_PHRASE"], account_index=1
)

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, submission.receipt.chi_used)

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 and submit_contract submit cleartext source. Nodes compile submitted source and persist canonical IR, so normal deployment does not require the compile extra. Use compile only for offline artifact inspection.

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_contracts = client.get_token_contracts(limit=20)
    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_contracts.total,
        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-cli and xian-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, and commit modes; 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 clients
  • client.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 projections
  • Wallet — 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

Validation

uv sync --group dev --extra compile
uv run ruff check .
uv run ruff format --check .
uv run pytest

Related Docs

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

xian_tech_py-0.5.0.tar.gz (54.8 kB view details)

Uploaded Source

Built Distribution

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

xian_tech_py-0.5.0-py3-none-any.whl (54.9 kB view details)

Uploaded Python 3

File details

Details for the file xian_tech_py-0.5.0.tar.gz.

File metadata

  • Download URL: xian_tech_py-0.5.0.tar.gz
  • Upload date:
  • Size: 54.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for xian_tech_py-0.5.0.tar.gz
Algorithm Hash digest
SHA256 1128bee0fdb5e46b61f131d92c5fafaefa653eaaa623b4e4d71bbb7090263a51
MD5 2b42ea06eccf41d0306a864c5d7fef88
BLAKE2b-256 dd9ab931a5224c48c1cdc9fa4f85d53afafe476ecd5b2d55699b3b8fb8849b79

See more details on using hashes here.

Provenance

The following attestation bundles were made for xian_tech_py-0.5.0.tar.gz:

Publisher: release.yml on xian-technology/xian-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file xian_tech_py-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: xian_tech_py-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 54.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for xian_tech_py-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3890d0d1596f07144986007f9eceaa9b2a17d6d35ea97b2036562bdb75c1e67e
MD5 16c4584d6263d18ef8eaf824d8bebf4a
BLAKE2b-256 91b15ee9feaaed00f5e53cc3b03f6abc84e84b23a8a47ff82fcb897447d732fb

See more details on using hashes here.

Provenance

The following attestation bundles were made for xian_tech_py-0.5.0-py3-none-any.whl:

Publisher: release.yml on xian-technology/xian-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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