polyplace-client 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- polyplace_client-0.1.0/LICENSE +21 -0
- polyplace_client-0.1.0/PKG-INFO +183 -0
- polyplace_client-0.1.0/README.md +159 -0
- polyplace_client-0.1.0/pyproject.toml +58 -0
- polyplace_client-0.1.0/setup.cfg +4 -0
- polyplace_client-0.1.0/src/polyplace_client/__init__.py +49 -0
- polyplace_client-0.1.0/src/polyplace_client/artifacts/PlaceFaucet.json +291 -0
- polyplace_client-0.1.0/src/polyplace_client/artifacts/PlaceGrid.json +546 -0
- polyplace_client-0.1.0/src/polyplace_client/artifacts/PlaceToken.json +528 -0
- polyplace_client-0.1.0/src/polyplace_client/cli/__init__.py +1 -0
- polyplace_client-0.1.0/src/polyplace_client/cli/main.py +473 -0
- polyplace_client-0.1.0/src/polyplace_client/client.py +396 -0
- polyplace_client-0.1.0/src/polyplace_client/contracts.py +48 -0
- polyplace_client-0.1.0/src/polyplace_client/errors.py +250 -0
- polyplace_client-0.1.0/src/polyplace_client/networks/polygon.json +21 -0
- polyplace_client-0.1.0/src/polyplace_client/networks.py +81 -0
- polyplace_client-0.1.0/src/polyplace_client/paint.py +120 -0
- polyplace_client-0.1.0/src/polyplace_client/watcher.py +184 -0
- polyplace_client-0.1.0/src/polyplace_client.egg-info/PKG-INFO +183 -0
- polyplace_client-0.1.0/src/polyplace_client.egg-info/SOURCES.txt +29 -0
- polyplace_client-0.1.0/src/polyplace_client.egg-info/dependency_links.txt +1 -0
- polyplace_client-0.1.0/src/polyplace_client.egg-info/entry_points.txt +2 -0
- polyplace_client-0.1.0/src/polyplace_client.egg-info/requires.txt +4 -0
- polyplace_client-0.1.0/src/polyplace_client.egg-info/top_level.txt +1 -0
- polyplace_client-0.1.0/tests/test_cli_helpers.py +91 -0
- polyplace_client-0.1.0/tests/test_contracts.py +77 -0
- polyplace_client-0.1.0/tests/test_errors.py +99 -0
- polyplace_client-0.1.0/tests/test_integration.py +141 -0
- polyplace_client-0.1.0/tests/test_networks.py +106 -0
- polyplace_client-0.1.0/tests/test_paint.py +123 -0
- polyplace_client-0.1.0/tests/test_watcher.py +140 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Josh Greenhalgh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polyplace-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK and CLI for Polyplace — an on-chain pixel grid on Polygon Amoy
|
|
5
|
+
Author: Josh Greenhalgh
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://polyplace-frontend.joshuadouglasgreenhalgh.workers.dev
|
|
8
|
+
Project-URL: Repository, https://github.com/josh-gree/polyplace-client
|
|
9
|
+
Project-URL: Issues, https://github.com/josh-gree/polyplace-client/issues
|
|
10
|
+
Keywords: ethereum,polygon,web3,pixel-art,cli
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Topic :: Games/Entertainment
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: httpx>=0.28.1
|
|
20
|
+
Requires-Dist: pillow>=12.2.0
|
|
21
|
+
Requires-Dist: typer>=0.26.7
|
|
22
|
+
Requires-Dist: web3>=7.15.0
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# polyplace-client
|
|
26
|
+
|
|
27
|
+
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](https://polyplace-frontend.joshuadouglasgreenhalgh.workers.dev).
|
|
28
|
+
|
|
29
|
+
This package is *the* way to paint: the web frontend is a read-only viewer, and all contract interaction happens here.
|
|
30
|
+
|
|
31
|
+
| | |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Grid | 1000×1000 cells, one colour each |
|
|
34
|
+
| Rent | 1 PLACE per cell, lasts 7 days |
|
|
35
|
+
| Faucet | 150 PLACE per claim, 1-day cooldown |
|
|
36
|
+
| Gas | POL on Polygon mainnet — real, but a few cents' worth paints hundreds of cells |
|
|
37
|
+
| Contracts | [polyplace-contracts](https://github.com/josh-gree/polyplace-contracts) on Polygon (chain 137) |
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
pip install polyplace-client # or: uv tool install polyplace-client
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quickstart
|
|
46
|
+
|
|
47
|
+
Reads need **zero configuration** — they use the public Polygon RPC and the live deployment out of the box:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
polyplace grid params # rent price and duration
|
|
51
|
+
polyplace grid cell 500 500 # who owns a cell, what colour, when it expires
|
|
52
|
+
polyplace grid free --limit 10 # find available cells
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To paint, you need a funded key:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
# 1. Create a wallet (any Ethereum key works — this is a quick way)
|
|
59
|
+
python -c "from eth_account import Account; a = Account.create(); print(a.address); print(a.key.hex())"
|
|
60
|
+
export POLYPLACE_PRIVATE_KEY=0x... # the key printed above
|
|
61
|
+
|
|
62
|
+
# 2. Fund it with a little POL for gas (Polygon mainnet — real POL,
|
|
63
|
+
# but a few cents' worth paints hundreds of cells)
|
|
64
|
+
|
|
65
|
+
# 3. Claim free PLACE tokens (150 PLACE, once a day)
|
|
66
|
+
polyplace faucet claim
|
|
67
|
+
|
|
68
|
+
# 4. Rent a pixel — one transaction, no token approval needed
|
|
69
|
+
polyplace grid rent 500 500 '#FF0000'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Your pixel is now live on the [grid viewer](https://polyplace-frontend.joshuadouglasgreenhalgh.workers.dev) within seconds.
|
|
73
|
+
|
|
74
|
+
> 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 `.env` file.
|
|
75
|
+
|
|
76
|
+
## Paint an image
|
|
77
|
+
|
|
78
|
+
One cell per opaque pixel; transparent pixels are skipped, so sprites with alpha just work:
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
polyplace grid paint sprite.png --at 480 480 --dry-run # preview cost first
|
|
82
|
+
polyplace grid paint sprite.png --at 480 480 # then for real
|
|
83
|
+
polyplace grid paint photo.png --at 200 200 --scale 40 # resize to 40 cells wide
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
The preview shows cells × rent price, transaction count (≤100 cells per
|
|
87
|
+
transaction), and your balance. Cells currently rented by *someone else* are
|
|
88
|
+
skipped and reported; your own active rentals are repainted. A 40×40 image is
|
|
89
|
+
1,600 cells = 1,600 PLACE, so big art means saving up a few days of faucet
|
|
90
|
+
claims — and rentals last 7 days before you need to renew.
|
|
91
|
+
|
|
92
|
+
## CLI reference
|
|
93
|
+
|
|
94
|
+
| Command | What it does |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `polyplace grid cell X Y` | State of one cell (renter, colour, expiry) |
|
|
97
|
+
| `polyplace grid rent X Y '#RRGGBB'` | Rent a cell and set its colour (single tx, EIP-2612 permit) |
|
|
98
|
+
| `polyplace grid color X Y '#RRGGBB'` | Recolour a cell you already rent |
|
|
99
|
+
| `polyplace grid bulk-rent 'X,Y,#RRGGBB' ...` | Rent up to 100 cells in one tx |
|
|
100
|
+
| `polyplace grid bulk-color 'X,Y,#RRGGBB' ...` | Recolour up to 100 cells in one tx |
|
|
101
|
+
| `polyplace grid paint IMG --at X Y` | Paint an image (see above) |
|
|
102
|
+
| `polyplace grid free [--region X0 Y0 X1 Y1]` | Find available cells |
|
|
103
|
+
| `polyplace grid show --region X0 Y0 X1 Y1` | Render a region in the terminal (truecolor) |
|
|
104
|
+
| `polyplace grid params` / `address` | Rent price/duration; grid contract address |
|
|
105
|
+
| `polyplace faucet claim` / `info` | Claim PLACE; claim amount, cooldown, your next claim |
|
|
106
|
+
| `polyplace token balance [ADDR]` / `supply` | PLACE + POL balances; total supply |
|
|
107
|
+
| `polyplace token approve SPENDER AMOUNT` | Manual ERC-20 approval (rarely needed) |
|
|
108
|
+
| `polyplace watcher health` | Indexer status of the watcher service |
|
|
109
|
+
|
|
110
|
+
Every command supports `--help`.
|
|
111
|
+
|
|
112
|
+
## Python SDK
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from polyplace_client import PolyplaceClient
|
|
116
|
+
|
|
117
|
+
# Read-only — live Polygon deployment over the public RPC
|
|
118
|
+
client = PolyplaceClient()
|
|
119
|
+
cell = client.get_cell(500, 500)
|
|
120
|
+
print(cell.is_available, cell.color_hex, cell.expires_at)
|
|
121
|
+
print(client.grid_params()) # GridParams(rent_price=..., rent_duration=604800)
|
|
122
|
+
|
|
123
|
+
# Writes — permit-based rent, no approve transaction needed
|
|
124
|
+
client = PolyplaceClient(private_key="0x...")
|
|
125
|
+
client.claim_faucet()
|
|
126
|
+
client.rent_cell(500, 500, 0xFF0000)
|
|
127
|
+
client.bulk_rent_cells([(501, 500, 0x00FF00), (502, 500, 0x0000FF)])
|
|
128
|
+
client.set_color(500, 500, 0x123456)
|
|
129
|
+
|
|
130
|
+
# Whole-grid state via the watcher (no RPC scanning)
|
|
131
|
+
from polyplace_client.watcher import WatcherClient
|
|
132
|
+
snapshot = WatcherClient().grid_snapshot()
|
|
133
|
+
free = list(snapshot.free_cells((100, 100, 120, 120)))
|
|
134
|
+
|
|
135
|
+
# Image painting
|
|
136
|
+
from polyplace_client.paint import plan_paint, execute_paint
|
|
137
|
+
plan = plan_paint("sprite.png", origin=(480, 480), snapshot=snapshot,
|
|
138
|
+
own_address=client.account.address)
|
|
139
|
+
execute_paint(client, plan)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Contract errors are translated into readable `PolyplaceError` messages
|
|
143
|
+
("cell 5005 (x=5, y=5) is already rented until …", "faucet cooldown …").
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
Everything is optional; defaults target the live deployment.
|
|
148
|
+
|
|
149
|
+
| Env var | Default | Purpose |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| `POLYPLACE_PRIVATE_KEY` | — | Hex private key; required for writes only |
|
|
152
|
+
| `POLYPLACE_RPC_URL` | public Polygon RPC | Your own RPC endpoint (`AMOY_RPC_URL` also accepted) |
|
|
153
|
+
| `POLYPLACE_MANIFEST` | bundled Polygon manifest | Path to a deployment-manifest JSON — target a local Anvil deploy or another network (e.g. Amoy) |
|
|
154
|
+
| `POLYPLACE_WATCHER_URL` | production watcher | Alternate watcher for `grid free/show/paint` and `watcher health` |
|
|
155
|
+
|
|
156
|
+
## Troubleshooting
|
|
157
|
+
|
|
158
|
+
- **"faucet cooldown"** — one claim per day per address; `polyplace faucet info` shows your next claim time.
|
|
159
|
+
- **"already rented until …"** — someone holds that cell; `polyplace grid free` finds open ones. Rentals expire after 7 days, then the cell is up for grabs (its colour lingers, greyed out on the viewer).
|
|
160
|
+
- **"Not enough POL to pay for gas"** — your wallet needs a little POL on Polygon mainnet (a few cents covers a lot of painting).
|
|
161
|
+
- **Public RPC rate limits** — set `POLYPLACE_RPC_URL` to a free Alchemy/Infura Polygon endpoint.
|
|
162
|
+
|
|
163
|
+
## Development
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
uv sync
|
|
167
|
+
just test # pytest — integration tests need `anvil` (Foundry) on PATH
|
|
168
|
+
just check && just fmt
|
|
169
|
+
just sync-artifacts # re-vendor ABIs + manifest from ../polyplace-contracts
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Integration tests deploy fresh contracts onto a throwaway Anvil chain per
|
|
173
|
+
test via the `polyplace_contracts` deploy library (a dev-only git
|
|
174
|
+
dependency); without `anvil` they're skipped automatically.
|
|
175
|
+
|
|
176
|
+
### Releasing (maintainers)
|
|
177
|
+
|
|
178
|
+
Bump `version` in `pyproject.toml`, merge, then tag — `release.yml` builds and
|
|
179
|
+
publishes to PyPI via trusted publishing (no token secrets):
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
git tag v0.1.0 && git push origin v0.1.0
|
|
183
|
+
```
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# polyplace-client
|
|
2
|
+
|
|
3
|
+
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](https://polyplace-frontend.joshuadouglasgreenhalgh.workers.dev).
|
|
4
|
+
|
|
5
|
+
This package is *the* way to paint: the web frontend is a read-only viewer, and all contract interaction happens here.
|
|
6
|
+
|
|
7
|
+
| | |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Grid | 1000×1000 cells, one colour each |
|
|
10
|
+
| Rent | 1 PLACE per cell, lasts 7 days |
|
|
11
|
+
| Faucet | 150 PLACE per claim, 1-day cooldown |
|
|
12
|
+
| Gas | POL on Polygon mainnet — real, but a few cents' worth paints hundreds of cells |
|
|
13
|
+
| Contracts | [polyplace-contracts](https://github.com/josh-gree/polyplace-contracts) on Polygon (chain 137) |
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pip install polyplace-client # or: uv tool install polyplace-client
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
Reads need **zero configuration** — they use the public Polygon RPC and the live deployment out of the box:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
polyplace grid params # rent price and duration
|
|
27
|
+
polyplace grid cell 500 500 # who owns a cell, what colour, when it expires
|
|
28
|
+
polyplace grid free --limit 10 # find available cells
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
To paint, you need a funded key:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
# 1. Create a wallet (any Ethereum key works — this is a quick way)
|
|
35
|
+
python -c "from eth_account import Account; a = Account.create(); print(a.address); print(a.key.hex())"
|
|
36
|
+
export POLYPLACE_PRIVATE_KEY=0x... # the key printed above
|
|
37
|
+
|
|
38
|
+
# 2. Fund it with a little POL for gas (Polygon mainnet — real POL,
|
|
39
|
+
# but a few cents' worth paints hundreds of cells)
|
|
40
|
+
|
|
41
|
+
# 3. Claim free PLACE tokens (150 PLACE, once a day)
|
|
42
|
+
polyplace faucet claim
|
|
43
|
+
|
|
44
|
+
# 4. Rent a pixel — one transaction, no token approval needed
|
|
45
|
+
polyplace grid rent 500 500 '#FF0000'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Your pixel is now live on the [grid viewer](https://polyplace-frontend.joshuadouglasgreenhalgh.workers.dev) within seconds.
|
|
49
|
+
|
|
50
|
+
> 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 `.env` file.
|
|
51
|
+
|
|
52
|
+
## Paint an image
|
|
53
|
+
|
|
54
|
+
One cell per opaque pixel; transparent pixels are skipped, so sprites with alpha just work:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
polyplace grid paint sprite.png --at 480 480 --dry-run # preview cost first
|
|
58
|
+
polyplace grid paint sprite.png --at 480 480 # then for real
|
|
59
|
+
polyplace grid paint photo.png --at 200 200 --scale 40 # resize to 40 cells wide
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The preview shows cells × rent price, transaction count (≤100 cells per
|
|
63
|
+
transaction), and your balance. Cells currently rented by *someone else* are
|
|
64
|
+
skipped and reported; your own active rentals are repainted. A 40×40 image is
|
|
65
|
+
1,600 cells = 1,600 PLACE, so big art means saving up a few days of faucet
|
|
66
|
+
claims — and rentals last 7 days before you need to renew.
|
|
67
|
+
|
|
68
|
+
## CLI reference
|
|
69
|
+
|
|
70
|
+
| Command | What it does |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `polyplace grid cell X Y` | State of one cell (renter, colour, expiry) |
|
|
73
|
+
| `polyplace grid rent X Y '#RRGGBB'` | Rent a cell and set its colour (single tx, EIP-2612 permit) |
|
|
74
|
+
| `polyplace grid color X Y '#RRGGBB'` | Recolour a cell you already rent |
|
|
75
|
+
| `polyplace grid bulk-rent 'X,Y,#RRGGBB' ...` | Rent up to 100 cells in one tx |
|
|
76
|
+
| `polyplace grid bulk-color 'X,Y,#RRGGBB' ...` | Recolour up to 100 cells in one tx |
|
|
77
|
+
| `polyplace grid paint IMG --at X Y` | Paint an image (see above) |
|
|
78
|
+
| `polyplace grid free [--region X0 Y0 X1 Y1]` | Find available cells |
|
|
79
|
+
| `polyplace grid show --region X0 Y0 X1 Y1` | Render a region in the terminal (truecolor) |
|
|
80
|
+
| `polyplace grid params` / `address` | Rent price/duration; grid contract address |
|
|
81
|
+
| `polyplace faucet claim` / `info` | Claim PLACE; claim amount, cooldown, your next claim |
|
|
82
|
+
| `polyplace token balance [ADDR]` / `supply` | PLACE + POL balances; total supply |
|
|
83
|
+
| `polyplace token approve SPENDER AMOUNT` | Manual ERC-20 approval (rarely needed) |
|
|
84
|
+
| `polyplace watcher health` | Indexer status of the watcher service |
|
|
85
|
+
|
|
86
|
+
Every command supports `--help`.
|
|
87
|
+
|
|
88
|
+
## Python SDK
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from polyplace_client import PolyplaceClient
|
|
92
|
+
|
|
93
|
+
# Read-only — live Polygon deployment over the public RPC
|
|
94
|
+
client = PolyplaceClient()
|
|
95
|
+
cell = client.get_cell(500, 500)
|
|
96
|
+
print(cell.is_available, cell.color_hex, cell.expires_at)
|
|
97
|
+
print(client.grid_params()) # GridParams(rent_price=..., rent_duration=604800)
|
|
98
|
+
|
|
99
|
+
# Writes — permit-based rent, no approve transaction needed
|
|
100
|
+
client = PolyplaceClient(private_key="0x...")
|
|
101
|
+
client.claim_faucet()
|
|
102
|
+
client.rent_cell(500, 500, 0xFF0000)
|
|
103
|
+
client.bulk_rent_cells([(501, 500, 0x00FF00), (502, 500, 0x0000FF)])
|
|
104
|
+
client.set_color(500, 500, 0x123456)
|
|
105
|
+
|
|
106
|
+
# Whole-grid state via the watcher (no RPC scanning)
|
|
107
|
+
from polyplace_client.watcher import WatcherClient
|
|
108
|
+
snapshot = WatcherClient().grid_snapshot()
|
|
109
|
+
free = list(snapshot.free_cells((100, 100, 120, 120)))
|
|
110
|
+
|
|
111
|
+
# Image painting
|
|
112
|
+
from polyplace_client.paint import plan_paint, execute_paint
|
|
113
|
+
plan = plan_paint("sprite.png", origin=(480, 480), snapshot=snapshot,
|
|
114
|
+
own_address=client.account.address)
|
|
115
|
+
execute_paint(client, plan)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Contract errors are translated into readable `PolyplaceError` messages
|
|
119
|
+
("cell 5005 (x=5, y=5) is already rented until …", "faucet cooldown …").
|
|
120
|
+
|
|
121
|
+
## Configuration
|
|
122
|
+
|
|
123
|
+
Everything is optional; defaults target the live deployment.
|
|
124
|
+
|
|
125
|
+
| Env var | Default | Purpose |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| `POLYPLACE_PRIVATE_KEY` | — | Hex private key; required for writes only |
|
|
128
|
+
| `POLYPLACE_RPC_URL` | public Polygon RPC | Your own RPC endpoint (`AMOY_RPC_URL` also accepted) |
|
|
129
|
+
| `POLYPLACE_MANIFEST` | bundled Polygon manifest | Path to a deployment-manifest JSON — target a local Anvil deploy or another network (e.g. Amoy) |
|
|
130
|
+
| `POLYPLACE_WATCHER_URL` | production watcher | Alternate watcher for `grid free/show/paint` and `watcher health` |
|
|
131
|
+
|
|
132
|
+
## Troubleshooting
|
|
133
|
+
|
|
134
|
+
- **"faucet cooldown"** — one claim per day per address; `polyplace faucet info` shows your next claim time.
|
|
135
|
+
- **"already rented until …"** — someone holds that cell; `polyplace grid free` finds open ones. Rentals expire after 7 days, then the cell is up for grabs (its colour lingers, greyed out on the viewer).
|
|
136
|
+
- **"Not enough POL to pay for gas"** — your wallet needs a little POL on Polygon mainnet (a few cents covers a lot of painting).
|
|
137
|
+
- **Public RPC rate limits** — set `POLYPLACE_RPC_URL` to a free Alchemy/Infura Polygon endpoint.
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
uv sync
|
|
143
|
+
just test # pytest — integration tests need `anvil` (Foundry) on PATH
|
|
144
|
+
just check && just fmt
|
|
145
|
+
just sync-artifacts # re-vendor ABIs + manifest from ../polyplace-contracts
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Integration tests deploy fresh contracts onto a throwaway Anvil chain per
|
|
149
|
+
test via the `polyplace_contracts` deploy library (a dev-only git
|
|
150
|
+
dependency); without `anvil` they're skipped automatically.
|
|
151
|
+
|
|
152
|
+
### Releasing (maintainers)
|
|
153
|
+
|
|
154
|
+
Bump `version` in `pyproject.toml`, merge, then tag — `release.yml` builds and
|
|
155
|
+
publishes to PyPI via trusted publishing (no token secrets):
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
git tag v0.1.0 && git push origin v0.1.0
|
|
159
|
+
```
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "polyplace-client"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Python SDK and CLI for Polyplace — an on-chain pixel grid on Polygon Amoy"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "Josh Greenhalgh" }]
|
|
9
|
+
keywords = ["ethereum", "polygon", "web3", "pixel-art", "cli"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Environment :: Console",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Topic :: Games/Entertainment",
|
|
16
|
+
]
|
|
17
|
+
requires-python = ">=3.10"
|
|
18
|
+
dependencies = [
|
|
19
|
+
"httpx>=0.28.1",
|
|
20
|
+
"pillow>=12.2.0",
|
|
21
|
+
"typer>=0.26.7",
|
|
22
|
+
"web3>=7.15.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["setuptools>=61"]
|
|
27
|
+
build-backend = "setuptools.build_meta"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["src"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.package-data]
|
|
33
|
+
polyplace_client = ["artifacts/*.json", "networks/*.json"]
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
|
+
"polyplace-contracts",
|
|
38
|
+
"pytest>=9.0.3",
|
|
39
|
+
"ruff>=0.15.12",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
line-length = 100
|
|
44
|
+
target-version = "py310"
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
polyplace = "polyplace_client.cli.main:main"
|
|
48
|
+
|
|
49
|
+
[project.urls]
|
|
50
|
+
Homepage = "https://polyplace-frontend.joshuadouglasgreenhalgh.workers.dev"
|
|
51
|
+
Repository = "https://github.com/josh-gree/polyplace-client"
|
|
52
|
+
Issues = "https://github.com/josh-gree/polyplace-client/issues"
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
extend-select = ["I", "UP"]
|
|
56
|
+
|
|
57
|
+
[tool.uv.sources]
|
|
58
|
+
polyplace-contracts = { git = "https://github.com/josh-gree/polyplace-contracts.git", subdirectory = "packages/python" }
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""polyplace-client: Python SDK and CLI for interacting with Polyplace."""
|
|
2
|
+
|
|
3
|
+
from polyplace_client.client import (
|
|
4
|
+
Balances,
|
|
5
|
+
CellState,
|
|
6
|
+
FaucetInfo,
|
|
7
|
+
GridParams,
|
|
8
|
+
PolyplaceClient,
|
|
9
|
+
connect,
|
|
10
|
+
make_faucet_contract,
|
|
11
|
+
make_grid_contract,
|
|
12
|
+
make_token_contract,
|
|
13
|
+
)
|
|
14
|
+
from polyplace_client.contracts import (
|
|
15
|
+
INITIAL_SUPPLY,
|
|
16
|
+
PLACE_FAUCET_ABI,
|
|
17
|
+
PLACE_FAUCET_BYTECODE,
|
|
18
|
+
PLACE_GRID_ABI,
|
|
19
|
+
PLACE_GRID_BYTECODE,
|
|
20
|
+
PLACE_TOKEN_ABI,
|
|
21
|
+
PLACE_TOKEN_BYTECODE,
|
|
22
|
+
)
|
|
23
|
+
from polyplace_client.errors import PolyplaceError
|
|
24
|
+
from polyplace_client.networks import Network, load_network
|
|
25
|
+
|
|
26
|
+
__version__ = "0.1.0"
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"__version__",
|
|
30
|
+
"INITIAL_SUPPLY",
|
|
31
|
+
"PLACE_TOKEN_ABI",
|
|
32
|
+
"PLACE_FAUCET_ABI",
|
|
33
|
+
"PLACE_GRID_ABI",
|
|
34
|
+
"PLACE_TOKEN_BYTECODE",
|
|
35
|
+
"PLACE_FAUCET_BYTECODE",
|
|
36
|
+
"PLACE_GRID_BYTECODE",
|
|
37
|
+
"Balances",
|
|
38
|
+
"CellState",
|
|
39
|
+
"FaucetInfo",
|
|
40
|
+
"GridParams",
|
|
41
|
+
"Network",
|
|
42
|
+
"PolyplaceClient",
|
|
43
|
+
"PolyplaceError",
|
|
44
|
+
"connect",
|
|
45
|
+
"load_network",
|
|
46
|
+
"make_token_contract",
|
|
47
|
+
"make_faucet_contract",
|
|
48
|
+
"make_grid_contract",
|
|
49
|
+
]
|