> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbitflare.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WebSocket-клиент

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

## Установка

```bash theme={null}
go get github.com/orbitflare/orbitflare-sdk-go
```

```go theme={null}
import "github.com/orbitflare/orbitflare-sdk-go/ws"
```

## Сборка клиента

```go theme={null}
import (
    "time"

    "github.com/orbitflare/orbitflare-sdk-go/orbitflare"
    "github.com/orbitflare/orbitflare-sdk-go/ws"
)

client, err := ws.NewBuilder().
    URL("ws://ny.rpc.orbitflare.com").
    FallbackURL("ws://fra.rpc.orbitflare.com").
    APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN").
    Retry(orbitflare.RetryPolicy{
        InitialDelay: 100 * time.Millisecond,
        MaxDelay:     30 * time.Second,
        Multiplier:   2.0,
        MaxAttempts:  0,
    }).
    PingInterval(10 * time.Second).
    MaxMissedPongs(3).
    Build(ctx)
```

Минимально:

```go theme={null}
client, err := ws.NewBuilder().
    URL("ws://ny.rpc.orbitflare.com").
    Build(ctx)
```

Обратите внимание: `.Build(ctx)` устанавливает WebSocket-соединение до возврата - переданный контекст управляет начальным подключением. Если соединение не удаётся, вы сразу получаете ошибку. Далее клиент работает до `Close()`.

### Методы билдера

**`.URL(url)`** - Основной WebSocket-эндпоинт. Резерв: переменная `ORBITFLARE_WS_URL`.

```go theme={null}
.URL("ws://ny.rpc.orbitflare.com")
```

**`.URLs(urls...)`** - Основной и резервные за один вызов.

```go theme={null}
.URLs("ws://ny.rpc.orbitflare.com", "ws://fra.rpc.orbitflare.com")
```

**`.FallbackURL(url)` / `.FallbackURLs(urls...)`** - Добавить резервные эндпоинты. При переподключении SDK перебирает их.

```go theme={null}
.FallbackURL("ws://fra.rpc.orbitflare.com")
```

**`.APIKey(key)`** - Лицензионный ключ. Резерв: переменная `ORBITFLARE_LICENSE_KEY`. Подставляется в URL при подключении, никогда не попадает в логи или ошибки.

```go theme={null}
.APIKey("ORBIT-XXXXXX-NNNNNN-NNNNNN")
```

**`.Retry(policy)`** - Backoff переподключения. По умолчанию: 100 мс начальная, 30 с максимум, множитель 2x, бесконечно.

```go theme={null}
.Retry(orbitflare.RetryPolicy{
    InitialDelay: 200 * time.Millisecond,
    MaxDelay:     15 * time.Second,
    Multiplier:   2.0,
    MaxAttempts:  0,
})
```

**`.PingInterval(duration)`** - Как часто SDK отправляет WebSocket-кадры `Ping`. По умолчанию 10 с.

```go theme={null}
.PingInterval(15 * time.Second)
```

**`.MaxMissedPongs(n)`** - Сколько ping без ответа допустимо, прежде чем соединение будет закрыто. По умолчанию 3.

```go theme={null}
.MaxMissedPongs(5)
```

**`.Logger(logger)`** - `*slog.Logger` для событий жизненного цикла соединения. Если не задан, управляется через `ORBITFLARE_LOG`.

## Доступные подписки

### `SlotSubscribe(ctx)`

Срабатывает каждый раз, когда слот обработан, подтверждён или финализирован.

```go theme={null}
sub, err := client.SlotSubscribe(ctx)
```

Каждое событие - JSON с полями `slot`, `parent` и `root`:

```json theme={null}
{"slot": 413014740, "parent": 413014739, "root": 413014708}
```

### `AccountSubscribe(ctx, address, commitment)`

Срабатывает при изменении данных указанного аккаунта.

```go theme={null}
sub, err := client.AccountSubscribe(ctx, "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", "confirmed")
```

Возвращает данные аккаунта в base64 с lamports, owner и флагом executable.

### `LogsSubscribe(ctx, mentions, commitment)`

Срабатывает для транзакций, упоминающих заданные адреса. Передайте пустой срез для всех транзакций.

```go theme={null}
sub, err := client.LogsSubscribe(ctx, []string{"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}, "confirmed")
```

Все логи:

```go theme={null}
sub, err := client.LogsSubscribe(ctx, nil, "confirmed")
```

### `SignatureSubscribe(ctx, signature, commitment)`

Срабатывает один раз, когда транзакция достигает заданного уровня commitment. Удобно для подтверждения только что отправленной транзакции.

```go theme={null}
sub, err := client.SignatureSubscribe(ctx, "5K8F2j...", "confirmed")
```

## Чтение событий

Все подписки возвращают `*ws.Subscription`. Вызовите `Next(ctx)`, чтобы получить следующее событие:

```go theme={null}
for {
    event, ok := sub.Next(ctx)
    if !ok {
        break // контекст отменён или клиент завершён
    }
    fmt.Println(string(event))
}
```

`Next(ctx)` возвращает `(json.RawMessage, bool)`. Если `ok` равно false, подписка завершена (контекст отменён или клиент закрыт).

## Отписка

```go theme={null}
sub.Unsubscribe()
```

Отправляет серверу сообщение об отписке. Если вы перестаёте читать подписку, не вызвав это, SDK обнаруживает осиротевшую подписку и отправляет отписку автоматически.

## Несколько подписок

Все подписки работают на одном WebSocket-соединении. SDK внутри маршрутизирует уведомления к нужной подписке.

```go theme={null}
slots, _ := client.SlotSubscribe(ctx)
usdc, _ := client.AccountSubscribe(ctx, "EPjFWdd5...", "confirmed")
logs, _ := client.LogsSubscribe(ctx, []string{"6EF8r..."}, "confirmed")
```

Новые подписки можно добавлять в любой момент, пока работают существующие.

## Переподключение

Если соединение обрывается, фоновая горутина переподключается с экспоненциальным backoff и автоматически переоформляет все подписки. Ваши вызовы `Next(ctx)` продолжают работать - события возобновляются, как только соединение восстановлено.

## Полный пример

Мониторинговая программа, которая подписывается на обновления слотов и изменения аккаунта USDC и печатает живую ленту.

```go theme={null}
package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"

    "github.com/orbitflare/orbitflare-sdk-go/ws"
)

func main() {
    ctx := context.Background()

    client, err := ws.NewBuilder().
        URL("ws://ny.rpc.orbitflare.com").
        Build(ctx)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    slots, err := client.SlotSubscribe(ctx)
    if err != nil {
        log.Fatal(err)
    }
    defer slots.Unsubscribe()

    fmt.Println("watching slots...")
    for {
        event, ok := slots.Next(ctx)
        if !ok {
            return
        }
        var slot struct {
            Slot   uint64 `json:"slot"`
            Parent uint64 `json:"parent"`
        }
        json.Unmarshal(event, &slot)
        fmt.Printf("slot %d (parent %d)\n", slot.Slot, slot.Parent)
    }
}
```
