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.
Files changed (63) hide show
  1. scfg_renpy-0.1.1/.gitignore +49 -0
  2. scfg_renpy-0.1.1/LICENSE +21 -0
  3. scfg_renpy-0.1.1/PKG-INFO +73 -0
  4. scfg_renpy-0.1.1/README.md +100 -0
  5. scfg_renpy-0.1.1/docs/developers/README.md +14 -0
  6. scfg_renpy-0.1.1/docs/developers/configure/README.md +136 -0
  7. scfg_renpy-0.1.1/docs/developers/security/README.md +24 -0
  8. scfg_renpy-0.1.1/docs/developers/use/README.md +45 -0
  9. scfg_renpy-0.1.1/installer/README.md +56 -0
  10. scfg_renpy-0.1.1/installer/scfg_renpy/__init__.py +1 -0
  11. scfg_renpy-0.1.1/installer/scfg_renpy/__main__.py +5 -0
  12. scfg_renpy-0.1.1/installer/scfg_renpy/cli.py +141 -0
  13. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/__init__.py +7 -0
  14. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/_qrcodegen.py +907 -0
  15. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/_xkcp.py +90 -0
  16. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/amounts.py +84 -0
  17. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/__init__.py +42 -0
  18. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/base.py +36 -0
  19. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/evm.py +40 -0
  20. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/backends/solana.py +359 -0
  21. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/chains.py +161 -0
  22. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/checkout.py +513 -0
  23. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/coio.py +118 -0
  24. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/config.py +384 -0
  25. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/engine.py +243 -0
  26. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/errors.py +9 -0
  27. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/evm.py +98 -0
  28. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/keccak.py +17 -0
  29. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/local_server.py +203 -0
  30. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/models.py +46 -0
  31. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/overlay.py +110 -0
  32. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/page_messages.py +42 -0
  33. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/paylinks.py +41 -0
  34. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/payments.py +57 -0
  35. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/pump_worker.py +168 -0
  36. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/receipts.py +68 -0
  37. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/restore.py +132 -0
  38. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/restore_flow.py +100 -0
  39. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/reverify.py +90 -0
  40. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/rpc.py +263 -0
  41. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/sessions.py +97 -0
  42. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/__init__.py +2 -0
  43. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/base58.py +48 -0
  44. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/ed25519.py +24 -0
  45. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/paylinks.py +16 -0
  46. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/pda.py +36 -0
  47. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/rpc.py +143 -0
  48. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/solana/tx.py +104 -0
  49. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/transfers.py +20 -0
  50. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/watcher.py +184 -0
  51. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/web_page.py +98 -0
  52. scfg_renpy-0.1.1/plugin/game/python-packages/scfg_core/worker.py +100 -0
  53. scfg_renpy-0.1.1/plugin/game/scfg/scfg_api.rpy +767 -0
  54. scfg_renpy-0.1.1/plugin/game/scfg/scfg_screens.rpy +848 -0
  55. scfg_renpy-0.1.1/plugin/game/scfg/ui/dot.png +0 -0
  56. scfg_renpy-0.1.1/plugin/game/scfg/ui/fill.png +0 -0
  57. scfg_renpy-0.1.1/plugin/game/scfg/ui/line.png +0 -0
  58. scfg_renpy-0.1.1/plugin/game/scfg/web/app.js +224 -0
  59. scfg_renpy-0.1.1/plugin/game/scfg/web/bridge.js +284 -0
  60. scfg_renpy-0.1.1/plugin/game/scfg/web/evm.js +178 -0
  61. scfg_renpy-0.1.1/plugin/game/scfg/web/index.html +71 -0
  62. scfg_renpy-0.1.1/plugin/game/scfg/web/solana.js +182 -0
  63. 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/
@@ -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>`."""
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ sys.exit(main())