Абстракция комиссий

Каждая транзакция в Solana требует SOL для оплаты сетевых комиссий. Но пользователи, приходящие в ваше платёжное приложение, ожидают работать со стейблкоинами, а не управлять балансом второго токена. Абстракция комиссий устраняет это неудобство, позволяя другому участнику оплачивать комиссии.

В этом руководстве рассматриваются два уровня:

  1. Как работает спонсорство комиссий — базовый примитив Solana
  2. Абстракция комиссий в масштабе с Kora — готовый к использованию сервис абстракции комиссий

Как работает спонсорство комиссий

В транзакциях Solana есть назначенный плательщик комиссии — аккаунт, который оплачивает сетевую комиссию. По умолчанию это первый подписант. Но вы можете указать другой аккаунт в качестве плательщика комиссии, что позволяет третьей стороне ("спонсору") покрывать комиссии за отправителя.

И отправитель, и спонсор должны подписать транзакцию:

  • Отправитель подписывает для авторизации перевода своих токенов
  • Спонсор подписывает для подтверждения оплаты сетевой комиссии

Подробнее о ключевых понятиях платежей см. в разделе Как работают платежи в Solana.

Ниже приведены основные этапы процесса. Полный рабочий код смотрите в разделе Демо.

Создайте аккаунт спонсора

Сгенерируйте отдельный keypair для спонсора, который будет оплачивать комиссии за транзакции. Спонсору нужен SOL для оплаты комиссий, но не обязательно иметь токены, которые переводятся.

Создайте инструкцию перевода

Создайте инструкцию перевода токенов с отправителем в роли владельца. Отправитель владеет токенами и должен подписать перевод.

Sponsor Transaction Fee
const sponsor = (await generateKeypair()).signer;
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender, // Sender signs for the transfer
amount: 250_000n // adjusted for the mint's decimals
});

Отправка с использованием спонсора как плательщика комиссии

Используйте prepareAndSend вместе с authority (отправитель, который подписывает перевод) и feePayer (спонсор, который оплачивает комиссию). Оба должны подписать транзакцию.

Sponsor Transaction Fee
const sponsor = (await generateKeypair()).signer;
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender,
amount: 250_000n
});
const signature = await client.transaction.prepareAndSend({
authority: sender, // Signs the transfer instruction
feePayer: sponsor, // Pays the transaction fees
instructions: [transferInstruction],
version: 0
});

Создайте аккаунт спонсора

Сгенерируйте отдельный keypair для спонсора, который будет оплачивать комиссии за транзакции. Спонсору нужен SOL для оплаты комиссий, но не обязательно иметь токены, которые переводятся.

Создайте инструкцию перевода

Создайте инструкцию перевода токенов с отправителем в роли владельца. Отправитель владеет токенами и должен подписать перевод.

Отправка с использованием спонсора как плательщика комиссии

Используйте prepareAndSend вместе с authority (отправитель, который подписывает перевод) и feePayer (спонсор, который оплачивает комиссию). Оба должны подписать транзакцию.

Sponsor Transaction Fee
const sponsor = (await generateKeypair()).signer;

Демонстрация

Demo
// Generate keypairs for sender, recipient, and sponsor (fee payer)
const sender = (await generateKeypair()).signer;
const recipient = (await generateKeypair()).signer;
const sponsor = (await generateKeypair()).signer;
console.log("Sender Address:", sender.address);
console.log("Recipient Address:", recipient.address);
console.log("Sponsor Address (Fee Payer):", sponsor.address);
// Demo Setup: Create client, mint account, token accounts, and fund with initial tokens
const { client, mint } = await demoSetup(sender, recipient, sponsor);
console.log("\nMint Address:", mint.address);
// Derive the Associated Token Accounts addresses (ATAs) for sender and recipient
const [senderAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: sender.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
const [recipientAta] = await findAssociatedTokenPda({
mint: mint.address,
owner: recipient.address,
tokenProgram: TOKEN_2022_PROGRAM_ADDRESS
});
console.log("Sender Token Account:", senderAta.toString());
console.log("Recipient Token Account:", recipientAta.toString());
// =============================================================================
// Sponsored Token Payment Demo
// =============================================================================
// Create instruction to transfer tokens from sender to recipient
// Transferring 250,000 base units = 0.25 tokens (with 6 decimals)
const transferInstruction = getTransferInstruction({
source: senderAta,
destination: recipientAta,
authority: sender, // Pass signer, not just address
amount: 250_000n // 0.25 tokens
});
// Prepare and send transaction with sponsor as fee payer using @solana/client
// The sponsor pays transaction fees, sender signs for the transfer
const signature = await client.transaction.prepareAndSend({
authority: sender, // Sender signs the transfer instruction
feePayer: sponsor, // Sponsor pays the transaction fees (different account)
instructions: [transferInstruction],
version: 0
});
console.log("\n=== Sponsored Token Payment Complete ===");
console.log("Transaction Signature:", signature.toString());
// Fetch final token account balances using @solana/client SPL token helper
const splToken = client.splToken({
mint: mint.address,
tokenProgram: "auto"
});
const senderBalance = await splToken.fetchBalance(sender.address);
const recipientBalance = await splToken.fetchBalance(recipient.address);
console.log("\nSender Token Account Balance:", senderBalance);
console.log("Recipient Token Account Balance:", recipientBalance);
// Fetch transaction details
const transaction = await client.runtime.rpc
.getTransaction(signature, {
encoding: "jsonParsed",
maxSupportedTransactionVersion: 0
})
.send();
const feePayer = transaction?.transaction.message.accountKeys?.[0];
console.log("\nNote: The first account in accountKeys is always the fee payer");
console.log("Fee Payer Address:", feePayer);
// =============================================================================
// Demo Setup Helper Function
// =============================================================================
Console
Click to execute the code.

Когда вы создаёте токен аккаунт для конечного пользователя, он может закрыть его и вернуть SOL, использованный для rent. Рассмотрите возможность взимать плату с пользователей за создание аккаунта в стейблкоинах или заложите эту стоимость в экономику вашего продукта.

Абстракция комиссии в масштабе с Kora

Примитив fee payer мощный, но для построения полноценной production-системы без газа нужно больше: управление кошельками спонсоров, обработка конвертации токенов (чтобы пользователи могли "оплачивать" комиссии в USDC), лимитирование и контроль безопасности.

Kora берёт на себя эту сложность. Это JSON-RPC сервер, который обеспечивает абстракцию комиссии, чтобы пользователям не требовался SOL. Вы можете полностью спонсировать комиссии или принимать оплату комиссии в любом токене.

Разверните Kora одной командой:

cargo install kora-cli
kora --config path/to/kora.toml rpc start --signers-config path/to/signers.toml

Затем используйте клиент Kora для подписи и отправки транзакций:

pnpm add @solana/kora
import { KoraClient } from "@solana/kora";
const kora = new KoraClient({ rpcUrl: "https://your-kora-instance" });
const { signature } = await kora.signAndSendTransaction({
transaction: base64EncodedTransaction
});

Ресурсы по Kora

Is this page helpful?

Содержание

Редактировать страницу

Управляется

© 2026 Фонд Solana.
Все права защищены.
Подключиться