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

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

> OrbitFlare Apex принимает транзакции Solana v1 (SIMD-0385) размером до 4096 байт через sendTransaction, HTTP-маршруты отправки и QUIC. Узнайте, как их сериализовать.

## Ограничения размера по форматам

Транзакция v1 использует формат сообщения, введённый в SIMD-0385. Главное изменение для отправителей касается размера: транзакция v1 может занимать до **4096 байт**, тогда как для транзакций legacy и v0 остаётся предел 1232.

| Формат | Максимальный размер | Где принимается                                               |
| ------ | ------------------- | ------------------------------------------------------------- |
| Legacy | 1232 байта          | Все маршруты                                                  |
| v0     | 1232 байта          | Все маршруты                                                  |
| **v1** | **4096 байт**       | `sendTransaction`, `/send`, `/send-bin`, `/send-batch` и QUIC |

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

<Note>
  Участники [бандла](/ru/apex/bundles) ограничены 1232 байтами каждый независимо от формата.
</Note>

Транзакция legacy или v0 больше 1232 байт, как и любая транзакция больше 4096 байт, отклоняется как слишком большая. См. [Ошибки и лимиты запросов](/ru/apex/errors-and-rate-limits).

## Сериализуйте v1 каноническим кодировщиком

Транзакция v1 должна быть сериализована каноническим кодировщиком этого формата. В Rust это `wincode`. Прежний путь через bincode и serde даёт правильные байты для legacy и v0, но **неправильные байты для v1**, и транзакция будет отклонена как некорректная.

Клиент на Rust предоставляет правильный кодировщик как `apex_sender_client::serialize_transaction`. Для legacy и v0 он побайтово идентичен bincode, а для v1 даёт корректный результат. `ApexSenderClient::send_transaction` использует его внутри, поэтому вызывать его самостоятельно нужно, только когда вам нужны байты, например для HTTP-маршрутов.

В других языках используйте версию библиотеки Solana, которая реализует сериализацию SIMD-0385. Перед отправкой проверьте, что первый байт вашего сериализованного сообщения является префиксом версии v1.

## Правило чаевых то же самое

Транзакции v1 нужны те же [чаевые](/ru/apex/tips): один перевод SystemProgram верхнего уровня на опубликованный аккаунт для чаевых, оплаченный подписантом, не ниже минимума вашего тарифа.

Одно отличие касается вычислительного бюджета. В сообщении v1 он задаётся в собственном `TransactionConfig` сообщения, а не инструкциями программы ComputeBudget. Каждый лимит, не заданный в `TransactionConfig`, равен 0, включая размер данных загружаемых аккаунтов. В нашем тесте в mainnet 2026-09-19 транзакция v1, которая полагалась на инструкции ComputeBudget, попала в блок и завершилась в сети ошибкой `MaxLoadedAccountsDataSizeExceeded`.

Поэтому задавайте все три значения: лимит вычислительных единиц, лимит размера данных загружаемых аккаунтов и, для приоритета, комиссию. Приоритетная комиссия задаётся общей суммой в lamports, а не ценой за вычислительную единицу. С такой конфигурацией транзакции v1 размером 276, 741 и 1641 байт попали в блоки mainnet через Apex по QUIC. Транзакция в 1641 байт превышает предел legacy в 1232 байта.

## Пример на Rust

Эта программа собирает транзакцию v1 с полезной нагрузкой в стиле memo размером 3,000 байт, которая не поместилась бы в транзакцию legacy, и отправляет её по QUIC. В дополнение к [зависимостям из раздела Быстрый старт](/ru/apex/quickstart#зависимости-rust) ей нужна `solana-message = "=4.4.1"`.

```rust theme={null}
use std::str::FromStr;
use std::time::Duration;

use apex_sender_client::rpc::{RpcClient, SolanaRpc};
use apex_sender_client::{
    serialize_transaction, tip, tip_instruction, ApexSenderClient, Region, MIN_TIP_LAMPORTS,
};
use solana_instruction::{AccountMeta, Instruction};
use solana_keypair::read_keypair_file;
use solana_message::v1::{self, TransactionConfig};
use solana_message::VersionedMessage;
use solana_pubkey::Pubkey;
use solana_signer::Signer;
use solana_transaction::versioned::VersionedTransaction;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = std::env::var("APEX_API_KEY")?;
    let solana = SolanaRpc::new(
        std::env::var("SOLANA_RPC_URL").unwrap_or_else(|_| "https://api.mainnet-beta.solana.com".into()),
    );
    let payer = read_keypair_file(std::env::var("KEYPAIR_PATH").unwrap_or_else(|_| "payer.json".into()))?;
    let region = Region::Frankfurt;

    let client = ApexSenderClient::connect(region, &api_key).await?;
    let tip_accounts = RpcClient::new(region, &api_key).get_tip_accounts().await?;
    let tip_account = tip::pick_tip_account(&tip_accounts).ok_or("no tip accounts")?;

    // A payload far larger than a legacy transaction could carry.
    let memo = Instruction::new_with_bytes(
        Pubkey::from_str("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")?,
        "v".repeat(3_000).as_bytes(),
        vec![AccountMeta::new(payer.pubkey(), true)],
    );
    let instructions = [
        memo,
        tip_instruction(&payer.pubkey(), &tip_account, MIN_TIP_LAMPORTS),
    ];

    // v1 sets the compute budget in the message config. Every limit left unset is 0.
    let config = TransactionConfig::empty()
        .with_compute_unit_limit(400_000)
        .with_loaded_accounts_data_size_limit(1024 * 1024)
        .with_priority_fee(4_000); // total lamports, not a price per compute unit
    let message = v1::Message::try_compile_with_config(
        &payer.pubkey(),
        &instructions,
        solana.latest_blockhash().await?,
        config,
    )?;
    let tx = VersionedTransaction::try_new(VersionedMessage::V1(message), &[&payer])?;

    let wire = serialize_transaction(&tx)?;
    println!("v1 transaction: {} bytes, prefix {:#04x}", wire.len(), wire[0]);
    assert_eq!(wire[0], v1::V1_PREFIX);

    let signature = client.send_transaction(&tx).await?;
    println!("sent: {signature}");
    println!("slot: {:?}", solana.confirm(&signature.to_string(), Duration::from_secs(30)).await?);
    Ok(())
}
```

Отправка более 4096 байт завершается локальной ошибкой `Error::TooLarge` до того, как что-либо попадёт в сеть.

## Отправка v1 по HTTP

Когда у вас есть правильно сериализованные байты v1, HTTP-маршруты принимают их точно так же, как любую транзакцию: base64 для `sendTransaction` и `/send`, сырые байты для `/send-bin` и `/send-batch`.

```rust theme={null}
// `tx` is the v1 transaction from the example above.
use apex_sender_client::rpc::RpcClient;
use apex_sender_client::{serialize_transaction, Region};

let rpc = RpcClient::new(Region::Frankfurt, &api_key);
let wire = serialize_transaction(&tx)?;
let signature = rpc.send_transaction_binary(&wire, false, None).await?;
println!("accepted: {signature}");
```
