Metadata-Version: 2.4
Name: paperproof-sdk-py
Version: 0.2.2
Summary: Python SDK for the PaperProof protocol on Sui
Author-email: PaperProof Labs <paperproof.labs@gmail.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/PaperProofLabs/paperproof-sdk-py
Project-URL: Repository, https://github.com/PaperProofLabs/paperproof-sdk-py
Project-URL: Issues, https://github.com/PaperProofLabs/paperproof-sdk-py/issues
Keywords: paperproof,sui,walrus,sdk,python
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: httpx>=0.26
Requires-Dist: typing-extensions>=4.9
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: mypy>=1.8; extra == "dev"
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Provides-Extra: sui
Requires-Dist: pysui>=0.98; extra == "sui"
Provides-Extra: walrus
Requires-Dist: walrus-python>=0.1.0; extra == "walrus"
Provides-Extra: all
Requires-Dist: pysui>=0.98; extra == "all"
Requires-Dist: walrus-python>=0.1.0; extra == "all"
Dynamic: license-file

# PaperProof SDK for Python

Python SDK for the PaperProof protocol on Sui.

This first release focuses on local correctness and developer ergonomics:

- deployment constants for the current PaperProof mainnet deployment;
- typed protocol inputs and event models;
- strict input validation matching the Move contract limits;
- transaction-plan builders for publishing, comments, likes, and governance;
- operator/admin transaction-plan builders for protocol pause, status changes, fee changes, authority changes, migrations, and upgrade-cap flows;
- read helpers that return typed views for root, series, versions, comments trees, likes books, proposals, governance vault/config, and fee manager objects;
- deployment verification and deployment manifest update checks;
- coin balance pagination, summary, and selection helpers;
- Move abort explanations for common PaperProof/Sui failures;
- trusted event parsing helpers for indexers and automation scripts;
- real `pysui` gRPC object/balance/coin reads;
- GraphQL-first, pluggable event/history query providers with explicit JSON-RPC compatibility fallback;
- Walrus HTTP read/write helpers;
- structured errors that preserve causes and suggestions.

The Python SDK is intended for server-side scripts, research automation, indexers, maintenance tooling, and integration
tests. Browser wallet flows are intentionally handled by the TypeScript SDK, while this package keeps Python users close
to the same protocol surface through typed builders, reads, event parsing, and optional `pysui` execution.

The SDK does not run mainnet integration tests by default. Network tests should be enabled explicitly by downstream projects.

## Install

```bash
pip install paperproof-sdk-py
```

Optional Sui and Walrus adapters:

```bash
pip install "paperproof-sdk-py[sui,walrus]"
```

For development:

```bash
python -m pip install -e ".[dev]"
pytest
python -m mypy paperproof
python -m ruff check .
python -m build
```

## Quick Start

```python
from paperproof import PaperProofClient

sdk = PaperProofClient.mainnet()
print(sdk.transport)  # "grpc"
```

The default data transport is gRPC to match the TypeScript and Rust SDKs. With the optional `sui` extra installed, the
bundled `GrpcSuiProvider` uses `pysui.SuiGrpcClient` for object, balance, and coin reads. Historical events use the
pluggable query-provider layer, which defaults to Sui GraphQL and only falls back to JSON-RPC when you explicitly build a
JSON-RPC data provider.

```python
from paperproof import EventQuery, PaperProofClient

sdk = PaperProofClient.mainnet(query_transport="graphql")
votes = await sdk.query.query_governance_vote_cast_events(EventQuery("all", limit=20, descending=True))
print(votes.provider, len(votes.events))
```

Governance event helpers query both the original and current governance packages, require the configured PaperProof root
or registry id, and deduplicate by transaction digest plus event sequence. This avoids the common indexer/frontend
mistake of reporting "no governance history" after a package upgrade.

Use JSON-RPC only when you deliberately need compatibility or historical event queries:

```python
from paperproof import PaperProofClient

sdk = PaperProofClient.mainnet_jsonrpc(query_transport="fallback")

root = await sdk.get_root_view()
print(root.id, root.paused)
```

Build a transaction without signing or submitting:

```python
tx = sdk.service.build_publish_preprint({
    "title": "A Minimal PaperProof Preprint",
    "abstract_text": "This transaction plan can be compiled by a Sui adapter.",
    "authors": ["PaperProof Labs"],
    "keywords": ["paperproof", "sui"],
    "field": "decentralized publishing",
    "license": "CC-BY-4.0",
    "page_count": 1,
    "content_hash": "sha256:example",
    "walrus_blob_id": "example-blob",
    "walrus_blob_object_id": "0x1",
    "content_type": "application/pdf",
})

print(tx.calls[0].target)
```

`sdk.service` is the high-level business facade. Advanced callers can still use the low-level builders directly:

```python
sdk.publishing.publish_preprint({...})
sdk.comments.add_onchain_comment({"tree_id": "0xTREE", "content": "hello"})
sdk.governance.vote_no({"proposal_id": "0xPROPOSAL", "coin_id": "0xPPRF_COIN"})
sdk.ops.set_governance_authority("0xNEW_AUTHORITY")
```

## API Layers

- `PaperProofClient`: deployment-aware entry point for reads, event queries, builders, Walrus helper, and services.
- `sdk.service`: high-level helpers for common publish/comment/like/governance/owner-transfer/query flows.
- `sdk.publishing`, `sdk.comments`, `sdk.governance`, `sdk.ops`: low-level transaction-plan builders for advanced PTB composition.
- `paperproof.types`: stable dataclasses and typed dictionaries for deployments, inputs, event pages, transaction results, and object views.
- `paperproof.errors`: structured errors with `code`, `suggestion`, `details`, `cause`, and `to_dict()`.
- `paperproof.events` and `paperproof.events_trust`: typed event extraction plus canonical package/root filtering for indexers.
- `paperproof.coin_utils`: coin pagination, selection, and balance summaries.
- `paperproof.query_providers`: GraphQL, JSON-RPC, fallback, and custom event/history query providers.
- `paperproof.watch`: polling event watchers with cursors, dedupe, callbacks, and high-level PaperProof event helpers.
- `paperproof.analytics`: CSV/JSONL/DataFrame export, paged event collection, governance history, and batched object reads.
- `paperproof.sync`: synchronous facade for ordinary scripts that do not want to manage `asyncio`.
- `paperproof.sui`: provider protocols split into `SuiDataProvider` and `SuiExecutionProvider`, plus gRPC and JSON-RPC data adapters.

See [docs/API.md](docs/API.md) for a compact API map covering reads, events, builders, execution, Walrus helpers, deployment checks, and errors.

## Watch API

`sdk.watch` wraps the query layer for scripts, bots, lightweight indexers, and services that want a simple polling
interface without hand-building event type strings. Watchers keep their cursor, deduplicate by transaction digest plus
event sequence, and can be driven one tick at a time or started as an asyncio background task.

```python
from paperproof import PaperProofClient, WatchOptions

sdk = PaperProofClient.mainnet(query_transport="graphql")

async def on_comment(event):
    print(event.transaction_digest, event.parsed_json)

watcher = sdk.watch.watch_comment_added_events(
    WatchOptions(limit=20, interval_seconds=5, on_event=on_comment)
)

page = await watcher.next()  # one polling tick
watcher.start()              # or keep polling in the background
await watcher.done
```

High-level helpers cover publishing, versioning, comments, likes, status changes, owner transfers, and governance
lifecycle events. Governance watchers query both the current and original governance package so upgraded deployments do
not appear to have empty voting history.

## Analytics And Notebook Helpers

Python users often want to inspect protocol state, export events, or work in notebooks without learning every Move event
shape. The SDK exposes lightweight helpers for that workflow and keeps pandas optional.

```python
from paperproof import (
    PaperProofClient,
    collect_events,
    events_to_csv,
    events_to_dataframe,
    events_to_jsonl,
    query_governance_history,
)

sdk = PaperProofClient.mainnet(query_transport="graphql")

events = await collect_events(sdk.query, EventQuery("all", limit=50, descending=True), max_pages=2)
csv_text = events_to_csv(events)
jsonl_text = events_to_jsonl(events)

# Higher-level analytics queries:
recent = await sdk.query.get_recent_artifacts(limit=20)
mine = await sdk.query.get_my_artifacts("0xMY_ADDRESS", limit=20)
votes = await sdk.query.get_my_votes("0xMY_ADDRESS", limit=20)

# If pandas is installed:
df = events_to_dataframe(events)

governance = await query_governance_history(sdk.query, limit=20)
print({name: len(items) for name, items in governance.items()})
```

Use `batch_get_objects(sdk, object_ids, chunk_size=50)` for simple chunked object reads in scripts. These helpers sit on
top of the normal query/read clients, so advanced users can still drop down to `sdk.query` or custom providers.

## Synchronous Scripts

For operations and research scripts, use `PaperProofSyncClient` when you prefer normal blocking calls:

```python
from paperproof import EventQuery, PaperProofSyncClient, events_to_csv

sdk = PaperProofSyncClient.mainnet(query_transport="graphql")

root = sdk.get_root_view()
events = sdk.collect_events(EventQuery("all", limit=20, descending=True), max_pages=1)
my_votes = sdk.get_my_votes("0xMY_ADDRESS", limit=10)
print(root.id)
print(events_to_csv(events))
```

The sync facade is for ordinary scripts and CLIs. In notebooks that already support top-level `await`, the async
`PaperProofClient` is still the better fit. If the sync facade is called inside an active event loop, it raises a clear
`InvalidInputError` with a suggestion instead of hiding an event-loop failure.

## CLI

The package installs a small read-only helper CLI. It is meant for quick inspection and exports, not signing or mainnet
writes.

```bash
paperproof query-root
paperproof query-events --kind all --limit 20 --descending --format csv --output paperproof-events.csv
paperproof query-events --kind move-event-type --value 0x...::publishing::ArtifactPublishedEvent --format jsonl
paperproof governance-history --limit 20 --format json --output governance-history.json
paperproof recent-artifacts --limit 20 --format csv --output artifacts.csv
paperproof my-artifacts --address 0xMY_ADDRESS --limit 20 --format jsonl
paperproof my-votes --address 0xMY_ADDRESS --limit 20 --format csv
```

Before installation, run the same commands with:

```bash
python -m paperproof.cli query-root
```

## Transaction Plans And pysui Execution

The Python SDK first returns a neutral `TransactionPlan`. This keeps the public API stable while the Python Sui
ecosystem evolves and lets applications inspect or batch protocol calls before signing.

When the optional `sui` extra is installed, `PysuiTransactionCompiler` and `PysuiTransactionService` can compile those
plans into `pysui` programmable transactions:

```python
from paperproof import MAINNET_DEPLOYMENT, PaperProofClient, PysuiTransactionService

sdk = PaperProofClient(MAINNET_DEPLOYMENT)
plan = sdk.comments.add_onchain_comment({
    "tree_id": "0xTREE",
    "content": "Looks good.",
})

service = PysuiTransactionService(MAINNET_DEPLOYMENT, pysui_client)
tx = service.build(plan, sender="0xSENDER")
inspect_result = service.inspect(plan, sender="0xSENDER")
```

For a higher-level write API, inject an execution provider or a `pysui_client` when constructing the SDK:

```python
from paperproof import PaperProofClient

sdk = PaperProofClient.mainnet(pysui_client=pysui_client)

execution, published = sdk.service.publish_preprint({
    "title": "A Minimal PaperProof Preprint",
    "abstract_text": "Submitted through the high-level Python SDK.",
    "authors": ["PaperProof Labs"],
    "keywords": ["paperproof"],
    "field": "decentralized publishing",
    "license": "CC-BY-4.0",
    "page_count": 1,
    "content_hash": "sha256:example",
    "walrus_blob_id": "example-blob",
    "walrus_blob_object_id": "0x1",
    "content_type": "application/pdf",
})

print(execution.digest, published.series_id, published.comments_tree_id)
```

High-level write helpers return `(TransactionExecutionResult, typed_result)` for publish, add-version, comment,
like/unlike, proposal creation, proposal finalization, and proposal execution. Vote helpers return the normalized
execution result directly.

Robust execution is available for scripts that need clearer failure handling around transient RPC/object-version errors:

```python
from paperproof import RetryOptions

execution, comment = sdk.service.add_onchain_comment(
    {
        "tree_id": "0xTREE",
        "content": "Reviewed through PaperProof.",
    },
    retry=RetryOptions(attempts=3, base_delay_seconds=0.5),
    rebuild_retry=True,
)
```

`rebuild_retry=True` rebuilds the transaction plan before each retry, which is useful when a prior attempt may have
observed stale object versions. `expect_failure=True` is intended for negative tests and guarded probes: it returns a
normalized `TransactionExecutionResult` marked as `expected_failure` instead of treating the failure as an SDK error.
All execution paths normalize `digest`, `events`, `object_changes`, `balance_changes`, `effects`, `status`, and
`error` so callers can log or inspect failures consistently.

The compiler is deliberately conservative. It supports object arguments, pure arguments, `Option<ID>`,
`Option<Coin<SUI>>`, `vector<MetadataAttribute>` including empty metadata, and returned-object transfers emitted by the
SDK builders. If a shape is ambiguous, it raises `TransactionBuildError` with a suggestion instead of building a
transaction that may submit the wrong object.

Publishing builders cover all built-in artifact types for both first publish and add-version flows:

```python
sdk.publishing.publish_dataset({...})
sdk.publishing.add_dataset_version("0xSERIES", {...})
```

Operational builders mirror the protocol management paths exposed by the TypeScript SDK:

```python
pause_tx = sdk.ops.set_protocol_paused({
    "operator_permit_id": "0xPERMIT",
    "paused": True,
})

authority_tx = sdk.ops.set_governance_authority("0xNEW_AUTHORITY")
```

## Reads And Views

The raw read methods return `ReadObject | None`; `require_object` throws `ObjectNotFoundError` when absence is exceptional. Typed view helpers parse common Move object fields into Python dataclasses:

```python
root = await sdk.get_root_view()
config = await sdk.get_governance_config_view()
liked = await sdk.has_liked("0xLIKES_BOOK", "0xADDRESS")
```

High-level state aggregation:

```python
state = await sdk.service.get_series_state("0xSERIES")
print(state["series"], state["comments_tree"], state["likes_book"])
```

Deployment helpers are useful for scripts and services that need to detect stale package or object IDs:

```python
from paperproof import verify_deployment, check_deployment_update

verification = await verify_deployment(sdk)
update = await check_deployment_update()
```

Coin utilities help select usable owned coin objects before building transactions:

```python
from paperproof import select_coins_covering

selection = select_coins_covering(owner, sdk.deployment.coin_types.pprf, coins, 100_000_000_000)
```

Move abort explanations turn common protocol errors into actionable developer messages:

```python
from paperproof import explain_paperproof_error

explanation = explain_paperproof_error(error)
print(explanation.title, explanation.suggestion)
```

## Trusted Events

Indexers should treat the configured PaperProof package IDs and root object as the trust anchor. Use:

```python
from paperproof.events_trust import filter_canonical_paperproof_events

trusted = filter_canonical_paperproof_events(response, MAINNET_DEPLOYMENT)
```

Paged queries are available through the service facade:

```python
from paperproof import EventQuery

page = await sdk.service.query_canonical_event_page(
    EventQuery(
        "move_event_type",
        f"{MAINNET_DEPLOYMENT.packages.publishing}::publishing::ArtifactPublishedEvent",
        limit=20,
        descending=True,
    )
)
```

The SDK also includes typed extractors for common transaction responses:

```python
from paperproof import (
    extract_publish_result,
    extract_add_version_result,
    extract_comment_result,
    extract_proposal_result,
    extract_like_event,
)

published = extract_publish_result(response, MAINNET_DEPLOYMENT)
proposal = extract_proposal_result(response, MAINNET_DEPLOYMENT)
liked = extract_like_event(response, MAINNET_DEPLOYMENT)
```

Pass `MAINNET_DEPLOYMENT` to filter by the canonical PaperProof package IDs before parsing. Without a deployment,
extractors parse by struct name only and are useful for local mocks.

## Walrus

`WalrusClient` wraps HTTP publisher and aggregator endpoints. It is intentionally small and can be combined with future official or community Walrus Python SDKs.

For production-like uploads and reads, use the robust wrapper exposed as `sdk.robust_walrus`:

```python
result = await sdk.robust_walrus.write_blob(b"paperproof content", epochs=5, verify=True)
data = await sdk.robust_walrus.read_blob(result.blob_id, expected_digest=result.digest)
```

The robust helper retries transient HTTP failures, parses common Walrus publisher response shapes, returns the blob id
and blob object id when present, and can verify reads with a `sha256:` digest. Digest verification is local integrity
checking; it does not replace the protocol's on-chain Walrus blob object reference.

For application code, prefer `PaperProofContentService`. It provides the same
PaperProof-shaped content lifecycle as the TypeScript SDK: publish content,
read and verify it, extend storage, and transfer owned blob objects without
making users juggle low-level Walrus response shapes.

```python
from paperproof import PaperProofContentService

content = PaperProofContentService(sdk.robust_walrus)
published = await content.publish_content(
    b"paperproof content",
    epochs=5,
    content_type="text/plain",
    share=False,
)
await content.extend_content(published.blob_object_id, epochs=2, signer_address="0x...")
```

The Python SDK currently exposes Walrus through a small HTTP/adapter layer.
When a project needs fully signed Walrus mainnet writes, provide a client
adapter backed by a trusted Walrus execution backend. The high-level
`PaperProofContentService` API is stable so that backend can be upgraded
without changing application code.

## Examples

- `examples/build_transaction.py`: build a publish transaction plan without executing.
- `examples/query_example.py`: read root and governance config from mainnet.
- `examples/parse_events.py`: parse typed PaperProof events from a transaction-like response.
- `examples/indexer_helper_example.py`: query and trust-filter canonical events.
- `examples/execute_transaction_example.py`: show where to attach a `pysui` client for build/inspect/execute.
- `examples/deployment_check.py`: verify configured mainnet package/object bindings and deployment drift.
- `examples/query_thread_example.py`: query a known comment thread with explicit environment inputs.
- `examples/robust_walrus_example.py`: guarded Walrus upload/read/verify flow.
- `examples/export_events_example.py`: export recent canonical PaperProof events to CSV and JSONL.
- `examples/notebook_analytics_example.py`: notebook-style read/query/export workflow with optional pandas.
- `examples/sync_facade_example.py`: blocking script workflow using `PaperProofSyncClient`.
- `examples/sqlite_sink_example.py`: write recent artifact and governance vote data into a local SQLite database.
- `examples/mainnet_analytics_validation.py`: read live mainnet artifacts/votes, pick real author/voter addresses, and export CSV/JSONL/SQLite.
- `examples/mainnet_guarded_rw_probe.py`: guarded maintainer probe for RPC and asset conservation.
- `examples/mainnet_full_smoke_plan.py`: guarded full mainnet scenario.
- `examples/mainnet_four_user_journey.py`: guarded four-account mainnet journey.

## Live Mainnet Analytics Validation

Most unit tests use mock providers so CI remains deterministic and never depends on private keys, network timing, or
mutable chain history. Use the live validation example when you want to prove the usability helpers against current
mainnet data:

```bash
python examples/mainnet_analytics_validation.py --sqlite
```

The script performs only read-only GraphQL queries. It fetches recent real artifact publish events, selects a real
author and calls `get_my_artifacts()`, fetches real governance vote events, selects a real voter and calls
`get_my_votes()`, then exports CSV, JSONL, summary JSON, and optionally SQLite files under
`examples/artifacts/mainnet-validation`.

Equivalent CLI checks:

```bash
python -m paperproof.cli recent-artifacts --limit 5 --format json
python -m paperproof.cli governance-history --limit 5 --format json
python -m paperproof.cli my-artifacts --address 0x... --limit 5 --format json
python -m paperproof.cli my-votes --address 0x... --limit 5 --format json
```

For the 2026-05-10 validation run, these commands read live mainnet records for real authors/voters and the SQLite
example wrote 50 artifact rows plus 39 vote-event rows. A guarded SUI roundtrip also succeeded without changing PPRF
balances: `8iuRVo9P74uEvxaV89YmN9yjfCuXBgRq6PkxM7EQNBuo` and
`9iHtW6462Y9aTvsJFvG7c5fKxemm5Y7qd9PPLZMFthu5`.

## Guarded Mainnet Probes

Mainnet read/write probes are deliberately opt-in. They are intended for maintainers who need to validate the SDK against the live PaperProof deployment without risking protocol assets.

Read-only and dry-run harness checks require an external env file with `ADDR_1` through `ADDR_4` and matching private keys:

```bash
PAPERPROOF_RUN_MAINNET_WRITES=1 \
PAPERPROOF_MAINNET_ENV=/absolute/path/to/.env \
pytest tests/test_mainnet_harness.py::test_mainnet_harness_dry_run_sui_transfer
```

The guarded example first snapshots PPRF balances, dry-runs a tiny SUI transfer, fuzz-reads canonical PaperProof objects, and verifies that total PPRF did not change:

```bash
PAPERPROOF_RUN_MAINNET_WRITES=1 \
PAPERPROOF_MAINNET_ENV=/absolute/path/to/.env \
python examples/mainnet_guarded_rw_probe.py
```

To send the tiny SUI roundtrip, set a second confirmation flag:

```bash
PAPERPROOF_RUN_MAINNET_WRITES=1 \
PAPERPROOF_CONFIRM_SMALL_SUI_WRITE=1 \
PAPERPROOF_MAINNET_ENV=/absolute/path/to/.env \
python examples/mainnet_guarded_rw_probe.py
```

The probe only transfers a small amount of SUI between configured accounts and sends it back. It does not touch PPRF, WAL, comments, publishing, or governance state.

For parity with the TypeScript SDK full smoke scenario, the Python SDK also ships a guarded planner:

```bash
python examples/mainnet_full_smoke_plan.py --validate
```

It builds and validates the same class of operations used by the TypeScript full smoke example: preprint publish,
metadata update, add-version, comments and replies, hidden-comment negative path, blob comments, software and generic
file artifacts, likes/unlikes, tree lock/reopen, owner transfer checks, governance proposal/vote/resolve/claim paths,
and local boundary failures. Add `--with-env` to load the four-account env file, snapshot balances, fuzz-read mainnet
objects, and verify that the total PPRF balance across the configured accounts is unchanged:

```bash
PAPERPROOF_RUN_MAINNET_WRITES=1 \
PAPERPROOF_MAINNET_ENV=/absolute/path/to/.env \
python examples/mainnet_full_smoke_plan.py --validate --with-env
```

The Python full-write parity mode is intentionally gated. The SDK now includes a conservative `pysui` compiler for the
argument shapes emitted by PaperProof builders, but high-volume mainnet write runs should still begin with build and
inspect passes. This keeps the test surface aligned with the TypeScript SDK without risking PPRF conservation or
governance recovery.

## Security Notes

- Do not commit private keys or `.env` files.
- Treat likes/comments/events as activity signals unless filtered against the canonical deployment.
- Full mainnet write smoke tests are explicitly gated and should be run only by maintainers with throwaway or operational accounts.
- PPRF governance voting power remains a protocol-level design choice and is not mitigated inside this SDK.

## Publishing

Build and inspect the package before uploading:

```bash
python -m ruff check .
python -m mypy paperproof
python -m pytest -q
python -m build
twine check dist/*
```

The package supports regular `pip` and `uv` installs:

```bash
uv add paperproof-sdk-py
```
