# WebSocket

> WebSocket gateway NeuroChain — підключення до /ws, повідомлення init, усі живі події (блоки, транзакції, утримані перекази, рішення AI, сповіщення контрактів), підписка на баланси, RPC через сокет і ліміти.

Source: https://docs.nro.world/uk/reference/websocket

Gateway обслуговує WebSocket за шляхом **`/ws`** на тому ж хості й порту, що і REST API:

```
ws://localhost:3000/ws
```

Без автентифікації. Gateway надсилає події мережі й AI кожному підключеному клієнту; клієнт також може підписатися на баланси і викликати набір RPC-методів через той самий сокет.

## Підключення

```js
const ws = new WebSocket('ws://localhost:3000/ws')

ws.onmessage = (e) => {
  const msg = JSON.parse(e.data)
  switch (msg.type) {
    case 'init':      console.log('height', msg.data.height); break
    case 'new_block': console.log('block', msg.data.height, msg.data.proposer); break
    case 'tx_held':   console.warn('held', msg.data.tx_hash); break
  }
}
```

Кожне повідомлення сервера — JSON з полем `type`. Події кладуть вміст у `data`; `balance`, `rpc_reply` і `error` мають поля на верхньому рівні.

Перепідключайтеся при `close` із затримкою — вебзастосунок повторює спроби з експоненційно зростаючою паузою.

## Події

Gateway опитує ноду кожні `POLL_NODE_INTERVAL_MS` (за замовчуванням 2 000 мс) і перетворює побачене на події.

| `type` | Коли | Поля `data` |
|--------|------|-------------|
| `init` | Одразу після підключення | `height`, `tps`, `finality_ms`, `shard_count`, `validator_count`, `blocks`, `txs`, `total_blocks`, `total_txs` |
| `metrics` | Кожне опитування | `tps`, `height`, `finality_ms` |
| `new_block` | З'явилася нова висота | Блок у вигляді, як його повертає `GET /api/v1/blocks/:height` |
| `validator_update` | Кожні 10 блоків | `nodes`, `total_staked`, `height` |
| `tx` | Gateway передав транзакцію ноді | `hash`, `from`, `to`, `value`, `gas`, `gas_price`, `shard`, `timestamp`, `block` |
| `faucet` | Кран поповнив адресу | `address`, `amount`, `tx_hash` |
| `tx_held` | Переказ утримано [політикою Sentinel](/uk/ai/sentinel) | `tx_hash`, `to`, `value_human` |
| `tx_released` | Утриманий переказ звільнено | `tx_hash`, `to`, `value_human` |
| `tx_cancelled` | Утриманий переказ скасовано | `tx_hash`, `to`, `value_human` |
| `tx_anomaly` | Sentinel оцінив надіслану транзакцію як аномальну | `tx_hash`, `score`, `category` |
| `oracle_decision` | Записано рішення AI | `model`, `kind`, `subject`, `decision`, `confidence`, `timestamp` |
| `model_voting_update` | AI-моделі чекають підтверджень валідаторів | `models`, `height` |
| `contract_alert` | Незвичний обсяг викликів контракту або контракт з низькою оцінкою почали викликати | `address`, `alert`, `severity`, `call_count`, `delta`, `timestamp`, `model` |

::: tip `tx` — повідомлення про надсилання
Подія `tx` виникає, коли gateway передає транзакцію ноді, ще до потрапляння в блок — її поле `status` не остаточне. Стежте за фіналізацією через `new_block`, підписку на баланс або `GET /api/v1/transactions/:hash`.
:::

Сповіщення контрактів перевіряються кожні `SENTINEL_CHECK_INTERVAL_MS` (за замовчуванням 30 000 мс).

## Підписка на баланс

```js
ws.send(JSON.stringify({ type: 'subscribe_balance', address: 'neuro:<15 hex>.human' }))
```

Gateway відповідає одразу і знову після кожного нового блоку:

```json
{ "type": "balance", "address": "neuro:…", "balance": 50000000, "balance_human": "50", "nonce": 0 }
```

Підписки завершуються, коли сокет закривається.

## RPC через сокет

```js
ws.send(JSON.stringify({ type: 'rpc', id: 1, method: 'get_account', params: { address: 'neuro:…' } }))
// → { "type": "rpc_reply", "id": 1, "result": { … }, "error": null }
```

Методи відповідають REST-ендпоінтам і повертають ті самі дані:

| Група | Методи |
|-------|--------|
| Мережа | `get_status`, `get_blocks`, `get_block`, `get_block_txs`, `get_txs`, `search_tx`, `get_account` |
| Валідатори | `get_validator`, `get_validators` |
| Імена і NFT | `human_name_price`, `register_human`, `get_my_nfts`, `get_nft`, `transfer_nft` |
| Управління | `get_governance_params`, `get_proposals`, `get_proposal`, `create_proposal`, `cast_vote` |
| Сховища | `get_vault_config`, `list_member_vaults`, `get_vault_proposals`, `create_vault_proposal`, `sign_vault_proposal`, `add_vault_member`, `set_vault_threshold` |
| AI | `get_oracle_log`, `get_oracle_entry`, `get_model_registry`, `get_model_registry_entry`, `register_model` |
| Контракти | `get_contracts`, `get_contract`, `get_contract_events`, `deploy_contract`, `call_contract`, `update_contract_score`, `get_certificates`, `get_certificate`, `get_contract_certificate`, `certify_contract` |
| Тестові мережі | `faucet` |

Записуючим методам потрібні ті самі підписані поля, що і їхньому REST-ендпоінту (`signature`, `public_key`, `nonce`), усередині `params`.

## Ліміти

| Ліміт | За замовчуванням | Змінна | При перевищенні |
|-------|------------------|--------|-----------------|
| Розмір повідомлення | 64 КБ | `WS_MAX_MSG_BYTES` | `{"type":"error","error":"message too large"}` |
| Підписок на баланс на з'єднання | 50 | `WS_MAX_SUBS_PER_CLIENT` | `subscription limit exceeded` |
| RPC-викликів за секунду на з'єднання | 30 | `WS_RPC_MAX_RPS` | `rpc rate limit exceeded` |
| Очищення мертвих з'єднань | кожні 30 с | `WS_STALE_CHECK_MS` | — |

## Python

```python
import asyncio, json, websockets

async def listen():
    async with websockets.connect("ws://localhost:3000/ws") as ws:
        await ws.send(json.dumps({"type": "subscribe_balance", "address": "neuro:<15 hex>.human"}))
        async for raw in ws:
            msg = json.loads(raw)
            print(msg["type"], msg.get("data", msg))

asyncio.run(listen())
```
