Python SDK and CLI for Polyplace — an on-chain pixel grid on Polygon
Project description
polyplace-client
Python SDK and CLI for Polyplace — a 1000×1000 on-chain pixel grid on Polygon. Rent cells with PLACE tokens (free from the faucet), set their colours, and watch your pixels appear on the live grid viewer.
This package is the way to paint: the web frontend is a read-only viewer, and all contract interaction happens here.
| Grid | 1000×1000 cells, one colour each |
| Rent | 1 PLACE per cell, lasts 7 days |
| Faucet | 150 PLACE per claim, 1-day cooldown |
| Gas | POL on Polygon mainnet — real, but a few cents' worth paints hundreds of cells |
| Contracts | polyplace-contracts on Polygon (chain 137) |
Install
pip install polyplace-client # or: uv tool install polyplace-client
Quickstart
Reads need zero configuration — they use the public Polygon RPC and the live deployment out of the box:
polyplace grid params # rent price and duration
polyplace grid cell 500 500 # who owns a cell, what colour, when it expires
polyplace grid free --limit 10 # find available cells
To paint, you need a funded key:
# 1. Create a wallet (any Ethereum key works — this is a quick way)
python -c "from eth_account import Account; a = Account.create(); print(a.address); print(a.key.hex())"
export POLYPLACE_PRIVATE_KEY=0x... # the key printed above
# 2. Fund it with a little POL for gas (Polygon mainnet — real POL,
# but a few cents' worth paints hundreds of cells)
# 3. Claim free PLACE tokens (150 PLACE, once a day)
polyplace faucet claim
# 4. Rent a pixel — one transaction, no token approval needed
polyplace grid rent 500 500 '#FF0000'
Your pixel is now live on the grid viewer within seconds.
PLACE rents are free (faucet-funded) and gas is a few cents — it's fine to keep this key in your shell profile or an
.envfile.
Paint an image
One cell per opaque pixel; transparent pixels are skipped, so sprites with alpha just work:
polyplace grid paint sprite.png --at 480 480 --dry-run # preview cost first
polyplace grid paint sprite.png --at 480 480 # then for real
polyplace grid paint photo.png --at 200 200 --scale 40 # resize to 40 cells wide
The preview shows cells × rent price, transaction count (≤100 cells per transaction), and your balance. Cells currently rented by someone else are skipped and reported; your own active rentals are repainted. A 40×40 image is 1,600 cells = 1,600 PLACE, so big art means saving up a few days of faucet claims — and rentals last 7 days before you need to renew.
CLI reference
| Command | What it does |
|---|---|
polyplace grid cell X Y |
State of one cell (renter, colour, expiry) |
polyplace grid rent X Y '#RRGGBB' |
Rent a cell and set its colour (single tx, EIP-2612 permit) |
polyplace grid color X Y '#RRGGBB' |
Recolour a cell you already rent |
polyplace grid bulk-rent 'X,Y,#RRGGBB' ... |
Rent up to 100 cells in one tx |
polyplace grid bulk-color 'X,Y,#RRGGBB' ... |
Recolour up to 100 cells in one tx |
polyplace grid paint IMG --at X Y |
Paint an image (see above) |
polyplace grid free [--region X0 Y0 X1 Y1] |
Find available cells |
polyplace grid show --region X0 Y0 X1 Y1 |
Render a region in the terminal (truecolor) |
polyplace grid params / address |
Rent price/duration; grid contract address |
polyplace faucet claim / info |
Claim PLACE; claim amount, cooldown, your next claim |
polyplace token balance [ADDR] / supply |
PLACE + POL balances; total supply |
polyplace token approve SPENDER AMOUNT |
Manual ERC-20 approval (rarely needed) |
polyplace watcher health |
Indexer status of the watcher service |
Every command supports --help.
Python SDK
from polyplace_client import PolyplaceClient
# Read-only — live Polygon deployment over the public RPC
client = PolyplaceClient()
cell = client.get_cell(500, 500)
print(cell.is_available, cell.color_hex, cell.expires_at)
print(client.grid_params()) # GridParams(rent_price=..., rent_duration=604800)
# Writes — permit-based rent, no approve transaction needed
client = PolyplaceClient(private_key="0x...")
client.claim_faucet()
client.rent_cell(500, 500, 0xFF0000)
client.bulk_rent_cells([(501, 500, 0x00FF00), (502, 500, 0x0000FF)])
client.set_color(500, 500, 0x123456)
# Whole-grid state via the watcher (no RPC scanning)
from polyplace_client.watcher import WatcherClient
snapshot = WatcherClient().grid_snapshot()
free = list(snapshot.free_cells((100, 100, 120, 120)))
# Image painting
from polyplace_client.paint import plan_paint, execute_paint
plan = plan_paint("sprite.png", origin=(480, 480), snapshot=snapshot,
own_address=client.account.address)
execute_paint(client, plan)
Contract errors are translated into readable PolyplaceError messages
("cell 5005 (x=5, y=5) is already rented until …", "faucet cooldown …").
Configuration
Everything is optional; defaults target the live deployment.
| Env var | Default | Purpose |
|---|---|---|
POLYPLACE_PRIVATE_KEY |
— | Hex private key; required for writes only |
POLYPLACE_RPC_URL |
public Polygon RPC | Your own RPC endpoint (AMOY_RPC_URL also accepted) |
POLYPLACE_MANIFEST |
bundled Polygon manifest | Path to a deployment-manifest JSON — target a local Anvil deploy or another network (e.g. Amoy) |
POLYPLACE_WATCHER_URL |
production watcher | Alternate watcher for grid free/show/paint and watcher health |
Troubleshooting
- "faucet cooldown" — one claim per day per address;
polyplace faucet infoshows your next claim time. - "already rented until …" — someone holds that cell;
polyplace grid freefinds open ones. Rentals expire after 7 days, then the cell is up for grabs (its colour lingers, greyed out on the viewer). - "Not enough POL to pay for gas" — your wallet needs a little POL on Polygon mainnet (a few cents covers a lot of painting).
- Public RPC rate limits — set
POLYPLACE_RPC_URLto a free Alchemy/Infura Polygon endpoint.
Development
uv sync
just test # pytest — integration tests need `anvil` (Foundry) on PATH
just check && just fmt
just sync-artifacts # re-vendor ABIs + manifest from ../polyplace-contracts
Integration tests deploy fresh contracts onto a throwaway Anvil chain per
test via the polyplace_contracts deploy library (a dev-only git
dependency); without anvil they're skipped automatically.
Releasing (maintainers)
Bump version in pyproject.toml, merge, then tag — release.yml builds and
publishes to PyPI via trusted publishing (no token secrets):
git tag v0.1.0 && git push origin v0.1.0
Project details
Release history Release notifications | RSS feed
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 polyplace_client-0.1.1.tar.gz.
File metadata
- Download URL: polyplace_client-0.1.1.tar.gz
- Upload date:
- Size: 44.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5094414f62a1ed3ca3e6c676d5225af830c8ae3de5ab84d5f22634a29e25d58d
|
|
| MD5 |
967563165b093f0c0cdcc65c7b2c04b6
|
|
| BLAKE2b-256 |
938e1cb0c046368eff6f8f637c96ee731a647ad65d41c889da890bb3f53ed670
|
Provenance
The following attestation bundles were made for polyplace_client-0.1.1.tar.gz:
Publisher:
release.yml on josh-gree/polyplace-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polyplace_client-0.1.1.tar.gz -
Subject digest:
5094414f62a1ed3ca3e6c676d5225af830c8ae3de5ab84d5f22634a29e25d58d - Sigstore transparency entry: 1809341591
- Sigstore integration time:
-
Permalink:
josh-gree/polyplace-client@a9c2dbf58aaa2269edb1f8104b30fcf80895e76b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/josh-gree
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a9c2dbf58aaa2269edb1f8104b30fcf80895e76b -
Trigger Event:
push
-
Statement type:
File details
Details for the file polyplace_client-0.1.1-py3-none-any.whl.
File metadata
- Download URL: polyplace_client-0.1.1-py3-none-any.whl
- Upload date:
- Size: 39.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fca329584714ac8f4378b9fb75a6a9ea1c69223d0102fb2b7a1362f3bb423c58
|
|
| MD5 |
156ab989fd70d353a4e01e150cd40cd3
|
|
| BLAKE2b-256 |
2a9899701b1616f0cbfd45240b72bd0cf9726be0841c0079c11a67301c46e1b4
|
Provenance
The following attestation bundles were made for polyplace_client-0.1.1-py3-none-any.whl:
Publisher:
release.yml on josh-gree/polyplace-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polyplace_client-0.1.1-py3-none-any.whl -
Subject digest:
fca329584714ac8f4378b9fb75a6a9ea1c69223d0102fb2b7a1362f3bb423c58 - Sigstore transparency entry: 1809341645
- Sigstore integration time:
-
Permalink:
josh-gree/polyplace-client@a9c2dbf58aaa2269edb1f8104b30fcf80895e76b -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/josh-gree
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@a9c2dbf58aaa2269edb1f8104b30fcf80895e76b -
Trigger Event:
push
-
Statement type: