diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c16619..1cc5567 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,26 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased +## 0.17.1 — 2026-06-02 + +### Changed — WalletConnect works out of the box (bundled project ID) + +A WalletConnect project ID is a *per-application*, public client identifier (a +dapp ships one for all its users; it's embedded in browser bundles and isn't a +secret), not a per-user secret. clawmes previously required every user to +create their own at cloud.walletconnect.com and set `WALLETCONNECT_PROJECT_ID` +before `/connect` / `clawnchconnect(mode=walletconnect)` would work. + +- Bundled a default project ID so WalletConnect pairing works immediately with + no setup. `WALLETCONNECT_PROJECT_ID` (e.g. in `~/.hermes/.env`) still + **overrides** it for users who want their own relay quota / analytics, and an + explicitly-empty value falls back to the default. +- `hermes clawmes doctor` now reports the WC project ID as OK by default and + notes when the bundled default is in use. + +Tradeoff: default-id traffic counts against one shared Reown relay quota; set +your own `WALLETCONNECT_PROJECT_ID` to use a separate quota. + ## 0.17.0 — 2026-06-02 ### Added — `clawmes_info`: agent-callable bridge for the read-only command surface diff --git a/clawmes/_version.py b/clawmes/_version.py index 427e646..00b2f8c 100644 --- a/clawmes/_version.py +++ b/clawmes/_version.py @@ -7,4 +7,4 @@ * Tooling that does not want to incur a full package import """ -__version__ = "0.17.0" +__version__ = "0.17.1" diff --git a/clawmes/bridges/wc_client.py b/clawmes/bridges/wc_client.py index c2a2764..1d8e800 100644 --- a/clawmes/bridges/wc_client.py +++ b/clawmes/bridges/wc_client.py @@ -17,6 +17,7 @@ from __future__ import annotations +import os from pathlib import Path from typing import Any @@ -25,10 +26,37 @@ _log = logger_for("bridges.wc") +# Clawnch's shared Reown (WalletConnect v2) project ID. A WC project ID is a +# *public, per-application* client identifier — dapps ship a single one for all +# their users; it's embedded in browser bundles and is NOT a secret. Bundling a +# default here means wallet connection works out of the box instead of erroring +# until each user creates their own at cloud.walletconnect.com. +# +# Tradeoff: all default-id traffic counts against this one Reown project's relay +# quota. Power users (or anyone wanting their own analytics / to avoid the +# shared quota) override it by setting WALLETCONNECT_PROJECT_ID in +# ~/.hermes/.env, which always wins (see :func:`_bridge_env`). +_DEFAULT_WALLETCONNECT_PROJECT_ID = "f3a18ce66d092a392f3075ff566db1cf" + + +def _bridge_env() -> dict[str, str]: + """Environment for the WC bridge subprocess. + + Inherits the parent environment and guarantees a WalletConnect project id: + the ``WALLETCONNECT_PROJECT_ID`` env var wins when set (and non-empty), + otherwise the bundled default is used. The ``or`` (not ``setdefault``) means + an explicitly-empty env var also falls back to the default. + """ + env = dict(os.environ) + env["WALLETCONNECT_PROJECT_ID"] = ( + os.environ.get("WALLETCONNECT_PROJECT_ID") or _DEFAULT_WALLETCONNECT_PROJECT_ID + ) + return env + class WalletConnectClient: def __init__(self, entry: Path, *, node_bin: str = "node") -> None: - self._proc = BridgeProcess("clawmes-wc", entry, node_bin=node_bin) + self._proc = BridgeProcess("clawmes-wc", entry, node_bin=node_bin, env=_bridge_env()) def start(self) -> None: self._proc.start() diff --git a/clawmes/commands/doctor.py b/clawmes/commands/doctor.py index 442cd5c..828f362 100644 --- a/clawmes/commands/doctor.py +++ b/clawmes/commands/doctor.py @@ -206,14 +206,16 @@ def _bridge_section() -> _Section: ) ) - # Project ID + # Project ID — a bundled default ships so WalletConnect works out of the + # box; the env var overrides it. So this is always "ok"; we just note when + # the default is in use. pid = os.environ.get("WALLETCONNECT_PROJECT_ID") rows.append( ( - "[ok] " if pid else "[----] ", + "[ok] ", "WC project ID", "WALLETCONNECT_PROJECT_ID", - "" if pid else "free at https://cloud.walletconnect.com", + "" if pid else "bundled default (set WALLETCONNECT_PROJECT_ID to use your own)", ) ) diff --git a/clawmes/plugin.yaml b/clawmes/plugin.yaml index c65a8c6..5e47749 100644 --- a/clawmes/plugin.yaml +++ b/clawmes/plugin.yaml @@ -1,5 +1,5 @@ name: clawmes -version: 0.17.0 +version: 0.17.1 description: Hermes Agent for crypto. Wallet, swaps, DeFi, launches, automation. author: Clawnch kind: standalone diff --git a/plugin.yaml b/plugin.yaml index c65a8c6..5e47749 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -1,5 +1,5 @@ name: clawmes -version: 0.17.0 +version: 0.17.1 description: Hermes Agent for crypto. Wallet, swaps, DeFi, launches, automation. author: Clawnch kind: standalone diff --git a/pyproject.toml b/pyproject.toml index 83c11f2..ed03790 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "clawmes" -version = "0.17.0" +version = "0.17.1" description = "Hermes Agent plugin for crypto: wallets, DEX trading, lending and staking, governance, on-chain automation." readme = "README.md" license = { text = "MIT" } diff --git a/tests/bridges/test_wc_client.py b/tests/bridges/test_wc_client.py index 4145713..758d769 100644 --- a/tests/bridges/test_wc_client.py +++ b/tests/bridges/test_wc_client.py @@ -7,7 +7,11 @@ import pytest -from clawmes.bridges.wc_client import WalletConnectClient +from clawmes.bridges.wc_client import ( + _DEFAULT_WALLETCONNECT_PROJECT_ID, + WalletConnectClient, + _bridge_env, +) @pytest.fixture @@ -18,6 +22,30 @@ def client(): return c +class TestBridgeEnv: + def test_default_when_unset(self, monkeypatch): + monkeypatch.delenv("WALLETCONNECT_PROJECT_ID", raising=False) + env = _bridge_env() + assert env["WALLETCONNECT_PROJECT_ID"] == _DEFAULT_WALLETCONNECT_PROJECT_ID + + def test_env_override_wins(self, monkeypatch): + monkeypatch.setenv("WALLETCONNECT_PROJECT_ID", "my-own-project-id") + assert _bridge_env()["WALLETCONNECT_PROJECT_ID"] == "my-own-project-id" + + def test_empty_falls_back_to_default(self, monkeypatch): + monkeypatch.setenv("WALLETCONNECT_PROJECT_ID", "") + assert _bridge_env()["WALLETCONNECT_PROJECT_ID"] == _DEFAULT_WALLETCONNECT_PROJECT_ID + + def test_preserves_other_env(self, monkeypatch): + monkeypatch.setenv("CLAWMES_TEST_KEEP", "keepme") + assert _bridge_env()["CLAWMES_TEST_KEEP"] == "keepme" + + def test_client_passes_default_env_to_bridge(self, monkeypatch): + monkeypatch.delenv("WALLETCONNECT_PROJECT_ID", raising=False) + c = WalletConnectClient(Path("/fake/wc.mjs")) + assert c._proc._env["WALLETCONNECT_PROJECT_ID"] == _DEFAULT_WALLETCONNECT_PROJECT_ID + + class TestLifecycle: def test_start(self, client): client.start() diff --git a/tests/commands/test_doctor.py b/tests/commands/test_doctor.py index b9d2681..89ffe87 100644 --- a/tests/commands/test_doctor.py +++ b/tests/commands/test_doctor.py @@ -160,6 +160,16 @@ def test_project_id_set(self, monkeypatch): section = _bridge_section() lines = [ln for ln in section.body.splitlines() if "WC project ID" in ln] assert "[ok]" in lines[0] + assert "bundled default" not in lines[0] + + def test_project_id_default_when_unset(self, monkeypatch): + # A bundled default ships, so WC project ID is always [ok]; when the env + # var is unset we note that the default is in use. + monkeypatch.delenv("WALLETCONNECT_PROJECT_ID", raising=False) + section = _bridge_section() + lines = [ln for ln in section.body.splitlines() if "WC project ID" in ln] + assert "[ok]" in lines[0] + assert "bundled default" in lines[0] class TestPluginSection: