Skip to content

Recipes

Complete programs for common tasks. Change the URL to your Ogmios server.

import asyncio

import pyogmios
from pyogmios.types import RollForward


async def main() -> None:
    async with pyogmios.AsyncClient("ws://localhost:1337") as client:
        tip = await client.network.tip()
        async with client.chain_sync([tip]) as sync:
            async for event in sync:
                if isinstance(event, RollForward):
                    block = event.block
                    count = len(getattr(block, "transactions", None) or [])
                    print(f"block {block.height} at slot {block.slot}: {count} transactions")
                else:
                    print(f"rolled back to {event.point}")


asyncio.run(main())

Watch an address for payments

Follow the chain and report every output paid to an address. Outputs in a block that is later rolled back should be forgotten; this example only prints.

import asyncio

import pyogmios
from pyogmios.types import RollForward

ADDRESS = "addr_test1..."


async def main() -> None:
    async with pyogmios.AsyncClient("ws://localhost:1337") as client:
        async with client.chain_sync([await client.network.tip()]) as sync:
            async for event in sync:
                if not isinstance(event, RollForward):
                    continue
                for transaction in getattr(event.block, "transactions", None) or []:
                    for index, output in enumerate(transaction.outputs):
                        if output.address == ADDRESS:
                            ada = output.value.ada.lovelace / 1_000_000
                            print(f"{ada} ada in {transaction.id}#{index}")


asyncio.run(main())

Wait for a transaction to be on chain

Submit, then follow the chain from the tip at submission until a block contains the transaction.

import asyncio

import pyogmios
from pyogmios.types import RollForward


async def submit_and_wait(client: pyogmios.AsyncClient, cbor: bytes, timeout: float = 300) -> int:
    """Submit a signed transaction and return the height of the block that includes it."""
    tip = await client.network.tip()
    transaction_id = (await client.submit_transaction(cbor)).transaction.id
    async with asyncio.timeout(timeout), client.chain_sync([tip]) as sync:
        async for event in sync:
            if isinstance(event, RollForward):
                ids = {t.id for t in getattr(event.block, "transactions", None) or []}
                if transaction_id in ids:
                    return event.block.height
    raise AssertionError("unreachable")

For more certainty, wait for a few more blocks on top of it before treating it as final.

Carry on from where a program stopped

Save the points of the last blocks you processed, and start from them next time. Several points protect against the latest having been rolled back while you were away.

import json
from pathlib import Path

import pyogmios
from pyogmios.types import RollForward

CHECKPOINT = Path("checkpoint.json")

points = json.loads(CHECKPOINT.read_text()) if CHECKPOINT.exists() else ["origin"]
with pyogmios.Client("ws://localhost:1337") as client, client.chain_sync(points) as sync:
    recent: list[dict] = []
    for event in sync:
        if isinstance(event, RollForward) and getattr(event.block, "slot", None) is not None:
            recent = [{"slot": event.block.slot, "id": event.block.id}, *recent][:10]
            CHECKPOINT.write_text(json.dumps(recent))  # most recent first

Slots and times

Convert a slot to a time with the era summaries and the network's start time:

from datetime import timedelta

import pyogmios

with pyogmios.Client("ws://localhost:1337") as client:
    start = client.network.start_time()
    summaries = client.ledger_state.era_summaries()
    tip = client.network.tip()


def slot_time(slot: int):
    for era in summaries:
        if era.end is None or slot < era.end.slot:
            seconds = (
                era.start.time.seconds
                + (slot - era.start.slot) * era.parameters.slot_length.milliseconds / 1000
            )
            return start + timedelta(seconds=seconds)
    raise ValueError(f"slot {slot} is beyond the known eras")


print(f"the tip, slot {tip.slot}, was produced at {slot_time(tip.slot):%Y-%m-%d %H:%M:%S %Z}")

Era summaries only reach a little into the future (the "safe zone"), because a hard fork could change the slot length.

Governance at a glance

import pyogmios

with pyogmios.Client("ws://localhost:1337") as client:
    for proposal in client.ledger_state.governance_proposals():
        votes = {}
        for vote in proposal.votes:
            votes[vote.vote] = votes.get(vote.vote, 0) + 1
        print(proposal.action.type, "until epoch", proposal.until.epoch, votes)
    for representative in client.ledger_state.delegate_representatives():
        print(
            representative.type,
            getattr(representative, "id", ""),
            getattr(representative, "stake", ""),
        )
    committee = client.ledger_state.constitutional_committee()
    if committee is not None:
        print(len(committee.members), "committee members, quorum", committee.quorum)

Is the node ready?

import pyogmios

with pyogmios.Client("ws://localhost:1337") as client:
    health = client.health()
    if not health.is_synced:
        print(f"still syncing: {health.network_synchronization:.2%}")
    else:
        print(f"{health.network} is synced, era {health.current_era}, epoch {health.current_epoch}")

Using pyogmios with pycardano

To build and sign transactions with pycardano against an Ogmios node, use the Ogmios chain context from pccontext.