scfg-renpy 0.1.1__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.
- scfg_renpy-0.1.1/.gitignore +49 -0
- scfg_renpy-0.1.1/LICENSE +21 -0
- scfg_renpy-0.1.1/PKG-INFO +73 -0
- scfg_renpy-0.1.1/README.md +100 -0
- scfg_renpy-0.1.1/docs/developers/README.md +14 -0
- scfg_renpy-0.1.1/docs/developers/configure/README.md +136 -0
- scfg_renpy-0.1.1/docs/developers/security/README.md +24 -0
- scfg_renpy-0.1.1/docs/developers/use/README.md +45 -0
- scfg_renpy-0.1.1/installer/README.md +56 -0
- scfg_renpy-0.1.1/installer/scfg_renpy/__init__.py +1 -0
- scfg_renpy-0.1.1/installer/scfg_renpy/__main__.py +5 -0
- scfg_renpy-0.1.1/installer/scfg_renpy/cli.py +141 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/__init__.py +7 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/_qrcodegen.py +907 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/_xkcp.py +90 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/amounts.py +84 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/__init__.py +42 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/base.py +36 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/evm.py +40 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/solana.py +359 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/chains.py +161 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/checkout.py +513 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/coio.py +118 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/config.py +384 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/engine.py +243 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/errors.py +9 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/evm.py +98 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/keccak.py +17 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/local_server.py +203 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/models.py +46 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/overlay.py +110 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/page_messages.py +42 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/paylinks.py +41 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/payments.py +57 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/pump_worker.py +168 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/receipts.py +68 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/restore.py +132 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/restore_flow.py +100 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/reverify.py +90 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/rpc.py +263 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/sessions.py +97 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/__init__.py +2 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/base58.py +48 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/ed25519.py +24 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/paylinks.py +16 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/pda.py +36 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/rpc.py +143 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/tx.py +104 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/transfers.py +20 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/watcher.py +184 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/web_page.py +98 -0
- scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/worker.py +100 -0
- scfg_renpy-0.1.1/plugin/game/scfg/scfg_api.rpy +767 -0
- scfg_renpy-0.1.1/plugin/game/scfg/scfg_screens.rpy +848 -0
- scfg_renpy-0.1.1/plugin/game/scfg/ui/dot.png +0 -0
- scfg_renpy-0.1.1/plugin/game/scfg/ui/fill.png +0 -0
- scfg_renpy-0.1.1/plugin/game/scfg/ui/line.png +0 -0
- scfg_renpy-0.1.1/plugin/game/scfg/web/app.js +224 -0
- scfg_renpy-0.1.1/plugin/game/scfg/web/bridge.js +284 -0
- scfg_renpy-0.1.1/plugin/game/scfg/web/evm.js +178 -0
- scfg_renpy-0.1.1/plugin/game/scfg/web/index.html +71 -0
- scfg_renpy-0.1.1/plugin/game/scfg/web/solana.js +182 -0
- scfg_renpy-0.1.1/pyproject.toml +83 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.venv/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
.hypothesis/
|
|
7
|
+
.ruff_cache/
|
|
8
|
+
.coverage
|
|
9
|
+
htmlcov/
|
|
10
|
+
|
|
11
|
+
# Ren'Py build artefacts and runtime files
|
|
12
|
+
*.rpyc
|
|
13
|
+
*.rpymc
|
|
14
|
+
*.rpyb
|
|
15
|
+
cache/
|
|
16
|
+
saves/
|
|
17
|
+
log.txt
|
|
18
|
+
traceback.txt
|
|
19
|
+
errors.txt
|
|
20
|
+
files.txt
|
|
21
|
+
*-dists/
|
|
22
|
+
|
|
23
|
+
# Foundry (integration tests)
|
|
24
|
+
contracts/out/
|
|
25
|
+
contracts/cache/
|
|
26
|
+
|
|
27
|
+
# OS / editors
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
.idea/
|
|
31
|
+
.vscode/
|
|
32
|
+
|
|
33
|
+
# Plugin copies made by tools/sync_demo.py
|
|
34
|
+
examples/demo_game/game/scfg/
|
|
35
|
+
examples/demo_game/game/python-packages/
|
|
36
|
+
tests/renpy_e2e/game/scfg/
|
|
37
|
+
tests/renpy_e2e/game/python-packages/
|
|
38
|
+
tests/renpy_e2e/*.txt
|
|
39
|
+
dist/
|
|
40
|
+
tests/renpy_e2e/game/e2e_testcase.rpy
|
|
41
|
+
tests/renpy_web_e2e/game/scfg/
|
|
42
|
+
tests/renpy_web_e2e/game/python-packages/
|
|
43
|
+
tests/renpy_web_e2e/game/e2e_web_config.json
|
|
44
|
+
tests/renpy_web_e2e/progressive_download.txt
|
|
45
|
+
|
|
46
|
+
# Docusaurus (website/)
|
|
47
|
+
website/node_modules/
|
|
48
|
+
website/build/
|
|
49
|
+
website/.docusaurus/
|
scfg_renpy-0.1.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Repulse Game Studio
|
|
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,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scfg-renpy
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: USDC/USDT tips and unlocks for Ren'Py games, paid wallet to wallet. Installs the plugin into your project.
|
|
5
|
+
Project-URL: Homepage, https://jrpg.com/stablecoinforgames/
|
|
6
|
+
Project-URL: Documentation, https://jrpg.com/stablecoinforgames/docs/
|
|
7
|
+
Project-URL: Source, https://github.com/Repulse-Game-Studio/jrpg-stablecoin-games
|
|
8
|
+
Project-URL: Changelog, https://github.com/Repulse-Game-Studio/jrpg-stablecoin-games/releases
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ethereum,payments,renpy,solana,stablecoin,usdc,usdt,visual-novel
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Games/Entertainment
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# Stablecoin for Games
|
|
19
|
+
|
|
20
|
+
Take tips and sell unlocks in **USDC** and **USDT** inside your Ren'Py game, on Ethereum,
|
|
21
|
+
Arbitrum, Polygon, Base or Solana. The player's wallet pays yours directly. There is no
|
|
22
|
+
store, account or server in between: the game watches the blockchain itself.
|
|
23
|
+
|
|
24
|
+
This package installs the plugin into your Ren'Py project. Ren'Py only loads code from a
|
|
25
|
+
game's own `game/` folder, so the plugin is copied there rather than installed into Python.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
From your Ren'Py project folder (the one that contains `game/`):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uvx scfg-renpy install .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
or, with pipx: `pipx run scfg-renpy install .`
|
|
36
|
+
|
|
37
|
+
That adds:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
game/scfg/ (screens, API, browser checkout page)
|
|
41
|
+
game/python-packages/scfg_core/ (the engine; plain Python)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Update
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uvx scfg-renpy update .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
It replaces those two folders with the latest version. Your own files, your configuration
|
|
51
|
+
and your players' purchases are not touched.
|
|
52
|
+
|
|
53
|
+
## Use it
|
|
54
|
+
|
|
55
|
+
```renpy
|
|
56
|
+
init python:
|
|
57
|
+
scfg.configure(wallet="0xYourWallet", testnet=True)
|
|
58
|
+
scfg.add_item("chapter2", code=1, price="4.99", token="USDC", title="Chapter 2")
|
|
59
|
+
|
|
60
|
+
label end_of_chapter1:
|
|
61
|
+
$ result = scfg.pay("unlock", item="chapter2")
|
|
62
|
+
if scfg.owns("chapter2"):
|
|
63
|
+
jump chapter2
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Requires Ren'Py 8.2 or newer. Read the
|
|
67
|
+
[developer guide](https://jrpg.com/stablecoinforgames/docs/developers/install) and the
|
|
68
|
+
[security notes](https://jrpg.com/stablecoinforgames/docs/developers/security) before you
|
|
69
|
+
sell anything: without a server, unlocks are honour-grade.
|
|
70
|
+
|
|
71
|
+
- Docs: https://jrpg.com/stablecoinforgames/docs/
|
|
72
|
+
- Source and release zips: https://github.com/Repulse-Game-Studio/jrpg-stablecoin-games
|
|
73
|
+
- License: MIT
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Stablecoin for Games
|
|
2
|
+
|
|
3
|
+
Accept **USDC/USDT tips and unlock purchases** in Ren'Py games, paid straight to the
|
|
4
|
+
developer's wallet, on Ethereum, Arbitrum, Polygon, Base and **Solana**. No server, no
|
|
5
|
+
accounts, no middleman: the game watches the blockchain itself. Works in desktop builds and
|
|
6
|
+
in Ren'Py's web (HTML5) build.
|
|
7
|
+
|
|
8
|
+
```renpy
|
|
9
|
+
init python:
|
|
10
|
+
scfg.configure(wallet="0xYourWallet", testnet=True)
|
|
11
|
+
scfg.add_item("chapter2", code=1, price="4.99", token="USDC", title="Chapter 2")
|
|
12
|
+
|
|
13
|
+
label end_of_chapter1:
|
|
14
|
+
$ result = scfg.pay("unlock", item="chapter2")
|
|
15
|
+
if scfg.owns("chapter2"):
|
|
16
|
+
jump chapter2
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Using it in your game
|
|
20
|
+
|
|
21
|
+
1. From your Ren'Py project folder, run `uvx scfg-renpy install .` (or `pipx run scfg-renpy install .`),
|
|
22
|
+
and `uvx scfg-renpy update .` later for new versions. Or download `scfg-renpy-<version>.zip`
|
|
23
|
+
from the [latest release](https://github.com/Repulse-Game-Studio/jrpg-stablecoin-games/releases/latest).
|
|
24
|
+
To build it yourself instead, clone the repo and run `python tools/package.py` (Python 3.9+,
|
|
25
|
+
no dependencies), which writes the same zip to `dist/`. Don't copy the repo itself into your
|
|
26
|
+
game: only `plugin/game/` ships, and the zip holds exactly that.
|
|
27
|
+
2. Unzip it into your Ren'Py project folder, the one that contains `game/`. You get `game/scfg/` and
|
|
28
|
+
`game/python-packages/scfg_core/`. See [Install](docs/developers/install.md) for the layout and for updating.
|
|
29
|
+
3. Follow the [developer guide](docs/developers/README.md). It covers configuration, the
|
|
30
|
+
API, testing on testnets, customizing the look, web builds and the security model.
|
|
31
|
+
|
|
32
|
+
Requires Ren'Py 8.2 or newer. Read [Security and limitations](docs/developers/security/README.md)
|
|
33
|
+
before selling anything: without a server, unlocks are honour-grade.
|
|
34
|
+
|
|
35
|
+
The docs are published at https://jrpg.com/stablecoinforgames/docs/.
|
|
36
|
+
Players can read how paying works, which wallets work and how to restore a purchase in
|
|
37
|
+
[docs/players/](docs/players/paying.md).
|
|
38
|
+
|
|
39
|
+
## Repository layout
|
|
40
|
+
|
|
41
|
+
| Path | Contents |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `plugin/game/scfg/` | Ren'Py glue: API (`scfg_api.rpy`), screens, UI images (`ui/`), browser checkout page and web-build bridge (`web/`) |
|
|
44
|
+
| `plugin/game/python-packages/scfg_core/` | Engine-independent core (Python 3.9, stdlib only) |
|
|
45
|
+
| `examples/demo_game/` | Demo game (run `tools/sync_demo.py` first) |
|
|
46
|
+
| `docs/` | The docs: `players/` for players, `developers/` for the developer guide (joined into one `scfg-README.md` in the zip) |
|
|
47
|
+
| `website/` | Docusaurus site built from `docs/`, sidebar in `sidebars.json`. `.github/workflows/docs.yml` checks that it builds |
|
|
48
|
+
| `tests/unit/` | Fast unit tests |
|
|
49
|
+
| `tests/integration/` | Local Anvil chain, local Solana validator and headless Chrome tests (`-m integration`) |
|
|
50
|
+
| `tests/renpy_e2e/`, `tests/renpy_web_e2e/` | Real Ren'Py runs (desktop and web build) driving a payment through the screens |
|
|
51
|
+
| `tools/` | `package.py` (release zip), `sync_demo.py`, `check_rpcs.py`, `run_renpy_e2e.py`, `run_renpy_web_e2e.py`, `make_ui_assets.py` (screen corner images) |
|
|
52
|
+
| `contracts/MockToken.sol` | 6-decimal mock token used by integration tests |
|
|
53
|
+
|
|
54
|
+
## Development
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv sync # Python 3.9 venv + dev tools
|
|
58
|
+
uv run pytest # unit tests
|
|
59
|
+
uv run ruff check . # lint
|
|
60
|
+
|
|
61
|
+
# needs Foundry's `anvil` (https://getfoundry.sh) and Agave's `solana-test-validator`
|
|
62
|
+
# (https://github.com/anza-xyz/agave/releases) on PATH; browser tests need Chrome/Chromium
|
|
63
|
+
uv run pytest -m integration
|
|
64
|
+
|
|
65
|
+
# real Ren'Py run (opens a small window; needs a Ren'Py SDK, anvil and, for Solana, the validator)
|
|
66
|
+
uv run python tools/run_renpy_e2e.py --sdk /path/to/renpy-8.5.3-sdk
|
|
67
|
+
uv run python tools/run_renpy_e2e.py --sdk /path/to/renpy-8.5.3-sdk --family solana
|
|
68
|
+
|
|
69
|
+
# real web build in headless Chrome (needs an SDK with web support, anvil and Playwright's Chromium)
|
|
70
|
+
uv run python tools/run_renpy_web_e2e.py --sdk /path/to/renpy-8.5.3-sdk
|
|
71
|
+
|
|
72
|
+
uv run python tools/check_rpcs.py # are the built-in public RPCs alive?
|
|
73
|
+
uv run python tools/package.py # build dist/scfg-renpy-<version>.zip
|
|
74
|
+
|
|
75
|
+
# docs site (Node 20+)
|
|
76
|
+
cd website && npm ci && npm start # live preview at http://localhost:3000/stablecoinforgames/docs/
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Releasing
|
|
80
|
+
|
|
81
|
+
1. Set the new version in `plugin/game/python-packages/scfg_core/__init__.py` (`__version__`) and
|
|
82
|
+
merge it to `main`.
|
|
83
|
+
2. Tag that commit and push the tag:
|
|
84
|
+
```bash
|
|
85
|
+
git tag v0.2.0 && git push origin v0.2.0
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`.github/workflows/release.yml` then runs the lint and unit tests, checks that the tag matches
|
|
89
|
+
`__version__`, builds the zip with `tools/package.py` and publishes it as a GitHub release with
|
|
90
|
+
generated notes. A second job builds the `scfg-renpy` package (`uv build`: the installer in
|
|
91
|
+
`installer/` with the plugin files inside it) and uploads it to PyPI through trusted publishing,
|
|
92
|
+
from the `pypi` environment. A version with letters in it, such as `0.2.0rc1`, is published as a
|
|
93
|
+
prerelease and does not become "latest".
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT, see [`LICENSE`](LICENSE).
|
|
98
|
+
|
|
99
|
+
Third-party code vendored unmodified: `scfg_core/_xkcp.py` (Keccak team, CC0) and
|
|
100
|
+
`scfg_core/_qrcodegen.py` (Project Nayuki, MIT).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Developer guide
|
|
2
|
+
|
|
3
|
+
Accept **USDC** and **USDT** tips and unlock purchases in your Ren'Py game, paid
|
|
4
|
+
**directly to your own wallet**. There is no server, no account, and no middleman:
|
|
5
|
+
the plugin watches the blockchain itself.
|
|
6
|
+
|
|
7
|
+
- Chains: Ethereum, Arbitrum, Polygon (USDC + USDT), Base (USDC), **Solana (USDC + USDT; devnet USDC)**, plus any EVM chain you add yourself (see [Your own RPC endpoints and chains](configure/README.md#your-own-rpc-endpoints-and-chains) and [Solana](configure/solana.md))
|
|
8
|
+
- Platforms: Windows, macOS, Linux, Android and the web (HTML5) build, on your own site or on itch.io
|
|
9
|
+
- Requires Ren'Py 8.2 or newer (tested on 8.3.7 and 8.5.3)
|
|
10
|
+
|
|
11
|
+
Start with [Install](install.md).
|
|
12
|
+
|
|
13
|
+
> **Read [Security and limitations](security/README.md) before you sell anything.** Without a server,
|
|
14
|
+
> unlocks are honour-grade: a determined player can bypass them.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Configure
|
|
2
|
+
|
|
3
|
+
```renpy
|
|
4
|
+
init python:
|
|
5
|
+
scfg.configure(
|
|
6
|
+
wallet="0xYourWalletAddressHere", # where payments go (EIP-55 checked)
|
|
7
|
+
testnet=True, # remove (or False) for real money
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
# Paid items. `code` (1-99) is written into players' payments forever:
|
|
11
|
+
# NEVER change an item's code after release.
|
|
12
|
+
scfg.add_item("chapter2", code=1, price="4.99", token="USDC", title="Chapter 2: The Fall")
|
|
13
|
+
scfg.add_item("ost", code=2, price="2.00", token="USDT", title="Original Soundtrack")
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## All `configure()` options
|
|
17
|
+
|
|
18
|
+
| Option | Default | Meaning |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `wallet` | required unless `solana_wallet` is given | Your EVM payout address. A wrong-case checksum raises an error, which catches typos |
|
|
21
|
+
| `solana_wallet` | none | Your Solana payout address (base58) to accept USDC/USDT on Solana. See [Solana](solana.md) |
|
|
22
|
+
| `testnet` | `False` | `True` uses Sepolia, Arbitrum Sepolia, Polygon Amoy, Base Sepolia and Solana Devnet (USDC only) |
|
|
23
|
+
| `chains` | all built-ins + yours | Which networks players may pay on, in display order: `["arbitrum", "base", "polygon", "ethereum", "solana"]`. Chains you added with `add_chain()` can be listed too. By default the EVM chains (built-in, then yours) are offered if `wallet` is set, followed by `"solana"` if `solana_wallet` is set |
|
|
24
|
+
| `tokens` | both | `["USDC"]`, `["USDT"]` or both |
|
|
25
|
+
| `methods` | all on | Per-method options, see below |
|
|
26
|
+
| `rpc_urls` | none | Your own RPC endpoints per chain, tried before the built-in public ones, e.g. `{"arbitrum": ["https://arb-mainnet.g.alchemy.com/v2/KEY"]}`. See below |
|
|
27
|
+
| `confirmations` | per chain | Override blocks to wait on EVM chains, e.g. `{"ethereum": 12}`. Refused for `"solana"`, which has no block count (see [Solana](solana.md)) |
|
|
28
|
+
| `previous_wallets` | none | Wallets used by earlier versions of your game, EVM (`0x…`) and Solana addresses can be mixed. Keeps old purchases valid after you change wallets |
|
|
29
|
+
| `logo` | none | An image in your game folder, e.g. `"images/logo.png"` (`.png`, `.jpg` or `.webp`). Shown at the top of the payment screens and the browser checkout page, fitted into 240 × 64 (at 1280 × 720). A wide wordmark works best |
|
|
30
|
+
| `accent` | `"#0e7c74"` | Color of selected options and the main browser button, e.g. `accent=gui.accent_color`. Text on it switches between white and dark automatically |
|
|
31
|
+
|
|
32
|
+
## Payment methods
|
|
33
|
+
|
|
34
|
+
| Method | Options | What the player does |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `browser` | `enabled`, `timeout_min` (30) | Desktop: clicks "Open checkout in browser"; a page served by the game on `127.0.0.1` connects MetaMask, Rabby, Coinbase Wallet or another extension (on Solana: Phantom, Solflare or Backpack), and sends the payment. Web build: clicks "Open wallet"; the same checkout opens over the game (offered only when the page has a wallet for the network; see [Web builds](../security/web-builds.md)) |
|
|
37
|
+
| `qr` | `enabled`, `wallet_button` (Android "Open wallet app"), `chains` (restrict) | Scans a QR code with a phone wallet (EIP-681 payment link; on Solana, a Solana Pay link) |
|
|
38
|
+
| `manual` | `enabled`, `allow_tx_paste` | Sends the exact amount from any wallet or exchange; can paste the transaction hash |
|
|
39
|
+
|
|
40
|
+
```renpy
|
|
41
|
+
scfg.configure(wallet="0x...", methods={"browser": {"enabled": False},
|
|
42
|
+
"qr": {"chains": ["base", "polygon"]}})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Your own RPC endpoints and chains
|
|
46
|
+
|
|
47
|
+
**Your own RPC endpoint for a built-in chain.** The plugin talks to each chain through
|
|
48
|
+
free public RPC endpoints. They work, but they rate-limit and sometimes go down. If you
|
|
49
|
+
have an endpoint from Alchemy, Infura, QuickNode, Ankr, or your own node, pass it in
|
|
50
|
+
`rpc_urls`. Your endpoints are tried first, and the built-in public ones remain as a fallback:
|
|
51
|
+
|
|
52
|
+
```renpy
|
|
53
|
+
init python:
|
|
54
|
+
scfg.configure(
|
|
55
|
+
wallet="0xYourWallet",
|
|
56
|
+
rpc_urls={
|
|
57
|
+
"arbitrum": "https://arb-mainnet.g.alchemy.com/v2/YOUR_KEY",
|
|
58
|
+
"base": ["https://base-mainnet.g.alchemy.com/v2/YOUR_KEY",
|
|
59
|
+
"https://my-backup-node.example.com"],
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Your own chain.** For an EVM chain that isn't built in (Avalanche, Optimism, Linea,
|
|
65
|
+
your own appchain…), describe it with `scfg.add_chain()` **before** `configure()`.
|
|
66
|
+
Its key then works everywhere a built-in key does: `chains=`, `rpc_urls=`, `confirmations=`
|
|
67
|
+
and `methods={"qr": {"chains": [...]}}`.
|
|
68
|
+
|
|
69
|
+
```renpy
|
|
70
|
+
init python:
|
|
71
|
+
# Avalanche C-Chain, real money (used when testnet=False).
|
|
72
|
+
scfg.add_chain(
|
|
73
|
+
"avalanche",
|
|
74
|
+
name="Avalanche", # shown on the network button
|
|
75
|
+
chain_id=43114,
|
|
76
|
+
rpc=["https://api.avax.network/ext/bc/C/rpc"],
|
|
77
|
+
tokens={"USDC": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"},
|
|
78
|
+
confirmations=3,
|
|
79
|
+
explorer_tx="https://snowtrace.io/tx/{}",
|
|
80
|
+
native_symbol="AVAX",
|
|
81
|
+
fee_hint="low",
|
|
82
|
+
)
|
|
83
|
+
# The same key for testing (used when testnet=True): Avalanche Fuji.
|
|
84
|
+
scfg.add_chain(
|
|
85
|
+
"avalanche", testnet=True,
|
|
86
|
+
name="Avalanche Fuji", chain_id=43113,
|
|
87
|
+
rpc=["https://api.avax-test.network/ext/bc/C/rpc"],
|
|
88
|
+
tokens={"USDC": "0x5425890298aed601595a70AB815c96711a31Bc65"},
|
|
89
|
+
confirmations=3,
|
|
90
|
+
explorer_tx="https://testnet.snowtrace.io/tx/{}",
|
|
91
|
+
native_symbol="AVAX",
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
scfg.configure(
|
|
95
|
+
wallet="0xYourWallet",
|
|
96
|
+
testnet=True,
|
|
97
|
+
chains=["base", "avalanche"], # leave out to offer all built-ins + yours
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
| `add_chain()` argument | Meaning |
|
|
102
|
+
|---|---|
|
|
103
|
+
| `key` | Your name for the chain: lowercase letters, digits, `_`, `-`. It is stored in players' receipts, so **never change it after release**. It can't reuse a built-in key |
|
|
104
|
+
| `name` | Shown to players on the network button (at most 32 characters) |
|
|
105
|
+
| `chain_id` | The EVM chain id (see [chainlist.org](https://chainlist.org)). Wallets use it to switch networks, and QR codes carry it |
|
|
106
|
+
| `rpc` | One URL or a list, tried in order. `https://` only, except `http://127.0.0.1` / `http://localhost` for a local test node. The **last** one is also handed to the player's browser wallet if it needs to add the network, so put a public endpoint last |
|
|
107
|
+
| `tokens` | `{"USDC": "0x…"}` and/or `"USDT"`. Copy addresses from Circle's / Tether's official lists, never from a block explorer search |
|
|
108
|
+
| `confirmations` | Blocks to wait before a payment counts. Use the chain's finality: a few for fast-finality chains, more for chains that reorg |
|
|
109
|
+
| `explorer_tx` | Transaction page with `{}` where the hash goes |
|
|
110
|
+
| `native_symbol` | The gas token (`"AVAX"`, `"ETH"`, …), named in "you need gas" warnings. Default `"ETH"` |
|
|
111
|
+
| `fee_hint` | `"lowest"`, `"low"` (default) or `"high"`, shown under the network name |
|
|
112
|
+
| `testnet` | `False` (default) adds the chain for real-money games; `True` for `configure(testnet=True)` games. Add it twice with the same key to have both |
|
|
113
|
+
| `max_log_range` | Blocks per `eth_getLogs` call to start with (default 1000). The plugin shrinks it by itself when an endpoint complains |
|
|
114
|
+
|
|
115
|
+
Rules your chain must follow (checked when the game starts where possible):
|
|
116
|
+
|
|
117
|
+
- **EVM only.** The plugin finds payments through ERC-20 `Transfer` events over Ethereum
|
|
118
|
+
JSON-RPC (`eth_getLogs`, `eth_getTransactionReceipt`, …). Tron, Bitcoin and other non-EVM
|
|
119
|
+
chains can't be added this way. Solana is built in: see [Solana](solana.md).
|
|
120
|
+
- **6-decimal tokens only.** The item code lives in the last four of six decimals. This
|
|
121
|
+
rules out BNB Chain's USDC/USDT (18 decimals). The plugin can't check this offline, so
|
|
122
|
+
confirm `decimals()` on the explorer before you ship.
|
|
123
|
+
- **The endpoint must serve `eth_getLogs`** for recent blocks. Most do; check it with a test
|
|
124
|
+
payment on the chain's testnet.
|
|
125
|
+
- **Test it first.** Make one payment on the testnet version of your chain, then one small
|
|
126
|
+
real payment, before you release.
|
|
127
|
+
|
|
128
|
+
## Item options
|
|
129
|
+
|
|
130
|
+
`scfg.add_item(item_id, code, price, token, title, min_price=None)`
|
|
131
|
+
|
|
132
|
+
- `item_id`: lowercase letters, digits, `_` and `-`.
|
|
133
|
+
- `price`: a **string** like `"4.99"` (at most 2 decimals). Floats are refused.
|
|
134
|
+
- `min_price`: the lowest amount that still unlocks the item (defaults to `price`).
|
|
135
|
+
- **Sales:** you can pass a lower `amount` to `pay()` as long as it's at least `min_price`.
|
|
136
|
+
- **Raising a price later:** keep `min_price` at the old price, so earlier buyers stay unlocked.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Security and limitations
|
|
2
|
+
|
|
3
|
+
What the plugin protects against:
|
|
4
|
+
- **Typing a fake purchase into the save data:** every receipt is re-checked on the blockchain at each launch, and invented ones are removed.
|
|
5
|
+
- **Look-alike "USDC" tokens:** only the official contract addresses count.
|
|
6
|
+
- **Fake transaction hashes sent to the browser page:** every hash is verified on-chain.
|
|
7
|
+
|
|
8
|
+
What it **cannot** prevent (no server means nobody holds a secret):
|
|
9
|
+
- A player reusing **someone else's real transaction hash**, or copying a `persistent` file.
|
|
10
|
+
- **Patched game code**, or a player-controlled fake RPC endpoint.
|
|
11
|
+
- A pirate re-releasing your game with their own wallet address.
|
|
12
|
+
- **Rare amount collisions:** two players buying the same item within minutes of each other, paying by QR or manual transfer without pasting the hash (about 1 % chance per overlapping pair).
|
|
13
|
+
|
|
14
|
+
So treat unlocks as **honour-grade**: right for supporter rewards and tips, weak for valuable content.
|
|
15
|
+
|
|
16
|
+
## Debug menus and cheats
|
|
17
|
+
If your game has a hidden debug mode or a level select, gate it with `owns()`. Otherwise it is an easy way around the paywall.
|
|
18
|
+
|
|
19
|
+
## Platform rules
|
|
20
|
+
- **Steam** forbids games that use cryptocurrencies.
|
|
21
|
+
- **Google Play and the App Store** require their own billing for digital unlocks.
|
|
22
|
+
- **itch.io and direct distribution** are the natural home for this plugin.
|
|
23
|
+
|
|
24
|
+
Check each store's current terms yourself.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Use it
|
|
2
|
+
|
|
3
|
+
```renpy
|
|
4
|
+
label end_of_chapter1:
|
|
5
|
+
if not scfg.owns("chapter2"):
|
|
6
|
+
"Chapter 2 is available for 4.99 USDC."
|
|
7
|
+
$ result = scfg.pay("unlock", item="chapter2")
|
|
8
|
+
|
|
9
|
+
if scfg.owns("chapter2"):
|
|
10
|
+
jump chapter2
|
|
11
|
+
"Maybe later, then."
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Tips (the player can change the suggested amount on the payment screen):
|
|
15
|
+
|
|
16
|
+
```renpy
|
|
17
|
+
$ result = scfg.pay("tip", amount="3.00", token="USDC")
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Buttons in screens use non-blocking actions:
|
|
21
|
+
|
|
22
|
+
```renpy
|
|
23
|
+
textbutton "Support the developer" action ScfgPay("tip", amount="3", token="USDC")
|
|
24
|
+
textbutton "Restore purchases" action CryptoRestore()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Every call and the `PaymentResult` fields are listed in the [API reference](api-reference.md).
|
|
28
|
+
|
|
29
|
+
## Callbacks
|
|
30
|
+
|
|
31
|
+
```renpy
|
|
32
|
+
init python:
|
|
33
|
+
def on_payment(result):
|
|
34
|
+
if result.status == "paid":
|
|
35
|
+
achievement.grant("supporter")
|
|
36
|
+
scfg.on_result = on_payment # also catches payments finished after a restart
|
|
37
|
+
|
|
38
|
+
# per call:
|
|
39
|
+
# $ scfg.pay("unlock", item="chapter2", callback=on_payment)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Rules:
|
|
43
|
+
- Callbacks run on the main thread, for **side effects only**. Never call `jump`, `call` or `renpy.call_screen` inside one; change the story using `pay()`'s return value or `owns()`.
|
|
44
|
+
- The receipt is saved **before** your callback runs. Ownership never depends on the callback.
|
|
45
|
+
- A session can report `"cancelled"` or `"expired"` and **later** `"paid"` (the player paid late). Always treat `"paid"` as final.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Stablecoin for Games
|
|
2
|
+
|
|
3
|
+
Take tips and sell unlocks in **USDC** and **USDT** inside your Ren'Py game, on Ethereum,
|
|
4
|
+
Arbitrum, Polygon, Base or Solana. The player's wallet pays yours directly. There is no
|
|
5
|
+
store, account or server in between: the game watches the blockchain itself.
|
|
6
|
+
|
|
7
|
+
This package installs the plugin into your Ren'Py project. Ren'Py only loads code from a
|
|
8
|
+
game's own `game/` folder, so the plugin is copied there rather than installed into Python.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
From your Ren'Py project folder (the one that contains `game/`):
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
uvx scfg-renpy install .
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
or, with pipx: `pipx run scfg-renpy install .`
|
|
19
|
+
|
|
20
|
+
That adds:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
game/scfg/ (screens, API, browser checkout page)
|
|
24
|
+
game/python-packages/scfg_core/ (the engine; plain Python)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Update
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uvx scfg-renpy update .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
It replaces those two folders with the latest version. Your own files, your configuration
|
|
34
|
+
and your players' purchases are not touched.
|
|
35
|
+
|
|
36
|
+
## Use it
|
|
37
|
+
|
|
38
|
+
```renpy
|
|
39
|
+
init python:
|
|
40
|
+
scfg.configure(wallet="0xYourWallet", testnet=True)
|
|
41
|
+
scfg.add_item("chapter2", code=1, price="4.99", token="USDC", title="Chapter 2")
|
|
42
|
+
|
|
43
|
+
label end_of_chapter1:
|
|
44
|
+
$ result = scfg.pay("unlock", item="chapter2")
|
|
45
|
+
if scfg.owns("chapter2"):
|
|
46
|
+
jump chapter2
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Requires Ren'Py 8.2 or newer. Read the
|
|
50
|
+
[developer guide](https://jrpg.com/stablecoinforgames/docs/developers/install) and the
|
|
51
|
+
[security notes](https://jrpg.com/stablecoinforgames/docs/developers/security) before you
|
|
52
|
+
sell anything: without a server, unlocks are honour-grade.
|
|
53
|
+
|
|
54
|
+
- Docs: https://jrpg.com/stablecoinforgames/docs/
|
|
55
|
+
- Source and release zips: https://github.com/Repulse-Game-Studio/jrpg-stablecoin-games
|
|
56
|
+
- License: MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Installer for the Stablecoin for Games Ren'Py plugin: `scfg-renpy install <project>`."""
|