Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,62 @@
# stackcoin-python

Python client for interacting with the StackCoin HTTP API.
Python library for the StackCoin API. Provides a typed async REST client and a WebSocket gateway for real-time events.

https://pypi.org/project/stackcoin/
## Install

```sh
pip install stackcoin
```

Requires Python 3.13+. Dependencies: `httpx`, `pydantic>=2`, `websockets`.

## Quick start

```python
import asyncio
import stackcoin

async def main():
async with stackcoin.Client(token="...") as client:
me = await client.get_me()
print(f"{me.username}: {me.balance} STK")

events = await client.get_events()
for event in events:
print(f"[{event.type}] {event.data}")

asyncio.run(main())
```

## Gateway (real-time events)

```python
import stackcoin

gateway = stackcoin.Gateway(token="...")

@gateway.on("transfer.completed")
async def on_transfer(event: stackcoin.TransferCompletedEvent):
print(f"Transfer of {event.data.amount} STK from #{event.data.from_id} to #{event.data.to_id}")

@gateway.on("request.accepted")
async def on_accepted(event: stackcoin.RequestAcceptedEvent):
print(f"Request #{event.data.request_id} accepted")

await gateway.connect()
```

## Examples

- `./examples/basic_usage.py`, bare bones creation of `AuthenticatedClient`, checking balance, making some requests, etc.
- `./examples/simple_cli.py`, thorough usage of the API in a bare-bones command line interface.
- `examples/basic_usage.py` -- REST client basics (balance, requests, transactions)
- `examples/simple_cli.py` -- interactive REPL with live gateway events

---
## Development

Models are generated from the StackCoin OpenAPI spec using `datamodel-codegen`:

```sh
STACKCOIN_ROOT=/path/to/StackCoin just generate
```

This package is generated by running [openapi-python-client](https://github.com/openapi-generators/openapi-python-client) against [StackCoin's OpenAPI specification](https://stackcoin.world/swaggerui), the `./stackcoin` directory in this repository is the package, and is generated by running `just generate` (or running the commands under `generate` in the `Justfile`).
This regenerates `stackcoin/stackcoin/models.py` from `openapi.json`.
109 changes: 29 additions & 80 deletions examples/basic_usage.py
Original file line number Diff line number Diff line change
@@ -1,96 +1,44 @@
"""Basic usage of the stackcoin library."""

import asyncio
import os
from stackcoin_python import AuthenticatedClient
from stackcoin_python.models import (
CreateRequestParams,
BalanceResponse,
CreateRequestResponse,
RequestsResponse,
TransactionsResponse,
UsersResponse,
)
from stackcoin_python.api.default import (
stackcoin_self_balance,
stackcoin_create_request,
stackcoin_users,
stackcoin_requests,
stackcoin_transactions,
)


async def main(token, base_url):
client = AuthenticatedClient(base_url=base_url, token=token)

async with client as client:
print("Getting balance")
import stackcoin

my_balance = await stackcoin_self_balance.asyncio(client=client)

if not isinstance(my_balance, BalanceResponse):
raise Exception("Failed to get balance")
async def main(token: str, base_url: str):
async with stackcoin.Client(token, base_url=base_url) as client:
# Who am I?
me = await client.get_me()
print(f"Logged in as {me.username} with balance {me.balance} STK")

print(f"Logged in as {my_balance.username} with balance {my_balance.balance}")

print("Creating request")

request = await stackcoin_create_request.asyncio(
client=client,
user_id=2,
body=CreateRequestParams(
amount=100,
label="pay up buddy",
),
# Create a request
req = await client.create_request(
to_user_id=2, amount=100, label="pay up buddy"
)

if not isinstance(request, CreateRequestResponse):
raise Exception("Failed to create request")

print(
f"Created request {request.request_id} to {request.responder.username} with amount {request.amount}"
f"Created request #{req.request_id} to {req.responder.username} for {req.amount} STK"
)

print("Getting requests")

requests = await stackcoin_requests.asyncio(client=client)

if not isinstance(requests, RequestsResponse):
raise Exception("Failed to get requests")

if not isinstance(requests.requests, list):
raise Exception("Failed to get requests")

for request in requests.requests:
print(
f"Request {request.id} to {request.responder.username} with amount {request.amount}"
)

print("Getting transactions")

transactions = await stackcoin_transactions.asyncio(client=client)

if not isinstance(transactions, TransactionsResponse):
raise Exception("Failed to get transactions")

if not isinstance(transactions.transactions, list):
raise Exception("Failed to get transactions")
# List pending requests
requests = await client.get_requests()
print(f"\n{len(requests)} request(s):")
for r in requests:
print(f" #{r.id} -> {r.responder.username}: {r.amount} STK ({r.status})")

for transaction in transactions.transactions:
# List recent transactions
transactions = await client.get_transactions()
print(f"\n{len(transactions)} transaction(s):")
for txn in transactions:
print(
f"Transaction {transaction.id} from {transaction.from_.username} to {transaction.to.username} with amount {transaction.amount}"
f" #{txn.id} {txn.from_.username} -> {txn.to.username}: {txn.amount} STK"
)

print("Getting users")

users = await stackcoin_users.asyncio(client=client)

if not isinstance(users, UsersResponse):
raise Exception("Failed to get users")

if not isinstance(users.users, list):
raise Exception("Failed to get users")

for user in users.users:
print(f"User {user.id} {user.username}")
# List users
users = await client.get_users()
print(f"\n{len(users)} user(s):")
for user in users:
print(f" #{user.id} {user.username} ({user.balance} STK)")


if __name__ == "__main__":
Expand All @@ -101,6 +49,7 @@ async def main(token, base_url):
print("Token is required")
exit(1)

# Can be omitted to hit production (https://stackcoin.world).
# Set STACKCOIN_BASE_URL for local development, e.g. http://localhost:4000
base_url = os.getenv("STACKCOIN_BASE_URL", "https://stackcoin.world")

asyncio.run(main(token, base_url))
Loading