Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

## 0.17.2 — 2026-06-02

### Reverted — no bundled WalletConnect project ID (security)

0.17.1 bundled a default WalletConnect project ID so pairing worked with no
setup. That was a mistake: a project id baked into an open-source client ships
publicly (the repo + the PyPI sdist), where anyone can extract it and burn the
shared Reown relay quota — degrading WalletConnect for all users. Reown's own
guidance is "avoid committing project keys to the repo; use env variables."

- Removed the bundled default. `WALLETCONNECT_PROJECT_ID` is supplied per-user
via the environment (typically `~/.hermes/.env`); the WC bridge inherits it.
When unset, WalletConnect returns a clear "set WALLETCONNECT_PROJECT_ID"
error and `hermes clawmes doctor` reports it as not-configured with a setup
hint.
- The 0.17.1 default id has been **rotated out and disabled** in Reown, so it is
dead regardless.

For a shared, zero-setup project id done safely, the only sound approach is a
server-side relay proxy (the id never reaches clients) — tracked as future
work; not bundling a public key.

## 0.17.1 — 2026-06-02

### Changed — WalletConnect works out of the box (bundled project ID)
Expand Down
2 changes: 1 addition & 1 deletion clawmes/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
* Tooling that does not want to incur a full package import
"""

__version__ = "0.17.1"
__version__ = "0.17.2"
35 changes: 7 additions & 28 deletions clawmes/bridges/wc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

from __future__ import annotations

import os
from pathlib import Path
from typing import Any

Expand All @@ -26,37 +25,17 @@

_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
# WalletConnect project id is supplied per-user via the WALLETCONNECT_PROJECT_ID
# env var (typically in ~/.hermes/.env). We deliberately do NOT bundle a shared
# default: a project id baked into an open-source client ships publicly (repo +
# PyPI) where anyone can extract it and burn the shared relay quota. The bridge
# inherits the parent env, so a value set in the environment reaches it; when
# unset, the bridge surfaces a clear "set WALLETCONNECT_PROJECT_ID" error.


class WalletConnectClient:
def __init__(self, entry: Path, *, node_bin: str = "node") -> None:
self._proc = BridgeProcess("clawmes-wc", entry, node_bin=node_bin, env=_bridge_env())
self._proc = BridgeProcess("clawmes-wc", entry, node_bin=node_bin)

def start(self) -> None:
self._proc.start()
Expand Down
9 changes: 4 additions & 5 deletions clawmes/commands/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,15 @@ def _bridge_section() -> _Section:
)
)

# 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.
# Project ID — required, supplied per-user (no bundled default; see
# bridges/wc_client.py for why). Reports not-configured until it's set.
pid = os.environ.get("WALLETCONNECT_PROJECT_ID")
rows.append(
(
"[ok] ",
"[ok] " if pid else "[----] ",
"WC project ID",
"WALLETCONNECT_PROJECT_ID",
"" if pid else "bundled default (set WALLETCONNECT_PROJECT_ID to use your own)",
"" if pid else "free at https://cloud.reown.com — set in ~/.hermes/.env",
)
)

Expand Down
2 changes: 1 addition & 1 deletion clawmes/plugin.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: clawmes
version: 0.17.1
version: 0.17.2
description: Hermes Agent for crypto. Wallet, swaps, DeFi, launches, automation.
author: Clawnch
kind: standalone
Expand Down
2 changes: 1 addition & 1 deletion plugin.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: clawmes
version: 0.17.1
version: 0.17.2
description: Hermes Agent for crypto. Wallet, swaps, DeFi, launches, automation.
author: Clawnch
kind: standalone
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "clawmes"
version = "0.17.1"
version = "0.17.2"
description = "Hermes Agent plugin for crypto: wallets, DEX trading, lending and staking, governance, on-chain automation."
readme = "README.md"
license = { text = "MIT" }
Expand Down
30 changes: 1 addition & 29 deletions tests/bridges/test_wc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@

import pytest

from clawmes.bridges.wc_client import (
_DEFAULT_WALLETCONNECT_PROJECT_ID,
WalletConnectClient,
_bridge_env,
)
from clawmes.bridges.wc_client import WalletConnectClient


@pytest.fixture
Expand All @@ -22,30 +18,6 @@ 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()
Expand Down
10 changes: 4 additions & 6 deletions tests/commands/test_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,14 @@ 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.
def test_project_id_unset(self, monkeypatch):
# No bundled default — unset reports not-configured with a setup hint.
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]
assert "[----]" in lines[0]
assert "reown.com" in lines[0]


class TestPluginSection:
Expand Down
Loading