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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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.0"
__version__ = "0.17.1"
30 changes: 29 additions & 1 deletion clawmes/bridges/wc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from __future__ import annotations

import os
from pathlib import Path
from typing import Any

Expand All @@ -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()
Expand Down
8 changes: 5 additions & 3 deletions clawmes/commands/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
)
)

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.0
version: 0.17.1
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.0
version: 0.17.1
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.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" }
Expand Down
30 changes: 29 additions & 1 deletion tests/bridges/test_wc_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand Down
10 changes: 10 additions & 0 deletions tests/commands/test_doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading