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

# Отладка транзакций

> Как выяснить, почему транзакция Solana завершилась ошибкой, была отклонена до отправки или так и не попала в блок: ошибки симуляции и логи программ, истечение blockhash, ошибки кодирования и приоритетные комиссии.

Транзакция оказывается в одном из трёх состояний, и для каждого нужно своё решение. Прежде чем что-либо менять, выясните, в каком именно.

## Шаг 1: выясните, что произошло

Запросите у кластера статус подписи. `searchTransactionHistory` позволяет найти транзакцию любого возраста.

```bash 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}]}'
```

| Результат | Что это значит | Куда перейти |
| - | - | - |
| `"err": null` и `"confirmationStatus": "confirmed"` или `"finalized"` | Транзакция попала в блок и выполнилась успешно | Отлаживать нечего. Если результат всё равно не тот, инструкции сделали не то, что вы ожидали |
| `"err": { ... }` | Транзакция попала в блок и **завершилась ошибкой**. Комиссия списана, больше ничего не изменилось | [Попала в блок, но завершилась ошибкой](#попала-в-блок-но-завершилась-ошибкой) |
| `null` | Кластер никогда её не видел | [Так и не попала в блок](#так-и-не-попала-в-блок) |

Если ошибку вернул сам `sendTransaction`, подписи для проверки нет. Транзакция была отклонена до отправки. Перейдите к разделу [Отклонена до отправки](#отклонена-до-отправки).

Чтобы проверять подписи из кода, используйте [рецепт ожидания подтверждения](/ru/recipes#дождитесь-подтверждения-транзакции).

## Отклонена до отправки

По умолчанию `sendTransaction` сначала симулирует транзакцию и отказывается её отправлять, если симуляция не прошла. Вы получаете ошибку `-32002` с причиной и логами программ:

```json theme={"theme":{"light":"github-light","dark":"github-dark-default"}}
{
  "code": -32002,
  "message": "Transaction simulation failed: Error processing Instruction 0: custom program error: 0x1",
  "data": {
    "err": { "InstructionError": [0, { "Custom": 1 }] },
    "logs": [
      "Program 11111111111111111111111111111111 invoke [1]",
      "Transfer: insufficient lamports 60773561, need 1000000000000",
      "Program 11111111111111111111111111111111 failed: custom program error: 0x1"
    ],
    "unitsConsumed": 150
  }
}
```

Читайте её в таком порядке:

1. **`data.logs`** обычно простыми словами говорит, что пошло не так. Здесь у плательщика 0,06 SOL, а он пытался отправить 1000.
2. **`data.err`** показывает, какая инструкция завершилась ошибкой. `InstructionError: [0, ...]` — это первая инструкция, счёт идёт с нуля.
3. **Собственный код ошибки** принадлежит программе, которая завершилась ошибкой, а не Solana. `Custom: 1` от System Program означает «недостаточно средств», но `Custom: 1` от другой программы означает что-то другое. Найдите номер в списке ошибок этой программы (в её IDL или исходном коде). В сообщении код указан в шестнадцатеричном виде, поэтому `0x1770` — это `6000`, первая собственная ошибка в программе на Anchor.

Отклонение ничего не стоит. Транзакция так и не покинула RPC-узел.

### Частые причины отклонения

| Ошибка | Причина | Решение |
| - | - | - |
| `Transaction simulation failed: Blockhash not found` | Blockhash истёк, взят из другого кластера или выдуман | Получите новый через `getLatestBlockhash` непосредственно перед подписью |
| `Transaction simulation failed: Attempt to debit an account but found no record of a prior credit` | У плательщика комиссии никогда не было SOL в этом кластере | Пополните плательщика комиссии. В devnet проверьте, что вы не используете кошелёк из mainnet без баланса в devnet |
| `custom program error: 0x1` от System Program | Не хватает лампортов на перевод и комиссии | Проверьте баланс с учётом ренты за все аккаунты, которые создаёт транзакция |
| `exceeded CUs meter at BPF instruction` | Слишком низкий лимит вычислительных единиц | Выполните симуляцию, посмотрите `unitsConsumed` и установите лимит примерно на 10% выше |
| `invalid base58 encoding: InvalidCharacter` | Вы отправили байты в base64, не указав кодировку. `sendTransaction` по умолчанию ожидает base58 | Передайте `{"encoding": "base64"}` вторым параметром |
| `failed to deserialize ... VersionedTransaction: io error: failed to fill whole buffer` | Байты не являются полной транзакцией: они обрезаны, закодированы дважды или не подписаны | Сериализуйте подписанную транзакцию один раз и закодируйте один раз |

## Отладка с помощью simulateTransaction

Если нужны логи без отправки, выполните симуляцию. Два параметра делают её гораздо полезнее при отладке:

```bash 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":"simulateTransaction","params":["BASE64_TX",{"encoding":"base64","replaceRecentBlockhash":true,"sigVerify":false}]}'
```

* **`replaceRecentBlockhash: true`** подставляет актуальный blockhash, поэтому одну и ту же транзакцию можно симулировать и после истечения её blockhash. Нельзя сочетать с `sigVerify: true`.
* **`sigVerify: false`** пропускает проверку подписей, поэтому можно симулировать неподписанную или частично подписанную транзакцию.

Результат содержит `err`, `logs` и `unitsConsumed`, даже если симуляция прошла успешно. По `unitsConsumed` задайте точный лимит вычислительных единиц: чем плотнее лимит, тем эффективнее работает приоритетная комиссия, которую вы платите за единицу.

> [!WARNING]
>
> Не используйте `skipPreflight: true` во время отладки. Транзакция отправляется без симуляции, поэтому сломанная транзакция попадает в блок, завершается ошибкой и списывает комиссию, а ошибка видна только позже в `getSignatureStatuses`. Включайте этот параметр, когда транзакция уже точно работает и вам нужна скорость.

## Попала в блок, но завершилась ошибкой

Транзакция в блокчейне, комиссия списана, и ни одно её изменение не применилось. Получите полную запись:

```bash 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":"getTransaction","params":["SIGNATURE",{"encoding":"json","maxSupportedTransactionVersion":1}]}'
```

`meta.err` имеет тот же формат, что и ошибка симуляции, а `meta.logMessages` содержит логи программ. Читайте их так, как описано в разделе [Отклонена до отправки](#отклонена-до-отправки).

Транзакция, которая успешно прошла симуляцию, всё равно может завершиться ошибкой в блокчейне, потому что за это время изменилось состояние. Обычные причины — проскальзывание при обмене, аккаунт, который кто-то другой закрыл или изменил, или уже использованный nonce. Повторите симуляцию на текущем состоянии, чтобы убедиться, а затем заложите запас: более широкое проскальзывание, свежую котировку или повтор с пересборкой транзакции.

## Так и не попала в блок

`getSignatureStatuses` возвращает `null`: RPC-узел принял транзакцию, но ни один лидер её не включил. Ничего не списано.

### Blockhash истёк

Транзакция действительна только **150 блоков** после своего blockhash. При длительности слота около 215 мс это примерно 30–35 секунд. После этого транзакция уже никогда не попадёт в блок, поэтому перестаньте ждать и соберите её заново.

Проверьте, можно ли ещё использовать blockhash:

```bash 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":"isBlockhashValid","params":["BLOCKHASH",{"commitment":"processed"}]}'
```

Сохраняйте `lastValidBlockHeight`, который возвращает `getLatestBlockhash`, вместе с blockhash. Сравнивайте его с `getBlockHeight`: пока высота блока меньше, продолжайте повторно отправлять ту же подписанную транзакцию. Когда она стала больше, транзакция потеряна.

### Слишком низкая приоритетная комиссия

Когда блоки загружены, лидеры заполняют их транзакциями, которые платят больше всего за вычислительную единицу. Проверьте, что недавно попало в блоки для аккаунтов, с которыми вы работаете:

```bash 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":"getRecentPrioritizationFees","params":[["ACCOUNT_YOUR_TX_WRITES"]]}'
```

Выберите значение из последних результатов, задайте его через `SetComputeUnitPrice` и добавьте к нему точный `SetComputeUnitLimit` по результатам симуляции.

### Транзакция потерялась по пути

Отправленная транзакция может потеряться между RPC-узлом и лидером, особенно при перегрузке сети. Повторно отправляйте те же подписанные байты каждые пару секунд, пока транзакция не подтвердится или не истечёт blockhash. Повторная отправка безопасна: транзакция с одной и той же подписью может попасть в блок только один раз.

Если важно, чтобы транзакция попала в блок быстро, отправляйте её через [Apex](/ru/apex/overview). Apex отправляет каждую транзакцию ближайшим лидерам сразу несколькими путями и повторяет попытки, пока она не попадёт в блок или не истечёт. Собственные ошибки Apex перечислены в разделе [Ошибки и лимиты Apex](/ru/apex/errors-and-rate-limits).

## Если проблема не решена

Соберите подпись (или транзакцию в base64, если подпись так и не была получена), полный текст ошибки вместе с `data.logs`, регион, через который вы отправляли, и было ли включено preflight. Затем задайте вопрос в [Discord](https://discord.gg/orbitflare).


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