SDK v0.4.4

Arkiv SDK for Python — Getting Started

Home

1) Arkiv "Hello, World!"

Create your account

This allows you to interact with Arkiv Testnet. Your is safe to share. Never share your .

Safety: The account you generate here is for Arkiv Testnet/sandbox use only. Never use it on any Mainnet.

2) Voting Board

You’ve written your first entity to Arkiv—now let’s build something that feels more alive. Using the same test account and client, we’ll create a tiny Voting Board: a simple structure where people can open proposals, cast votes, and read results directly from the chain.

This part of the guide shows how a few small entities can already form a collaborative application—powered by Arkiv.

You can keep experimenting right here in the CodePlayground, or set up the SDK locally to continue from your own environment.

1. Proposal

A single entity that defines what is being decided and how long the voting stays open (expiresIn).

2. Votes

Multiple small entities that reference the proposal by its entityKey and store each voter’s choice.

3. Tally

A read query that fetches all votes linked to a proposal and counts them—the simplest form of an on-chain result.

Next up: we’ll create the proposal entity—the anchor for every vote that follows.

3) Open Proposal

Create the decision “room”: a proposal entity with a bounded time window (Expires In). This is where votes will attach—still using the very same client/account you verified.

  • Goal: Write a proposal entity with an expiration window (Expires In).
  • Why it matters: Gives your vote stream a clear scope and predictable cost.
  • Success check: You get a proposal.entityKey (the proposal ID).
1proposal_key, _ = client.arkiv.create_entity(
2    payload = b"Proposal: Switch stand-up to 9:30?",
3    content_type = "text/plain",
4    attributes = {
5        "type": "proposal",
6        "status": "open",
7        "version": 1,
8    },
9    expires_in = client.arkiv.to_seconds(days=3),
10)
11
12print("Proposal key:", proposal_key)

4) Cast Votes

Attach votes to the proposal. Each vote is its own entity linked by proposalKey and attributed to a voter address. Same client, same journey—now with multiple actors.

  • Goal: Create votes with { type="vote", proposalKey, voter, choice }.
  • Why it matters: Votes are small, auditable facts you can query later.
  • Success check: Two vote keys print, both linked to your proposal.
1voter = client.eth.default_account
2
3yes_vote_key, _ = client.arkiv.create_entity(
4    payload=b"vote: yes",
5    content_type="text/plain",
6    attributes={
7        "type": "vote",
8        "proposalKey": proposal_key,
9        "voter": voter,
10        "choice": "yes",
11        "weight": 1,
12    },
13    expires_in = client.arkiv.to_seconds(days=3),
14)
15
16print("Vote cast:", yes_vote_key)

5) Batch Votes

Add many votes in one go—useful for demos, fixtures, or cross-proposal actions. You’re still operating with the same client and proposal context.

  • Goal: Create multiple vote entities in a single call.
  • Success check: Receipt count matches the number you pushed.
1vote_prefix = str(client.eth.default_account)
2creates: list[CreateOp] = []
3
4for i in range(5):
5    creates.append(CreateOp(
6        payload=f"vote: yes #{i+1}".encode("utf-8"),
7        content_type="text/plain",
8        attributes={
9            "type": "vote",
10            "proposalKey": proposal_key,
11            "voter": f"{vote_prefix}-bot{i}",
12            "choice": "yes" if i == 2 else "no",
13            "weight": 1,
14        },
15        expires_in = client.arkiv.to_seconds(days=3),
16    ))
17
18batch_receipt = client.arkiv.execute(Operations(creates=creates))
19print(f"Batch created: {len(batch_receipt.creates)} votes; tx={batch_receipt.tx_hash}")

6) Tally Votes

Read the chain back. Query annotated entities to compute the result. Because reads are deterministic, the same query yields the same answer.

  • Goal: Query votes by proposalKey and choice.
  • Success check: YES/NO counts match your inputs.
1query_yes = f'type = "vote" and proposalKey = "{proposal_key}" and choice = "yes"'
2query_no  = f'type = "vote" and proposalKey = "{proposal_key}" and choice = "no"'
3
4yes_votes = list(client.arkiv.query_entities(query_yes))
5no_votes  = list(client.arkiv.query_entities(query_no))
6
7print(f"Tallies - YES: {len(yes_votes)}, NO: {len(no_votes)}")

7) Watch Live

Subscribe to creations and extensions in real time. No polling—just logs as the story unfolds. Keep the same client; it already knows where to listen.

  • Goal: Subscribe to creation and extension events for votes (and proposals).
  • Success check: Console logs “[Vote created] …” or “[Vote extended] …”.
1def on_created(event, tx_hash):
2    try:
3        ent = client.arkiv.get_entity(event.key)
4        if ent.attributes and ent.attributes.get("type") in ("vote", "proposal"):
5            if ent.attributes.get("type") == "vote":
6                print(f"[Created] key={event.key} vote={ent.attributes.get('choice')} tx={tx_hash}")
7            else:
8                print(f"[Created] key={event.key} proposal={ent.payload} tx={tx_hash}")
9    except Exception as e:
10        print("[Created] error:", e)
11
12def on_extended(event, tx_hash):
13    try:
14        print(f"[Extended] key={event.key} new_expiration_block={event.new_expiration_block} tx={tx_hash}")
15    except Exception as e:
16        print("[Extended] error:", e)
17
18filter_created = client.arkiv.watch_entity_created(on_created, from_block="latest", auto_start=True)
19filter_extended = client.arkiv.watch_entity_extended(on_extended, from_block="latest", auto_start=True)
20
21# When done watching:
22# client.arkiv.cleanup_filters()

8) Extend Window

Need more time to decide? Extend the proposal’s Expires In. You’re updating the same entity you opened earlier—continuing the narrative of one decision from start to finish.

  • Goal: Extend the proposal entity by N blocks.
  • Success check: Console prints the new expiration block.
1seconds = 2000
2receipt = client.arkiv.extend_entity(proposal_key, extend_by=seconds)
3print("Proposal extension receipt:", receipt.extensions)

Setup & Installation

If you want to run this outside the browser (CI, local venv, a service), set up the Python SDK in your own project. This section shows virtualenv/Poetry and a placeholder script so you can run the same Voting Board flow from your terminal.

Prerequisites

Python 3.11+ recommended. Use a virtual environment (venv or Poetry).

Python 3.11+

3.12+ also fine

Virtual Environment

venv or Poetry

Ethereum Wallet

With test ETH for your RPC

RPC Endpoint

HTTP + (optionally) WS

Arkiv Testnet "Mendoza" Resources

Arkiv Testnet Status

Installation

1# Using venv + pip
2python3 -m venv .venv
3source .venv/bin/activate  # on Windows: .venv\Scripts\activate
4pip install arkiv-sdk --pre
5pip install testcontainers websockets dotenv

Environment Configuration

1# .env
2PRIVATE_KEY=0x...                        # use the (TEST) private key generated above
3RPC_URL=https://your.rpc.endpoint/rpc    # e.g. https://mendoza.hoodi.arkiv.network/rpc
4WS_URL=wss://your.rpc.endpoint/rpc/ws    # e.g. wss://mendoza.hoodi.arkiv.network/rpc/ws

Troubleshooting

Invalid sender: Your RPC may point to an unexpected network for your key. Verify your RPC URL is correct.

Insufficient funds: Get test ETH from the faucet; writes require gas.

No events seen? Ensure fromBlock is low enough and keep the process running to receive logs.