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

# Рецепты

> Готовые к запуску программы для задач, с которых большинство начинает работу с OrbitFlare: проверка ключа, отслеживание аккаунта, ожидание подтверждения транзакции, поток транзакций программы и чтение истории кошелька. TypeScript, Rust, Go и curl.

Каждая программа на этой странице полностью готова. Скопируйте её в файл, задайте ключ и запустите. Перед публикацией каждая из них была проверена на реальных эндпоинтах OrbitFlare.

## Перед началом

Задайте лицензионный ключ один раз. Все три SDK читают его из переменной окружения, поэтому он никогда не появляется в вашем коде.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
export ORBITFLARE_LICENSE_KEY=ORBIT-XXXXXX-NNNNNN-NNNNNN
```

В примерах используется Франкфурт (`fra`). Замените хост на [регион](/ru/infrastructure), ближайший к вашим серверам. Если лицензия работает в [режиме API Key Only](/ru/authentication#режимы-аутентификации), SDK также передают ключ в Jetstream и Yellowstone. В режиме IP Whitelist перед запуском рецепта с потоковой передачей добавьте IP вашего сервера в лицензию в [панели управления](https://orbitflare.com/dashboard).

**TypeScript**

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
npm init -y && npm pkg set type=module
npm install @orbitflare/sdk ws @grpc/grpc-js yaml bs58
```

`ws` и `@grpc/grpc-js` являются необязательными peer-зависимостями SDK. Без них клиенты WebSocket и Jetstream падают с ошибкой `Cannot find package 'ws'` или `Cannot find package '@grpc/grpc-js'`. Запускайте файл командой `npx tsx file.ts`.

**Rust**

```toml Cargo.toml theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
[package]
name = "recipes"
version = "0.1.0"
edition = "2024"

[dependencies]
orbitflare-sdk = { version = "0.4", features = ["ws", "jetstream"] }
tokio = { version = "1", features = ["full"] }
serde_json = "1"
bs58 = "0.5"
```

Сохраняйте каждую программу в `examples/` и запускайте командой `cargo run --example name`.

**Go**

```bash theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
go mod init recipes
go get github.com/orbitflare/orbitflare-sdk-go@latest github.com/mr-tron/base58
```

Поместите каждую программу в отдельную папку с файлом `main.go` и запускайте командой `go run ./folder`.

## Проверьте, что ключ работает

Самый быстрый способ исключить проблемы с настройкой. Если программа выводит номер слота, с ключом, регионом и сетевым маршрутом всё в порядке.

```bash curl theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl -s https://fra.rpc.orbitflare.com \
  -H "x-api-key: $ORBITFLARE_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"getSlot"}'
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { RpcClientBuilder } from '@orbitflare/sdk';

// The key comes from ORBITFLARE_LICENSE_KEY, so it never has to appear in code.
const rpc = new RpcClientBuilder().url('https://fra.rpc.orbitflare.com').build();

// A slot number back means the key, region and network path all work.
console.log('slot:', await rpc.getSlot());
```

```rust Rust theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
use orbitflare_sdk::RpcClientBuilder;

#[tokio::main]
async fn main() -> orbitflare_sdk::Result<()> {
    // The key comes from ORBITFLARE_LICENSE_KEY, so it never has to appear in code.
    let rpc = RpcClientBuilder::new()
        .url("https://fra.rpc.orbitflare.com")
        .build()?;

    // A slot number back means the key, region and network path all work.
    println!("slot: {}", rpc.get_slot().await?);
    Ok(())
}
```

```go Go theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
package main

import (
	"context"
	"fmt"
	"log"

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

func main() {
	// The key comes from ORBITFLARE_LICENSE_KEY, so it never has to appear in code.
	client, err := rpc.NewBuilder().URL("https://fra.rpc.orbitflare.com").Build()
	if err != nil {
		log.Fatal(err)
	}

	// A slot number back means the key, region and network path all work.
	slot, err := client.GetSlot(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("slot:", slot)
}
```

Если что-то не так, ошибка подскажет причину:

| Ответ | Значение |
| - | - |
| `-32401 missing API key` (HTTP 401) | Ключ не был передан. Проверьте заголовок `x-api-key` или параметр `?api_key=`, а также что `ORBITFLARE_LICENSE_KEY` экспортирован в той же оболочке |
| `-32402 invalid API key` (HTTP 403) | Ключ неверный или был перевыпущен. Скопируйте его заново из панели управления |
| `-32404 IP not whitelisted` (HTTP 403) | IP этой машины или этот регион не разрешены в вашей лицензии. Добавьте IP в панели управления или используйте регион, входящий в лицензию |

Полный список приведён в [справочнике ошибок](/ru/rpc/error-codes).

## Отслеживайте изменения аккаунта

Программа подписывается через WebSocket и выводит каждое обновление. В примере отслеживается sysvar Clock, потому что он меняется каждый слот и вывод появляется сразу. Подставьте адрес любого аккаунта.

```bash wscat theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
wscat -c "wss://fra.rpc.orbitflare.com?api_key=$ORBITFLARE_LICENSE_KEY"
> {"jsonrpc":"2.0","id":1,"method":"accountSubscribe","params":["SysvarC1ock11111111111111111111111111111111",{"encoding":"base64","commitment":"confirmed"}]}
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { WsClientBuilder } from '@orbitflare/sdk/ws';

// The SDK pings every 10 s and resubscribes on its own after a reconnect.
const ws = await new WsClientBuilder().url('wss://fra.rpc.orbitflare.com').build();

// Notifies only when the account changes. The Clock sysvar changes every slot.
const sub = await ws.accountSubscribe('SysvarC1ock11111111111111111111111111111111', 'confirmed');

for (let account = await sub.next(); account !== undefined; account = await sub.next()) {
  // Clock layout: the first 8 bytes are the current slot as a little-endian u64.
  const data = Buffer.from(account.data[0], 'base64');
  console.log(`clock slot ${data.readBigUInt64LE(0)}, ${account.lamports} lamports`);
}
```

```rust Rust theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
use orbitflare_sdk::WsClientBuilder;

#[tokio::main]
async fn main() -> orbitflare_sdk::Result<()> {
    // The SDK pings every 10 s and resubscribes on its own after a reconnect.
    let ws = WsClientBuilder::new()
        .url("wss://fra.rpc.orbitflare.com")
        .build()
        .await?;

    // Notifies only when the account changes. The Clock sysvar changes every slot.
    let mut sub = ws
        .account_subscribe("SysvarC1ock11111111111111111111111111111111", "confirmed")
        .await?;

    // Each update is the account's full new state: lamports, owner and base64 data.
    while let Some(account) = sub.next().await {
        println!(
            "{} lamports, data {}",
            account["lamports"], account["data"][0]
        );
    }
    Ok(())
}
```

```go Go theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
package main

import (
	"context"
	"fmt"
	"log"

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

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

	// The SDK pings every 10 s and resubscribes on its own after a reconnect.
	client, err := ws.NewBuilder().URL("wss://fra.rpc.orbitflare.com").Build(ctx)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// Notifies only when the account changes. The Clock sysvar changes every slot.
	sub, err := client.AccountSubscribe(ctx, "SysvarC1ock11111111111111111111111111111111", "confirmed")
	if err != nil {
		log.Fatal(err)
	}
	defer sub.Unsubscribe()

	// Each update is the account's full new state: lamports, owner and base64 data.
	for {
		account, ok := sub.Next(ctx)
		if !ok {
			return
		}
		fmt.Println(string(account))
	}
}
```

Обновления приходят только при изменении аккаунта. Тихий аккаунт означает тихую подписку, а не сломанную. Если ничего не приходит, см. [отладку WebSocket](/ru/troubleshooting/websocket).

## Дождитесь подтверждения транзакции

Передайте подпись в командной строке. Программа опрашивает `getSignatureStatuses`, пока транзакция не будет подтверждена, не завершится ошибкой или не станет ясно, что она уже не появится.

```bash curl theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl -s https://fra.rpc.orbitflare.com \
  -H "x-api-key: $ORBITFLARE_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["SIGNATURE"],{"searchTransactionHistory":true}]}'
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { RpcClientBuilder } from '@orbitflare/sdk';

const signature = process.argv[2];
const rpc = new RpcClientBuilder().url('https://fra.rpc.orbitflare.com').build();

// 120 polls 500 ms apart is 60 s, well past the ~30 s a blockhash stays valid.
for (let attempt = 0; attempt < 120; attempt++) {
  // searchTransactionHistory also finds signatures older than the node's recent status cache.
  const { value } = await rpc.request('getSignatureStatuses', [
    [signature],
    { searchTransactionHistory: true },
  ]);
  const status = value[0];
  // err set means it landed and failed. The fee was still charged.
  if (status?.err) {
    console.log('landed but failed:', JSON.stringify(status.err));
    process.exit(1);
  }
  if (status?.confirmationStatus === 'confirmed' || status?.confirmationStatus === 'finalized') {
    console.log(`${status.confirmationStatus} in slot ${status.slot}`);
    process.exit(0);
  }
  // null means the cluster has not seen it yet, so keep polling.
  await new Promise((r) => setTimeout(r, 500));
}
console.log('not found after 60s: the transaction was dropped or its blockhash expired');
```

```rust Rust theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
use std::time::Duration;

use orbitflare_sdk::RpcClientBuilder;
use serde_json::json;

#[tokio::main]
async fn main() -> orbitflare_sdk::Result<()> {
    let signature = std::env::args()
        .nth(1)
        .expect("usage: wait_for_signature <SIGNATURE>");
    let rpc = RpcClientBuilder::new()
        .url("https://fra.rpc.orbitflare.com")
        .build()?;

    // 120 polls 500 ms apart is 60 s, well past the ~30 s a blockhash stays valid.
    for _ in 0..120 {
        // searchTransactionHistory also finds signatures older than the node's recent status cache.
        let res = rpc
            .request(
                "getSignatureStatuses",
                json!([[signature], {"searchTransactionHistory": true}]),
            )
            .await?;
        let status = &res["value"][0];
        // err set means it landed and failed. The fee was still charged.
        if !status["err"].is_null() {
            println!("landed but failed: {}", status["err"]);
            std::process::exit(1);
        }
        if let Some(level @ ("confirmed" | "finalized")) = status["confirmationStatus"].as_str() {
            println!("{level} in slot {}", status["slot"]);
            return Ok(());
        }
        // null means the cluster has not seen it yet, so keep polling.
        tokio::time::sleep(Duration::from_millis(500)).await;
    }
    println!("not found after 60s: the transaction was dropped or its blockhash expired");
    Ok(())
}
```

```go Go theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
package main

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

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

type statuses struct {
	Value []*struct {
		Slot               uint64          `json:"slot"`
		Err                json.RawMessage `json:"err"`
		ConfirmationStatus string          `json:"confirmationStatus"`
	} `json:"value"`
}

func main() {
	signature := os.Args[1]
	client, err := rpc.NewBuilder().URL("https://fra.rpc.orbitflare.com").Build()
	if err != nil {
		log.Fatal(err)
	}

	// 120 polls 500 ms apart is 60 s, well past the ~30 s a blockhash stays valid.
	for range 120 {
		// searchTransactionHistory also finds signatures older than the node's recent status cache.
		raw, err := client.Request(context.Background(), "getSignatureStatuses",
			[]any{[]string{signature}, map[string]bool{"searchTransactionHistory": true}})
		if err != nil {
			log.Fatal(err)
		}
		var res statuses
		if err := json.Unmarshal(raw, &res); err != nil {
			log.Fatal(err)
		}
		if s := res.Value[0]; s != nil {
			// err set means it landed and failed. The fee was still charged.
			if string(s.Err) != "null" && len(s.Err) > 0 {
				fmt.Println("landed but failed:", string(s.Err))
				os.Exit(1)
			}
			if s.ConfirmationStatus == "confirmed" || s.ConfirmationStatus == "finalized" {
				fmt.Printf("%s in slot %d\n", s.ConfirmationStatus, s.Slot)
				return
			}
		}
		// null means the cluster has not seen it yet, so keep polling.
		time.Sleep(500 * time.Millisecond)
	}
	fmt.Println("not found after 60s: the transaction was dropped or its blockhash expired")
}
```

Опрос с `searchTransactionHistory` работает для любой подписи, старой или новой. `signatureSubscribe` знает только о недавних транзакциях, поэтому подписка на транзакцию, подтверждённую несколько минут назад, никогда не пришлёт уведомление. Статус `null` означает, что кластер не видел транзакцию. Что с этим делать, описано в разделе [отладка транзакций](/ru/troubleshooting/transactions).

## Поток транзакций программы

Программа использует Jetstream v2 и выводит каждую транзакцию, затрагивающую pump.fun, вместе с плательщиком комиссии и ценой вычислительной единицы из полей обогащения.

```bash grpcurl theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
grpcurl -plaintext -import-path . -proto jetstream_v2.proto \
  -d '{"add_filters":{"filters":[{"filter_id":"pump","filter":{"account_include":["6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"],"include_enrichment":true}}]}}' \
  fra.jetstream.orbitflare.com:80 jetstream.v2.JetstreamV2/SubscribeTransactions
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import bs58 from 'bs58';
import { JetstreamClientBuilder, TransactionFilter } from '@orbitflare/sdk/jetstream/v2';

const PUMP_FUN = '6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P';

// In API Key Only mode the SDK sends ORBITFLARE_LICENSE_KEY as x-token.
const jetstream = new JetstreamClientBuilder().url('http://fra.jetstream.orbitflare.com').build();

// Matches every transaction that mentions pump.fun. Enrichment adds the fee payer and CU price.
const filter = new TransactionFilter().accountInclude([PUMP_FUN]).includeEnrichment(true).withId('pump');

for await (const update of jetstream.subscribeTransactions([filter])) {
  // Other updates are filter validations, heartbeats and pongs.
  const tx = update.transaction?.transaction;
  if (!tx) continue;
  // compute_unit_price is micro-lamports per CU, not the total priority fee.
  console.log(
    `${tx.slot} ${bs58.encode(tx.signature)} payer=${bs58.encode(tx.feePayer)} cu_price=${tx.computeUnitPrice}`,
  );
}
```

```rust Rust theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
use orbitflare_sdk::jetstream::v2::{JetstreamClientBuilder, TransactionFilter};
use orbitflare_sdk::proto::jetstream::v2::subscribe_transactions_response::Payload;

const PUMP_FUN: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P";

#[tokio::main]
async fn main() -> orbitflare_sdk::Result<()> {
    // In API Key Only mode the SDK sends ORBITFLARE_LICENSE_KEY as x-token.
    let jetstream = JetstreamClientBuilder::new()
        .url("http://fra.jetstream.orbitflare.com")
        .build()?;

    // Matches every transaction that mentions pump.fun. Enrichment adds the fee payer and CU price.
    let filter = TransactionFilter::new()
        .account_include([PUMP_FUN])
        .include_enrichment(true)
        .with_id("pump");

    let mut stream = jetstream.subscribe_transactions(vec![filter]);

    while let Some(update) = stream.next().await {
        // Other updates are filter validations, heartbeats and pongs.
        if let Some(Payload::Transaction(ft)) = update?.payload
            && let Some(tx) = ft.transaction
        {
            // compute_unit_price is micro-lamports per CU, not the total priority fee.
            println!(
                "{} {} payer={} cu_price={}",
                tx.slot,
                bs58::encode(&tx.signature).into_string(),
                bs58::encode(&tx.fee_payer).into_string(),
                tx.compute_unit_price
            );
        }
    }
    Ok(())
}
```

```go Go theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/mr-tron/base58"
	jetstreamv2 "github.com/orbitflare/orbitflare-sdk-go/jetstream/v2"
)

const pumpFun = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"

func main() {
	// In API Key Only mode the SDK sends ORBITFLARE_LICENSE_KEY as x-token.
	client, err := jetstreamv2.NewBuilder().URL("http://fra.jetstream.orbitflare.com").Build()
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// Matches every transaction that mentions pump.fun. Enrichment adds the fee payer and CU price.
	filter := jetstreamv2.NewTransactionFilter().
		AccountInclude(pumpFun).
		IncludeEnrichment(true).
		WithID("pump")

	stream := client.SubscribeTransactions(filter)
	defer stream.Close()

	for {
		update, err := stream.Next(context.Background())
		if err != nil {
			log.Fatal(err)
		}
		// Other updates are filter validations, heartbeats and pongs.
		tx := update.GetTransaction().GetTransaction()
		if tx == nil {
			continue
		}
		// compute_unit_price is micro-lamports per CU, not the total priority fee.
		fmt.Printf("%d %s payer=%s cu_price=%d\n",
			tx.GetSlot(),
			base58.Encode(tx.GetSignature()),
			base58.Encode(tx.GetFeePayer()),
			tx.GetComputeUnitPrice())
	}
}
```

Для версии с `grpcurl` нужен файл `jetstream_v2.proto` из [репозитория SDK](https://github.com/orbitflare/orbitflare-sdk-ts/tree/main/protos) в текущей папке. Jetstream фильтрует только транзакции. Для отслеживания состояния аккаунтов используйте [Yellowstone](/ru/data-streaming/yellowstone-account-monitoring).

## Прочитайте последние транзакции кошелька

Программа возвращает 10 последних транзакций адреса с помощью `getTransactionsForAddress`.

```bash curl theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
curl -s https://fra.rpc.orbitflare.com \
  -H "x-api-key: $ORBITFLARE_LICENSE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"getTransactionsForAddress","params":["WALLET",{"limit":10}]}'
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
import { RpcClientBuilder } from '@orbitflare/sdk';

type Entry = { signature: string; slot: number; blockTime: number; err: unknown };

const wallet = process.argv[2];
const rpc = new RpcClientBuilder().url('https://fra.rpc.orbitflare.com').build();

// Returns signatures by default. Ask for "full" with maxSupportedTransactionVersion 1 to get bodies.
const { data } = await rpc.getTransactionsForAddress(wallet, { limit: 10 });

// Newest first. Pass paginationToken from the response to get the next page.
for (const tx of data as Entry[]) {
  const when = new Date(tx.blockTime * 1000).toISOString();
  console.log(when, tx.slot, tx.signature, tx.err ? 'failed' : 'ok');
}
```

```rust Rust theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
use orbitflare_sdk::{GetTransactionsOptions, RpcClientBuilder};

#[tokio::main]
async fn main() -> orbitflare_sdk::Result<()> {
    let wallet = std::env::args().nth(1).expect("usage: history <WALLET>");
    let rpc = RpcClientBuilder::new()
        .url("https://fra.rpc.orbitflare.com")
        .build()?;

    let options = GetTransactionsOptions {
        limit: Some(10),
        ..Default::default()
    };
    // Returns signatures by default. Ask for "full" with maxSupportedTransactionVersion 1 to get bodies.
    let page = rpc.get_transactions_for_address(&wallet, options).await?;

    // Newest first. Pass paginationToken from the response to get the next page.
    for tx in page.data {
        let status = if tx["err"].is_null() { "ok" } else { "failed" };
        println!(
            "{} {} {}",
            tx["slot"],
            tx["signature"].as_str().unwrap_or(""),
            status
        );
    }
    Ok(())
}
```

```go Go theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
package main

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

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

type entry struct {
	Signature string          `json:"signature"`
	Slot      uint64          `json:"slot"`
	Err       json.RawMessage `json:"err"`
}

func main() {
	wallet := os.Args[1]
	client, err := rpc.NewBuilder().URL("https://fra.rpc.orbitflare.com").Build()
	if err != nil {
		log.Fatal(err)
	}

	limit := uint32(10)
	// Returns signatures by default. Ask for "full" with maxSupportedTransactionVersion 1 to get bodies.
	page, err := client.GetTransactionsForAddress(context.Background(), wallet, &rpc.GetTransactionsOptions{Limit: &limit})
	if err != nil {
		log.Fatal(err)
	}

	// Newest first. Pass paginationToken from the response to get the next page.
	for _, raw := range page.Data {
		var tx entry
		if err := json.Unmarshal(raw, &tx); err != nil {
			log.Fatal(err)
		}
		status := "ok"
		if string(tx.Err) != "null" {
			status = "failed"
		}
		fmt.Println(tx.Slot, tx.Signature, status)
	}
}
```

Каждая запись содержит `signature`, `slot`, `blockTime`, `err` и `confirmationStatus`. Чтобы получить следующую страницу, передайте возвращённый `paginationToken`. Для получения полных транзакций задайте `transactionDetails` равным `"full"` вместе с `"maxSupportedTransactionVersion": 1`. Без этого транзакции версии 1 молча пропускаются. Фильтры и сортировка описаны на странице [getTransactionsForAddress](/ru/fetching-data/getTransactionsForAddress).

## Другие примеры

> **[Справочник ошибок](/ru/rpc/error-codes)**
>
> Все ошибки, которые возвращает OrbitFlare, и что с ними делать.

> **[Отладка транзакций](/ru/troubleshooting/transactions)**
>
> Почему транзакция завершилась ошибкой, была отброшена или так и не попала в блок.

> **[Отправка через Apex](/ru/apex/quickstart)**
>
> Доставляйте транзакции быстрее с OrbitFlare Apex.

> **[Справочник SDK](/ru/sdk/overview)**
>
> Все клиенты, параметры билдеров и методы SDK.


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