# Подключение rpc-pool к другому проекту

Чтобы проект получал RPC из пула и сам переключался на следующий RPC по кругу, нужно пять шагов:

1. Получить у владельца API-ключ для проекта.
2. Поставить клиент `rpc_pool_client` из сборки на сайте пула.
3. Положить в окружение проекта `RPC_POOL_URL` и `RPC_POOL_API_KEY` и завести свои прокси.
4. Вызывать RPC через `RpcPool`.
5. Вставить блок правил в `CLAUDE.md` проекта (раздел 5), чтобы агент всегда знал правило и не просил напоминать.

Пул работает по адресу `https://bomzrpc.online`. Для моделей и агентов есть один файл со всем нужным, `https://bomzrpc.online/llms.txt`: начни с него,
этот документ с ним согласован.

Правило №1 (`docs/RULES.md`): **Work traffic to these RPCs MUST go through your proxy. Never call them from the host's main IP.**
Как устроен failover и что делает клиент при отказе: `docs/FAILOVER.md`.

## 1. Ключ для проекта: его выдаёт владелец

Ключи создаёт владелец пула на сервере. Попроси у него ключ для проекта и назови проект: по имени он потом отличит ключи друг от друга.

- Ключ показывается владельцу один раз, в базе пула хранится только его SHA-256. Потерял ключ: попроси владельца отозвать его и выдать новый.
- Нужен **отдельный ключ на каждый проект**. Жалобы на RPC считаются по ключам: три разных ключа за час исключают RPC из выдачи на час,
  один ключ может лишь отодвинуть RPC в конец списка. Общий ключ у нескольких проектов делит и жалобы, и лимит `600` запросов в минуту.
- Получил ключ: положи его в `.env` проекта (он в `.gitignore`) как `RPC_POOL_API_KEY`. Не в код, не в чат, не в логи.

## 2. Установка клиента

Клиент отдаёт сам сайт пула, как wheel. Ставь его через `uv add` и не правь список зависимостей руками:

```bash
uv add "rpc-pool-client @ <URL сборки со страницы https://bomzrpc.online/client/>"
# с поддержкой web3.py:
uv add "rpc-pool-client[web3] @ <URL сборки со страницы https://bomzrpc.online/client/>"
```

ТОЛЬКО если проект не использует uv (для web3.py так же добавь `[web3]`):

```bash
pip install "rpc-pool-client @ <URL сборки со страницы https://bomzrpc.online/client/>"
```

- Страница `https://bomzrpc.online/client/` (и `https://bomzrpc.online/llms.txt`) показывает готовую команду для самой новой сборки: бери URL оттуда.
- В URL нужно **настоящее имя файла** (`rpc_pool_client-<версия>-py3-none-any.whl`): `uv` берёт из него имя и версию пакета. Адрес `/client/latest.whl`
  перенаправляет на последнюю сборку и годится для скачивания файла, но не для `uv add`. Имя меняется с каждым релизом клиента.
- Клиент тянет `aiohttp`, `aiohttp-socks` и `pycryptodome`; `web3` только с extra `[web3]`.

## 3. Окружение и прокси

```bash
RPC_POOL_URL=https://bomzrpc.online
RPC_POOL_API_KEY=<ключ от владельца>
```

- `RpcPool` берёт эти переменные, если `api_url` и `api_key` не переданы аргументами. Клиент сам `.env` не читает: загружай его своим способом
  (например, `uv run --env-file .env python main.py`).
- `.env` проекта должен быть в `.gitignore`. Ключ не коммитится, не пишется в логи и не вставляется в код.
- **Прокси клиент из окружения не берёт**: список `proxies` ты передаёшь аргументом, хранить его можно где угодно (например,
  в `WORK_PROXIES` через запятую в том же `.env`; имя переменной выбирает проект). Формат: `http://`, `https://`, `socks4://`, `socks5://`,
  `socks5h://`, для SOCKS порт обязателен: `socks5://user:pass@proxy-a.example.com:1080`.
- Нет прокси: клиент не создастся (`ValueError`). Не «временно без них», а остановись и спроси владельца.

## 4. Минимальный код

```python
import asyncio
import os

from rpc_pool_client import RpcPool


async def main() -> None:
    proxies = [p.strip() for p in os.environ["WORK_PROXIES"].split(",") if p.strip()]
    async with RpcPool(proxies=proxies) as pool:  # RPC_POOL_URL и RPC_POOL_API_KEY из окружения
        head = await pool.chain(8453).call("eth_blockNumber")  # 8453 = Base
        print(int(str(head), 16))


asyncio.run(main())
```

- **В скриптах сеть задавай chain id (`8453`, число или строка) или ключом (`eip155:8453`, `solana:mainnet`), не алиасом (`base`, `eth`).**
  Алиас это удобство для рук: пока каталоги меняются, один и тот же алиас может заявить вторая сеть, и он станет неоднозначным (`404 unknown_chain`) у скрипта, который вчера работал (`docs/CHAINS.md`, раздел 2). Chain id и ключ однозначны всегда.
- Как найти chain id или ключ нужной сети: `curl -fsS -H "X-API-Key: $RPC_POOL_API_KEY" "$RPC_POOL_URL/v1/chains?q=base"` (в ответе `key`, `evm_chain_id`, `aliases`).
- `RpcPool` один на процесс (внутри одна сессия на каждый прокси). Создавай его в `main()` через `async with`, не на уровне модуля.
- Если на RPC что-то не так, ничего делать не надо: клиент переходит к следующему RPC по кругу и сам отправляет жалобу в пул.
- Остальное (`get_logs_chunked`, `archive=True`, web3.py, `run` для REST-SDK, исключения) разобрано в `docs/FAILOVER.md`.

## 5. Вставь в CLAUDE.md своего проекта

Блок ниже скопируй в `CLAUDE.md` проекта как есть (он на английском намеренно: агент читает его в каждой сессии). Подставлять в нём ничего не нужно.

```markdown
## RPC endpoints: use rpc-pool, only through proxies

Rule #1 (non-negotiable): Work traffic to these RPCs MUST go through your proxy. Never call them from the host's main IP.

- Always obtain RPC endpoints from the owner's rpc-pool via `rpc_pool_client`
  (`RpcPool(proxies=...)`, then `pool.chain(<chain id or key>)`). Never hardcode, paste or invent RPC URLs, and never take them from chainlist or other docs.
- The pool is at https://bomzrpc.online. Its entry point for agents is https://bomzrpc.online/llms.txt: read it before writing any RPC code.
- In scripts, refer to a chain by its chain id (`8453`) or key (`eip155:8453`, `solana:mainnet`), not by an alias (`base`, `eth`): aliases can become ambiguous when catalogs change.
- Work traffic goes only through this project's proxies (`proxies` is required). If the project has no proxies configured, stop and ask the owner.
- Never fall back to a direct connection: do not catch `ProxyError` to retry without a proxy, and do not hit a pool URL with a bare HTTP client, `curl` without a proxy, or `web3` `HTTPProvider`.
  With curl always use `curl --noproxy '' -x "$PROXY" ...` (a plain `-x` goes direct for hosts matched by `NO_PROXY`/`no_proxy`).
- Do not write retry loops around single RPC URLs. On a failure the client already switches to the next RPC of the chain's ring and repeats the same request; handle only its final exceptions (`NoHealthyRpcError`, `ProxyError`, `RpcError`, `LogsRangeTooLargeError`).
- Report failures to the pool: the client does this automatically (`POST /v1/report`); do not build your own reporting.
- Config comes from the environment: `RPC_POOL_URL=https://bomzrpc.online`, `RPC_POOL_API_KEY` (issued by the owner; never commit it).
  Install the client with `uv add "rpc-pool-client @ <wheel URL listed at https://bomzrpc.online/client/>"`.
  Docs: https://bomzrpc.online/docs/RULES.md, https://bomzrpc.online/docs/FAILOVER.md, https://bomzrpc.online/docs/INTEGRATION.md.
```

## 6. Другой язык или без клиента

`rpc_pool_client` написан на Python. Для другого языка в `docs/FAILOVER.md` есть псевдокод алгоритма, который нужно повторить, имея только HTTP API:
`GET /v1/chains/{chain}/rpcs` и `POST /v1/report`. Клиент на любом языке обязан прочитать блок `policy` и заголовок
`X-RPC-Pool-Policy: proxy-required` и не открывать соединение с RPC без прокси.

## 7. Проверка подключения

```bash
curl -si "$RPC_POOL_URL/v1/ping"    # 200, {"pong": true} и заголовок X-RPC-Pool-Policy: proxy-required; ключ не нужен
curl -fsS -H "X-API-Key: $RPC_POOL_API_KEY" "$RPC_POOL_URL/v1/chains/8453/rpcs?limit=3" | jq '.rpcs[] | {url, score}'
```

Эти `curl` ходят к API пула напрямую (это сервер владельца). Любой `curl` к URL самого RPC должен идти только через прокси и выглядеть
так: `curl --noproxy '' -x "$WORK_PROXY" ...`. Без `--noproxy ''` переменная окружения `NO_PROXY`/`no_proxy` заставит `curl` обойти `-x` и пойти напрямую (`docs/RULES.md`).

Если `rpcs` пуст или пул отвечает `503 pool_stale`, у него сейчас нет свежей проверки этой сети: подожди ближайшего цикла (раз в час) или сообщи владельцу.
Пул ты не запускаешь и не чинишь, твоя часть это ключ, клиент и прокси. Затем запусти код из шага 4.

## 8. Типичные ошибки

| Что видишь | Причина и что делать |
|---|---|
| `ValueError: proxies are required. Work traffic to these RPCs MUST go through your proxy...` | не передан `proxies`. Передай список прокси проекта; обхода нет |
| `ValueError: api_url is required: pass it or set RPC_POOL_URL` или `api_key is required: ...` | нет переменных окружения и аргументов |
| `PoolApiError: ... HTTP 401 unauthorized` | ключ неверный или отозван: проверь `RPC_POOL_API_KEY`, при необходимости попроси владельца выдать новый |
| `PoolApiError: ... HTTP 404 unknown_chain` | нет такой сети (или алиас стал неоднозначным: в скриптах задавай chain id или ключ); ближайшие имена: `GET /v1/chains?q=...` |
| `PoolApiError: pool api unreachable and no cache for ...` | сервер пула недоступен, и эту сеть клиент ещё ни разу не загружал (кеша нет). Проверь `RPC_POOL_URL` (`https://bomzrpc.online`) и `curl -si "$RPC_POOL_URL/v1/ping"`; если пул не отвечает, сообщи владельцу |
| `NoCapableRpcError` | у пула нет ни одного RPC сети с нужной возможностью (отправка транзакций, getLogs, архив) |
| `NoHealthyRpcError` | прошли все RPC сети, никто не ответил. Смотри `exc.attempts`; если все RPC в cooldown, подожди минуту |
| `ProxyError` | все прокси в cooldown. **Стоп**, не идти напрямую (`docs/RULES.md`) |
| `ImportError: install rpc-pool-client[web3]` | вызван `web3_provider()` без extra: поставь клиент в форме `uv add "rpc-pool-client[web3] @ <URL сборки>"` (шаг 2) |
| HTTP `429 rate_limited` от пула | больше `600` запросов в минуту на ключ; клиент при этом работает на списке в памяти или кеше |
| HTTP `503 pool_stale` от пула (в логе `pool api unavailable`) | пул давно не завершал проверку (перезапуск, сбой) и не ручается за списки; клиент работает на кеше, пока проверка не закончится. Без кеша это `PoolApiError: pool api unreachable and no cache for ...` |

## Памятка перед тем, как считать подключение готовым

- [ ] Ключ выдал владелец отдельно для проекта, он лежит в `.env`, `.env` в `.gitignore`.
- [ ] Клиент поставлен через `uv add "rpc-pool-client @ <URL сборки>"` (`pip install` только в проекте без uv) с настоящим именем wheel, не через `latest.whl`.
- [ ] `RPC_POOL_URL=https://bomzrpc.online`, `proxies` передаются явно, прямого соединения нет нигде, в том числе в скриптах и тестах.
- [ ] В коде нет жёстко прописанных URL RPC.
- [ ] Блок из шага 5 лежит в `CLAUDE.md` проекта.
- [ ] Смоук-тест из шага 7 проходит.

## Разработка самого пула

Этот раздел для владельца и для тех, кто правит сам пул. Проекту, который только пользуется пулом, он не нужен.

С локальной копией пула (`<путь к репозиторию rpc-pool>`; путь с пробелом пиши в кавычках) клиент можно поставить из исходников, и тогда правки клиента видны сразу:

```bash
uv add --editable "<путь к репозиторию rpc-pool>/packages/rpc_pool_client"
uv add --editable "<путь к репозиторию rpc-pool>/packages/rpc_pool_client[web3]"   # с web3.py
```

Локальный сервер и свой ключ, в каталоге репозитория пула:

```bash
uv run rpc-pool keys create my-project   # ключ печатается ровно один раз, в stdout
uv run rpc-pool once --chains 8453       # собрать каталоги и проверить эту сеть, пока база пуста
uv run rpc-pool serve                    # API на 127.0.0.1:8787; тогда RPC_POOL_URL=http://127.0.0.1:8787
```

- Локальная база это `data/rpc_pool.db` (или `RPC_POOL_DB_PATH`, или опция `--db` **перед** командой). Остальные команды: `keys list`, `keys revoke <id>`.
- Ключи боевого пула владелец создаёт на сервере; они в локальной базе не работают, и наоборот.

Чтобы агент знал правило во **всех** проектах, а не только в тех, где лежит блок из шага 5, в репозитории пула есть готовые файлы:

| Файл | Что это |
|---|---|
| `docs/claude-skill/rpc-pool/SKILL.md` | скилл `rpc-pool`: подхватывается автоматически, когда задача касается RPC, web3, ethers, solana-клиентов, подключений к сетям |
| `docs/claude-skill/global-claude-md-snippet.md` | 3 строки для глобального `~/.claude/CLAUDE.md`: правило и ссылка на сайт пула |

**Установка меняет глобальную конфигурацию Claude Code, поэтому делается только после явного «да» владельца.** Сам агент ничего
в `~/.claude` не пишет, пока не получил это «да» в чате.

```bash
mkdir -p ~/.claude/skills
cp -R "<путь к репозиторию rpc-pool>/docs/claude-skill/rpc-pool" ~/.claude/skills/
cat "<путь к репозиторию rpc-pool>/docs/claude-skill/global-claude-md-snippet.md" >> ~/.claude/CLAUDE.md   # дописывается в конец
```

Удаление: `rm -rf ~/.claude/skills/rpc-pool` и убрать три строки из `~/.claude/CLAUDE.md`. Блок в `CLAUDE.md` проекта работает только в этом проекте,
зато лежит рядом с кодом и попадает в git проекта; скилл и глобальная строка работают везде, но живут на машине владельца. Можно использовать оба.
