Python SDK for the PaperProof protocol on Sui
Project description
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
pysuigRPC 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
pip install paperproof-sdk-py
License and Identity
This SDK is licensed under Apache-2.0 to encourage broad integration, experimentation, commercial use, audits, forks, notebooks, automation, and tooling around PaperProof and Sui. The Apache-2.0 license applies to this SDK code. It does not grant rights to PaperProof trademarks, official status, official deployment authority, protocol governance authority, protected PaperProof contract source, protected official app source, or protected PaperProof documentation and brand materials.
PaperProof Protocol refers to the open protocol layer and official deployed protocol instances. PaperProof Labs refers to the originating team and maintainer of the official interface, SDKs, reference indexer, documentation, and brand identity.
You may use, fork, modify, and redistribute this SDK under Apache-2.0. If you publish a fork, wrapper, or integration, make its modified or unofficial status clear and do not imply endorsement by PaperProof Labs unless separately authorized.
Optional Sui and Walrus adapters:
pip install "paperproof-sdk-py[sui,walrus]"
For development:
python -m pip install -e ".[dev]"
pytest
python -m mypy paperproof
python -m ruff check .
python -m build
Quick Start
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.
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:
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:
reserve = sdk.service.build_reserve_preprint_code("0xYOUR_ADDRESS")
finalize = sdk.service.build_finalize_reserved_preprint("0xRESERVATION_ID", {
"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(reserve.calls[0].target)
print(finalize.calls[0].target)
sdk.service is the high-level business facade. Advanced callers can still use the low-level builders directly:
sdk.publishing.reserve_preprint_code("0xYOUR_ADDRESS")
sdk.publishing.finalize_reserved_preprint("0xRESERVATION_ID", {...})
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")
Preprints use a two-step protocol flow: reserve a preprint code, stamp that code into the PDF, then finalize the
reserved preprint. Direct preprint publishing is disabled by the mainnet contract and the SDK raises a clear
InvalidInputError if older code calls publish_preprint.
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 withcode,suggestion,details,cause, andto_dict().paperproof.eventsandpaperproof.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 manageasyncio.paperproof.sui: provider protocols split intoSuiDataProviderandSuiExecutionProvider, plus gRPC and JSON-RPC data adapters.
See 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.
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.
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:
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.
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:
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:
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:
from paperproof import PaperProofClient
sdk = PaperProofClient.mainnet(pysui_client=pysui_client)
reserve_execution, reservation = sdk.service.reserve_preprint_code("0xYOUR_ADDRESS")
# Stamp reservation.artifact_code into the PDF before finalizing the preprint content.
publish_execution, published = sdk.service.finalize_reserved_preprint(reservation.reservation_id, {
"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(reserve_execution.digest, reservation.artifact_code)
print(publish_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:
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:
sdk.publishing.publish_dataset({...})
sdk.publishing.add_dataset_version("0xSERIES", {...})
Operational builders mirror the protocol management paths exposed by the TypeScript SDK:
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:
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:
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:
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:
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:
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:
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:
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:
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. Mainnet writes require a funded/authenticated publisher or a local Walrus CLI; do not assume a free unauthenticated mainnet publisher exists.
For production-like uploads and reads, use the robust wrapper exposed as sdk.robust_walrus:
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)
For signed mainnet writes without an authenticated HTTP publisher, use a local Walrus CLI configured with your wallet:
from paperproof import RobustWalrusClient, WalrusCliClient
walrus = RobustWalrusClient(WalrusCliClient(cli_path="walrus"))
result = await walrus.write_blob(b"paperproof content", epochs=5, verify=True)
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.
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 apysuiclient 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 usingPaperProofSyncClient.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:
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:
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:
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:
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:
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:
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:
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
.envfiles. - 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:
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:
uv add paperproof-sdk-py
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file paperproof_sdk_py-0.2.3.tar.gz.
File metadata
- Download URL: paperproof_sdk_py-0.2.3.tar.gz
- Upload date:
- Size: 109.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a71178701b5bb55e6a7617f716d7448bf77607711e43de6aec64aaa23c875008
|
|
| MD5 |
ac3522e1ce68b41e1caf1a0d70311ed8
|
|
| BLAKE2b-256 |
8d15ec83090df910920baca57b195351a9e6974c1650df2dfa4fc9244dea769e
|
File details
Details for the file paperproof_sdk_py-0.2.3-py3-none-any.whl.
File metadata
- Download URL: paperproof_sdk_py-0.2.3-py3-none-any.whl
- Upload date:
- Size: 94.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cefb5aec15bf7a3b04c34d71f445e8996f2cc8445656c9878ef99ff6ebcad84
|
|
| MD5 |
66a23d7b23ee52c752eb58a195e64af2
|
|
| BLAKE2b-256 |
107d897a2b94c4b1e5dafffad2661dc2405c2436d18d187f581bd55dc7e8f7e8
|