Структура комиссий

Краткое резюме

Итоговая комиссия = базовая комиссия (5 000 lamport/подпись, 50% сжигается) + комиссия за приоритизацию (CU_price * CU_limit / 1M lamport, 100% validator'у). Взимается независимо от того, выполнена транзакция успешно или нет.

Алгоритм расчёта комиссии

Итоговая комиссия за транзакцию вычисляется функцией calculate_fee_details:

Total fee formula
total_fee = base_fee + prioritization_fee

На входе — сообщение транзакции, текущее значение lamports_per_signature (в настоящее время 5 000) и комиссия за приоритизацию, полученная из инструкций вычислительного бюджета. Итоговая комиссия списывается с плательщика комиссии до начала выполнения. Если транзакция завершается с ошибкой, комиссия всё равно взимается.

Базовая комиссия

Базовая комиссия покрывает стоимость верификации подписей Ed25519, а также верификации подписей прекомпилированными программами. С каждой подписи в транзакции взимается комиссия, включая подписи, верифицированные прекомпилированными программами Ed25519, Secp256k1 и Secp256r1.

Функция calculate_signature_fee вычисляет:

Base fee formula
signature_count = num_transaction_signatures
+ num_ed25519_signatures
+ num_secp256k1_signatures
+ num_secp256r1_signatures (if feature enabled)
base_fee = signature_count * lamports_per_signature
  • num_transaction_signatures: количество подписей Ed25519 в транзакции (как правило, по одной на каждого подписанта).
  • num_ed25519_signatures, num_secp256k1_signatures, num_secp256r1_signatures: подписи, верифицированные соответствующими прекомпилированными программами.
  • lamports_per_signature: в настоящее время 5 000 lamport.

Распределение комиссий

Базовая комиссия и комиссия за приоритизацию распределяются по-разному. Функция calculate_reward_and_burn_fee_details в среде выполнения вычисляет:

Fee distribution
burn_amount = transaction_fee * 50 / 100
validator_share = (transaction_fee - burn_amount) + priority_fee
  • Базовая комиссия (transaction_fee): 50% сжигается (изымается из обращения), а 50% направляется validator'у, создающему блок. Процент сжигания задаётся константой DEFAULT_BURN_PERCENT = 50.
  • Комиссия за приоритизацию: 100% направляется validator'у (ничего не сжигается), согласно SIMD-0096.

Итоговое вознаграждение validator'а за транзакцию составляет:

validator_reward = (base_fee / 2) + prioritization_fee

Комиссия за приоритизацию

Комиссия за приоритизацию — это необязательная комиссия, повышающая приоритет планирования транзакции. Вычислительная единица (CU) — это единица измерения вычислений, которую среда выполнения отслеживает в ходе выполнения транзакции. Каждая операция (арифметическая, обращение к памяти, системный вызов) стоит фиксированное количество CU. Задать комиссию за приоритизацию можно, включив инструкции вычислительного бюджета в свою транзакцию. (Подробнее см. руководство «Как использовать комиссии за приоритет».)

Формула комиссии за приоритизацию

Функция get_prioritization_fee вычисляет:

Prioritization fee formula
micro_lamport_fee = compute_unit_price * compute_unit_limit
prioritization_fee = ceil(micro_lamport_fee / 1,000,000)
ПеременнаяОписаниеПо умолчанию
compute_unit_priceМикро-lamport за CU, задаётся через SetComputeUnitPrice0 микро-lamport
compute_unit_limitМаксимальное количество CU, которое может использовать транзакция; задаётся через SetComputeUnitLimitСумма значений по умолчанию для каждой инструкции
1 000 000Коэффициент пересчёта микро-lamport в lamport (MICRO_LAMPORTS_PER_LAMPORT)--

Транзакции v1 задают комиссию напрямую

Формула цена × лимит применяется к устаревшим транзакциям и транзакциям v0. В формате v1 комиссия за приоритизацию задаётся как абсолютная итоговая сумма в lamport непосредственно в конфигурации сообщения — без цены за CU, без умножения и без округления.

Лимит вычислительных единиц

Лимит вычислительных единиц — это максимальное количество CU, которое может использовать транзакция. Если инструкция SetComputeUnitLimit не включена, значение по умолчанию рассчитывается исходя из количества и типа инструкций (200 000 CU для каждой встроенной инструкции, 3 000 — для каждой нативной). Подробнее о расчёте по умолчанию см. в разделе «Вычислительный бюджет».

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

Цена вычислительной единицы

Цена вычислительной единицы — это необязательная сумма в микро-lamport, уплачиваемая за каждую запрошенную CU. Цена CU напрямую определяет комиссию за приоритизацию. Чтобы задать цену CU, включите инструкцию SetComputeUnitPrice в свою транзакцию.

Цена CU по умолчанию равна 0, поэтому комиссия за приоритизацию по умолчанию также равна 0.

Актуальные оценки цены CU в реальном времени предоставляют указанные ниже провайдеры API комиссий за приоритет.

ПровайдерAPI комиссий за приоритет
HeliusДокументация
QuickNodeДокументация
TritonДокументация

Приоритет планирования транзакций

Планировщик ранжирует транзакции с помощью функции calculate_priority_and_cost:

Transaction priority formula
Priority = reward * 1,000,000 / (cost + 1)
  • reward — доход validator'а от комиссий = комиссия за приоритизацию + не сожжённая часть базовой комиссии. Вычисляется функцией calculate_reward_for_transaction.
  • cost — расчётная стоимость CU по оценке планировщика (estimated CU cost): стоимость подписей + стоимость блокировок на запись + стоимость instruction data + стоимость выполнения программы + стоимость загрузки данных аккаунтов. Подробнее см. в разделе «Вычислительный бюджет».
  • 1 000 000 — множитель для сохранения точности, поскольку стоимость нередко превышает вознаграждение в абсолютных значениях lamport.
  • +1 — защита от деления на ноль.

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

Пример: установка лимита CU и цены CU

В приведённых ниже примерах показано, как задать лимит CU и цену CU для транзакции с помощью SDK Solana.

SDKСсылка на исходный код
@solana/web3.js (Typescript)ComputeBudgetProgram
solana-sdk (Rust)ComputeBudgetInstruction
const limitInstruction = ComputeBudgetProgram.setComputeUnitLimit({
units: 300_000
});
const priceInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 1
});
import {
LAMPORTS_PER_SOL,
SystemProgram,
Transaction,
Keypair,
Connection,
ComputeBudgetProgram,
sendAndConfirmTransaction
} from "@solana/web3.js";
const connection = new Connection("http://localhost:8899", "confirmed");
const sender = Keypair.generate();
const recipient = new Keypair();
const airdropSignature = await connection.requestAirdrop(
sender.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature, "confirmed");
// Create compute budget instructions
const limitInstruction = ComputeBudgetProgram.setComputeUnitLimit({
units: 300_000
});
const priceInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 1
});
const transferInstruction = SystemProgram.transfer({
fromPubkey: sender.publicKey,
toPubkey: recipient.publicKey,
lamports: 0.01 * LAMPORTS_PER_SOL
});
// Add the compute budget and transfer instructions to a new transaction
const transaction = new Transaction()
.add(limitInstruction)
.add(priceInstruction)
.add(transferInstruction);
const signature = await sendAndConfirmTransaction(connection, transaction, [
sender
]);
console.log("Transaction Signature:", signature);
Console
Click to execute the code.

Is this page helpful?