> ## 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.

# Клиент Apex на Go

> orbitflare-apex-go является клиентом на Go для OrbitFlare Apex: постоянное QUIC-соединение с переподключениями 0-RTT, а также HTTP-хелперы для JSON-RPC, пакетов и бандлов.

`orbitflare-apex-go` является клиентом на Go для Apex. Он держит одно постоянное QUIC-соединение на каждый эндпоинт Apex, аутентифицируется клиентским сертификатом, производным от вашего API-ключа, отправляет одну сериализованную транзакцию на поток и переподключается с 0-RTT при обрыве соединения. Его возможности совпадают с возможностями [клиента на Rust](/ru/apex/rust-client), а его клиентский сертификат побайтово идентичен сертификату клиента на Rust.

## Установка

Модуль называется `github.com/orbitflare/orbitflare-apex-go`; исходный код находится на [GitHub](https://github.com/orbitflare/orbitflare-apex-go):

```bash theme={null}
go get github.com/orbitflare/orbitflare-apex-go@latest
go get github.com/gagliardetto/solana-go@v1.24.0
```

Модуль состоит из двух пакетов:

| Пакет | Импорт | Содержимое |
| - | - | - |
| `apex` | `github.com/orbitflare/orbitflare-apex-go` | QUIC-клиент, регионы, чаевые и формат передачи |
| `rpc` | `github.com/orbitflare/orbitflare-apex-go/rpc` | Эндпоинт Apex по HTTP, а также хелперы для blockhash и подтверждения на любом Solana RPC |

Транзакции собираются с помощью [`solana-go`](https://github.com/gagliardetto/solana-go), который поддерживает сообщения legacy, v0 и [v1](/ru/apex/transaction-v1). Путь QUIC не зависит от пакета `rpc`.

Модуль требует Go 1.26 или новее.

## Быстрый старт

Полная программа. Она собирает memo-транзакцию с вычислительным бюджетом и чаевыми, отправляет её по QUIC и ждёт подтверждения. Она читает те же переменные окружения, что и [Быстрый старт](/ru/apex/quickstart): `APEX_API_KEY`, `KEYPAIR_PATH` и `SOLANA_RPC_URL`.

```go main.go theme={null}
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/gagliardetto/solana-go"
	computebudget "github.com/gagliardetto/solana-go/programs/compute-budget"

	apex "github.com/orbitflare/orbitflare-apex-go"
	"github.com/orbitflare/orbitflare-apex-go/rpc"
)

func getenv(name, fallback string) string {
	if v := os.Getenv(name); v != "" {
		return v
	}
	return fallback
}

func main() {
	ctx := context.Background()
	apiKey := os.Getenv("APEX_API_KEY")
	payer, err := solana.PrivateKeyFromSolanaKeygenFile(getenv("KEYPAIR_PATH", "payer.json"))
	if err != nil {
		log.Fatal(err)
	}
	region := apex.Frankfurt

	client, err := apex.Connect(ctx, region, apiKey)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	tipAccounts, err := rpc.New(region, apiKey).GetTipAccounts(ctx)
	if err != nil {
		log.Fatal(err)
	}
	tipAccount, _ := apex.PickTipAccount(tipAccounts)

	solanaRPC := rpc.NewSolanaRPC(getenv("SOLANA_RPC_URL", "https://api.mainnet-beta.solana.com"))
	blockhash, err := solanaRPC.LatestBlockhash(ctx)
	if err != nil {
		log.Fatal(err)
	}

	memo := solana.NewInstruction(
		solana.MustPublicKeyFromBase58("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
		solana.AccountMetaSlice{solana.Meta(payer.PublicKey()).WRITE().SIGNER()},
		[]byte(fmt.Sprintf("apex go %d", time.Now().UnixNano())),
	)
	tx, err := solana.NewTransaction([]solana.Instruction{
		computebudget.NewSetComputeUnitLimitInstruction(100_000).Build(),
		computebudget.NewSetComputeUnitPriceInstruction(10_000).Build(),
		memo,
		apex.TipInstruction(payer.PublicKey(), tipAccount, apex.MinTipLamports),
	}, blockhash, solana.TransactionPayer(payer.PublicKey()))
	if err != nil {
		log.Fatal(err)
	}
	if _, err := tx.Sign(func(solana.PublicKey) *solana.PrivateKey { return &payer }); err != nil {
		log.Fatal(err)
	}

	sentAt := time.Now()
	signature, err := client.SendTransaction(ctx, tx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("sent in %d us\n", time.Since(sentAt).Microseconds())

	slot, landed, err := solanaRPC.Confirm(ctx, signature.String(), 30*time.Second)
	switch {
	case err != nil:
		log.Fatal(err)
	case landed:
		fmt.Printf("landed in slot %d after %d ms: %s\n", slot, time.Since(sentAt).Milliseconds(), signature)
	default:
		fmt.Printf("not confirmed within 30 s: %s\n", signature)
	}
}
```

```bash theme={null}
go mod init apexdemo
go get github.com/orbitflare/orbitflare-apex-go@latest github.com/gagliardetto/solana-go@v1.24.0
APEX_API_KEY=... KEYPAIR_PATH=payer.json go run .
```

`SendTransaction` возвращает управление, как только транзакция записана в поток, за микросекунды и без подтверждения получения. Фрагменты ниже встраиваются в эту программу: они используют её `ctx`, `client`, `apiKey`, `payer`, `tipAccount`, `blockhash`, `solanaRPC` и `tx`. Добавьте импорты стандартной библиотеки, которые использует фрагмент, например `errors`, `encoding/base64` или `sync`.

## Отправка с ответом

Двунаправленный поток читает ответ Apex перед возвратом: подпись, если транзакция принята, или `*apex.RejectedError` с [кодом допуска](/ru/apex/errors-and-rate-limits#коды-допуска-quic) и сообщением. Это стоит одного дополнительного обмена с эндпоинтом, поэтому используйте его при интеграции или когда вам нужна причина сразу в ответе.

```go theme={null}
signature, err := client.SendTransactionWithResponse(ctx, tx)
var rejected *apex.RejectedError
switch {
case errors.As(err, &rejected):
	fmt.Printf("rejected: %s: %s\n", rejected.Code, rejected.Message)
case err != nil:
	log.Fatal(err)
default:
	fmt.Println("accepted:", signature)
}
```

`SendWithResponse(ctx, wire)` делает то же самое с байтами, которые у вас уже есть, и возвращает сырой `apex.Admission` (`Accepted`, `Signature`, `Code`, `Message`).

## Транзакции v1

Транзакции v1 могут иметь размер до 4096 байт. Вычислительный бюджет задаётся в `solana.TransactionConfig` сообщения, а не инструкциями ComputeBudget, и каждый незаданный лимит равен 0, поэтому задайте лимит вычислительных единиц, лимит размера загружаемых данных аккаунтов и, ради приоритета, комиссию. См. [Транзакция v1](/ru/apex/transaction-v1).

```go theme={null}
config := solana.TransactionConfig{}.
	WithComputeUnitLimit(100_000).
	WithLoadedAccountsDataSizeLimit(1024 * 1024).
	WithPriorityFee(4_000)

tx, err := solana.NewTransaction([]solana.Instruction{
	yourInstruction,
	apex.TipInstruction(payer.PublicKey(), tipAccount, apex.MinTipLamports),
}, blockhash,
	solana.TransactionPayer(payer.PublicKey()),
	solana.TransactionV1Config(config),
)
if err != nil {
	log.Fatal(err)
}
if _, err := tx.Sign(func(solana.PublicKey) *solana.PrivateKey { return &payer }); err != nil {
	log.Fatal(err)
}
signature, err := client.SendTransaction(ctx, tx)
if err != nil {
	log.Fatal(err)
}
fmt.Println("sent v1:", signature)
```

## Сырые байты

Если ваша транзакция уже сериализована (другим подписантом, другим процессом или в файле), отправьте байты как есть. Ничто не кодирует их заново.

```go theme={null}
wire, err := base64.StdEncoding.DecodeString(signedBase64)
if err != nil {
	log.Fatal(err)
}
if err := client.SendTransactionBytes(ctx, wire); err != nil {
	log.Fatal(err)
}
```

`apex.EncodePacket(wire, mevProtect, maxRetries)` возвращает в точности тот пакет, который клиент записывает в поток: длину из 8 байт, транзакцию и завершающий блок из 3 байт. Это эталон для других языков.

## HTTP

Пакет `rpc` покрывает все HTTP-маршруты эндпоинта Apex. Он передаёт ключ в заголовке `x-api-key`.

```go theme={null}
apexRPC := rpc.New(apex.Frankfurt, apiKey)

if err := apexRPC.Ping(ctx); err != nil {
	log.Fatal(err)
}

wire, err := apex.SerializeTransaction(tx)
if err != nil {
	log.Fatal(err)
}

signature, err := apexRPC.SendTransaction(ctx, wire, false, nil)
if err != nil {
	log.Fatal(err)
}
fmt.Println("JSON-RPC:", signature)

signature, err = apexRPC.SendTransactionBinary(ctx, wire, false, apex.RetryBudget(20))
if err != nil {
	var rpcErr *rpc.Error
	if errors.As(err, &rpcErr) {
		fmt.Println("rejected:", rpcErr.Message)
	}
	log.Fatal(err)
}
fmt.Println("send-bin:", signature)
```

`SendTransaction` является JSON-RPC `sendTransaction` и подходит для замены в существующем коде. `SendTransactionBinary` отправляет сырые байты на `/send-bin`, самый дешёвый путь по HTTP. Последние два аргумента: `mevProtect` и `maxRetries`; `nil` оставляет бюджет повторов эндпоинта по умолчанию.

### Пакеты

`SendBatch` отправляет до 16 транзакций в одном запросе. Каждая принимается отдельно, а результат показывает, какие из них были приняты.

```go theme={null}
wires := make([][]byte, 0, len(txs))
for _, tx := range txs {
	wire, err := apex.SerializeTransaction(tx)
	if err != nil {
		log.Fatal(err)
	}
	wires = append(wires, wire)
}

result, err := apexRPC.SendBatch(ctx, wires, false, nil)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("%d accepted, %d rejected\n", result.Accepted, result.Rejected)
for i, item := range result.Results {
	if item.Accepted {
		fmt.Println(i, "accepted:", item.Signature)
	} else {
		fmt.Println(i, "rejected:", item.Error, item.Message)
	}
}
```

### Бандлы

`SendBundle` отправляет от одной до четырёх транзакций, которые попадают в блок по порядку, все или ни одной, причём ровно одна из них с чаевыми. Опрашивайте `BundleStatuses`, пока бандл не попадёт в блок или не завершится неудачей. См. [Бандлы](/ru/apex/bundles).

```go theme={null}
firstWire, _ := apex.SerializeTransaction(first)
secondWire, _ := apex.SerializeTransaction(second)

accepted, err := apexRPC.SendBundle(ctx, [][]byte{firstWire, secondWire})
if err != nil {
	log.Fatal(err)
}
fmt.Println("bundle", accepted.BundleID)

for range 60 {
	statuses, err := apexRPC.BundleStatuses(ctx, []string{accepted.BundleID})
	if err != nil {
		log.Fatal(err)
	}
	if len(statuses) > 0 {
		switch statuses[0].State {
		case rpc.BundleLanded:
			fmt.Println("landed in slot", *statuses[0].LandedSlot)
			return
		case rpc.BundleFailed, rpc.BundleInvalid:
			fmt.Println("did not land:", statuses[0].State)
			return
		}
	}
	time.Sleep(500 * time.Millisecond)
}
```

## Параллельные отправки

`*apex.Client` безопасен для параллельного использования, и каждая отправка получает собственный поток в одном соединении. Создайте клиент один раз и отправляйте из любого числа горутин.

```go theme={null}
var wg sync.WaitGroup
for _, tx := range txs {
	wg.Add(1)
	go func() {
		defer wg.Done()
		signature, err := client.SendTransaction(ctx, tx)
		if err != nil {
			log.Println("send failed:", err)
			return
		}
		log.Println("sent", signature)
	}()
}
wg.Wait()
```

## Регионы

Каждый эндпоинт Apex представлен константой `apex.Region`. `apex.ParseRegion` принимает код или название города, а `apex.AllRegions` содержит их список.

| Константа | Код |
| - | - |
| `apex.Frankfurt` | `fra` |
| `apex.Amsterdam` | `ams` |
| `apex.Dublin` | `dub` |
| `apex.London` | `lon` |
| `apex.NewYork` | `nyc` |
| `apex.SaltLakeCity` | `slc` |
| `apex.Singapore` | `sgp` |
| `apex.Tokyo` | `tyo` |
| `apex.Siauliai` | `sqq` |
| `apex.Global` | `global` |

```go theme={null}
region, ok := apex.ParseRegion("tokyo")
if !ok {
	log.Fatal("unknown region")
}
fmt.Println(region.Code(), region.QUICEndpoint(), region.RPCURL())
```

`QUICEndpoint()` возвращает `<code>.apex.orbitflare.com:7001`, а `RPCURL()` возвращает `http://<code>.apex.orbitflare.com`. См. [Эндпоинты и регионы](/ru/apex/endpoints).

## Транспорты

| | QUIC однонаправленный | QUIC двунаправленный | Бинарный HTTP | JSON-RPC |
| - | - | - | - | - |
| **Вызов** | `SendTransaction`, `SendTransactionBytes` | `SendTransactionWithResponse`, `SendWithResponse` | `rpc.Client.SendTransactionBinary`, `SendBatch` | `rpc.Client.SendTransaction` |
| **Возвращает** | Подпись. В ответ ничего не читается | Принято либо код и сообщение отклонения | Подпись или ошибку с меткой | Подпись или ошибку JSON-RPC |
| **Стоимость отправки** | Один поток в прогретом соединении | Один поток плюс один обмен с эндпоинтом | HTTP-запрос с сырыми байтами | HTTP-запрос с base64 в JSON |
| **Лучше всего для** | Ботов с постоянным соединением | Интеграции, отладки, инструментов, которым нужна причина сразу в ответе | Пакетов | Замены в коде с `sendTransaction` |

Транспорт не влияет на приоритет и маршрутизацию. На них влияют чаевые.

## API

| Элемент | Описание |
| - | - |
| `apex.Connect(ctx, region, apiKey)` | Подключение с эфемерным локальным портом и параметрами по умолчанию |
| `apex.ConnectWithOptions(ctx, opts, apiKey)` | Подключение с [`apex.Options`](#параметры) |
| `SendTransaction(ctx, tx)` | Сериализует (legacy, v0 или v1) и отправляет в однонаправленном потоке. Возвращает первую подпись |
| `SendTransactionBytes(ctx, wire)` | То же самое с байтами, которые у вас уже есть. Ничто не кодирует их заново |
| `SendTransactionWithResponse(ctx, tx)` | Двунаправленный поток. `*apex.RejectedError` при отклонении |
| `SendWithResponse(ctx, wire)` | То же самое с байтами. Возвращает сырой `apex.Admission` |
| `Health()`, `ReconnectsTotal()`, `ZeroRTTResumptionsTotal()`, `RemoteAddr()` | Состояние соединения для ваших метрик |
| `Reconnect(ctx)`, `Close()` | Жизненный цикл |
| `apex.TipInstruction(payer, tipAccount, lamports)` | Собирает перевод чаевых |
| `apex.PickTipAccount(accounts)` | Выбирает один из аккаунтов для чаевых случайным образом |
| `apex.MinTipLamports` | Минимум стандартного тарифа: 1,000,000 lamports |
| `rpc.Client` | Эндпоинт Apex по HTTP: `GetTipAccounts`, `SendTransaction` (JSON-RPC), `SendTransactionBinary`, `SendBatch`, `SendBundle`, `BundleStatuses`, `Ping` |
| `rpc.SolanaRPC` | Любой Solana RPC: `LatestBlockhash`, `Confirm(ctx, signature, timeout)` |
| `rpc.FetchVaults(ctx, solanaRPCURL)` | Читает vault-аккаунты программы чаевых из чейна (`rpc.TipProgramID`). Это не список для чаевых: он включает неопубликованные vault-аккаунты, которые эндпоинт отклоняет. Используйте `GetTipAccounts` для аккаунтов, которым отправлять чаевые, а это - только чтобы проверить, что конкретный аккаунт является настоящим vault-аккаунтом |
| `apex.ClientPubkey(apiKey)` | Ключ сертификата, который выводится из вашего API-ключа, как он показан в вашей панели управления |
| `apex.SerializeTransaction(tx)` | Канонические байты для передачи для legacy, v0 и v1 |
| `apex.EncodePacket`, `apex.FrameParts`, `apex.DecodeAdmission` | Формат передачи QUIC как эталон для других языков |
| `apex.RetryBudget(n)` | Значение `MaxRetries` для параметров и HTTP-вызовов |

Каждый вызов, который обращается к сети, принимает `context.Context`, поэтому вы можете отменить отправку или задать ей крайний срок. Вызовите `Close`, когда закончите работу; он останавливает фоновое переподключение и закрывает сокет.

## Параметры

Нулевое значение каждого поля даёт значения по умолчанию, поэтому задавайте только то, что вам нужно:

| Поле | По умолчанию | Описание |
| - | - | - |
| `Endpoint` | QUIC-адрес Франкфурта | `host:port` QUIC-адреса |
| `MEVProtect` | `false` | Пропускать лидеров из блок-листа Shield. См. [Защита от MEV](/ru/apex/mev-protection) |
| `MaxRetries` | `nil` | Бюджет повторов на транзакцию в виде `apex.RetryBudget(n)`. `nil` использует значение эндпоинта по умолчанию |
| `BindAddr` | Эфемерный порт | Локальный адрес привязки UDP для списков разрешений файрвола |
| `ConnectTimeout` | 3 s | Тайм-аут рукопожатия |
| `SendTimeout` | 2 s | Тайм-аут одной отправки |
| `KeepAlive` | 1 s | Интервал QUIC PING. Тайм-аут простоя эндпоинта составляет 30 s |
| `DisableAutoReconnect` | `false` | При `false` отправка, которая не удалась из-за потери соединения, переподключается и отправляет ещё раз |
| `DisableProactiveReconnect` | `false` | При `false` фоновая горутина выполняет новое рукопожатие, как только замечен обрыв, чтобы следующая отправка за него не платила |

```go theme={null}
client, err := apex.ConnectWithOptions(ctx, apex.Options{
	Endpoint:       apex.NewYork.QUICEndpoint(),
	BindAddr:       "0.0.0.0:47001",
	MEVProtect:     true,
	MaxRetries:     apex.RetryBudget(30),
	ConnectTimeout: 3 * time.Second,
	SendTimeout:    time.Second,
	KeepAlive:      time.Second,
}, apiKey)
if err != nil {
	log.Fatal(err)
}
defer client.Close()
```

## Keep-alive и переподключения

Соединение остаётся открытым благодаря QUIC PING каждую секунду при тайм-ауте простоя 30 секунд на эндпоинте Apex. В прогретом соединении отправка сводится к одному открытию потока и одной записи.

Если соединение оборвалось:

* Фоновая горутина замечает это и сразу выполняет новое рукопожатие, поэтому следующая отправка обычно находит живое соединение. Если соединение закрыл сам эндпоинт (неизвестный ключ, слишком много соединений), горутина удваивает ожидание после каждой попытки, вплоть до 30 секунд, вместо того чтобы настойчиво повторять.
* Переподключение использует закэшированный сессионный тикет и отправляет ожидающую транзакцию в первом пакете рукопожатия (**0-RTT**). Если эндпоинт отклоняет ранние данные, клиент отправляет их повторно после рукопожатия.
* Отправка, которая не удалась из-за потери соединения, переподключается и повторяется один раз. Установите `DisableAutoReconnect`, чтобы обрабатывать это самостоятельно.
* При каждом переподключении имя хоста разрешается заново, поэтому клиент на `apex.Global` следует за балансировщиком к следующему ближайшему эндпоинту, когда его собственный выходит из строя, без перезапуска. Разрешение выполняется в фоновом переподключении, а не при отправке; если оно не удалось или заняло больше двух секунд, клиент сохраняет прежний адрес. Эндпоинт, заданный IP-адресом, никогда не разрешается.

Экспортируйте состояние соединения в свои метрики:

```go theme={null}
go func() {
	for range time.Tick(10 * time.Second) {
		log.Printf("apex health=%s reconnects=%d zero_rtt=%d remote=%s",
			client.Health(), client.ReconnectsTotal(), client.ZeroRTTResumptionsTotal(), client.RemoteAddr())
	}
}()
```

## Ошибки

Ошибки оборачивают сигнальное значение, поэтому `errors.Is` показывает, на каком этапе произошёл сбой, а `errors.As` даёт доступ к типизированным ошибкам.

| Ошибка | Значение |
| - | - |
| `*apex.RejectedError` | Эндпоинт отклонил транзакцию в двунаправленном потоке. `Code` и `Message` содержат [код допуска](/ru/apex/errors-and-rate-limits#коды-допуска-quic) и его причину |
| `*apex.TooLargeError` | Больше 4096 байт. Перехватывается локально до какой-либо отправки |
| `apex.ErrTimeout` | Истёк тайм-аут подключения или отправки |
| `apex.ErrResolve`, `ErrBind`, `ErrTLS`, `ErrConnect`, `ErrConnection`, `ErrWrite`, `ErrRead` | Транспортные сбои |
| `apex.ErrSerialize`, `ErrBadAdmission`, `ErrNoSignature` | Проблемы кодирования |
| `apex.ErrClosed` | Не удалось завершить поток |
| `apex.ErrClientStopped` | Отправка после `Close` |

```go theme={null}
_, err := client.SendTransaction(ctx, tx)
var tooLarge *apex.TooLargeError
switch {
case err == nil:
case errors.As(err, &tooLarge):
	fmt.Println("transaction is", tooLarge.Size, "bytes")
case errors.Is(err, apex.ErrTimeout):
	fmt.Println("timed out, the transaction may still have been sent")
case errors.Is(err, apex.ErrConnect), errors.Is(err, apex.ErrConnection):
	fmt.Println("connection problem:", err)
default:
	fmt.Println(err)
}
```

Пакет `rpc` возвращает `*rpc.Error` с кодом и сообщением при отклонении JSON-RPC или простого HTTP-маршрута и `rpc.ErrBadResponse` для ответа, который он не может прочитать. Отклонение простого HTTP-маршрута имеет код 0 и сообщение вида `label: message`, а метки перечислены в разделе [Ошибки и лимиты запросов](/ru/apex/errors-and-rate-limits).

## Проверьте свой ключ

Выведите ключ сертификата, который выводится из вашего API-ключа. Он должен совпадать с клиентским публичным ключом, показанным рядом с ключом в вашей панели управления; если не совпадает, ключ был скопирован неправильно.

```go theme={null}
fmt.Println(apex.ClientPubkey(apiKey))
```

## Примеры в репозитории

Все примеры читают `APEX_API_KEY`, `KEYPAIR_PATH` (по умолчанию `payer.json`), `SOLANA_RPC_URL` и при необходимости `APEX_REGION`, `APEX_QUIC`, `APEX_RPC`, `TIP_LAMPORTS`, `APEX_TX_VERSION` (`legacy` или `v1`), `APEX_MEMO_BYTES` и `APEX_CU_LIMIT`. Каждый отправляет memo с чаевыми и сообщает слот, в который оно попало.

| Пример | Что показывает |
| - | - |
| `quic_send` | Однонаправленный поток: самый быстрый путь, затем подтверждение через Solana RPC |
| `quic_send_with_response` | Двунаправленный поток: ответ о принятии или отклонении и как читать отклонение |
| `rpc_send` | JSON-RPC `sendTransaction` по HTTP с тем же правилом чаевых |
| `http_binary` | `/ping`, `/send-bin` с сырыми байтами и `/send-batch` из трёх транзакций |
| `raw_bytes` | Заранее сериализованные байты при передаче и точная структура пакета |
| `throughput` | Одно прогретое соединение, N параллельных отправок, p50 и p99 на отправку, число попаданий в блок |
| `bundle` | Атомарный бандл из двух транзакций, одна с чаевыми, и его статус до попадания в блок |
| `transfer` | Перевод SOL с чаевыми на `TO` в размере `LAMPORTS`, legacy или v1, с ответом о допуске |
| `client_pubkey` | Ключ сертификата, который выводится из API-ключа, для сравнения с вашей панелью управления |

```bash theme={null}
git clone https://github.com/orbitflare/orbitflare-apex-go.git
cd orbitflare-apex-go
APEX_API_KEY=... KEYPAIR_PATH=payer.json SOLANA_RPC_URL=https://... \
  go run ./examples/quic_send
```

## Чего клиент не делает

* Не собирает и не подписывает транзакции и не выбирает вашу приоритетную комиссию.
* Не выполняет симуляцию и preflight-проверки. Этого не делает никто между вами и лидером.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.