Каждая транзакция в Solana требует SOL для оплаты сетевых комиссий. Но пользователи, приходящие в ваше платёжное приложение, ожидают работать со стейблкоинами, а не управлять балансом второго токена. Абстракция комиссий устраняет это неудобство, позволяя другому участнику оплачивать комиссии.
В этом руководстве рассматриваются два уровня:
- Как работает спонсорство комиссий — базовый примитив Solana
- Абстракция комиссий в масштабе с Kora — готовый к использованию сервис абстракции комиссий
Как работает спонсорство комиссий
В транзакциях Solana есть назначенный плательщик комиссии — аккаунт, который оплачивает сетевую комиссию. По умолчанию это первый подписант. Но вы можете указать другой аккаунт в качестве плательщика комиссии, что позволяет третьей стороне ("спонсору") покрывать комиссии за отправителя.
И отправитель, и спонсор должны подписать транзакцию:
- Отправитель подписывает для авторизации перевода своих токенов
- Спонсор подписывает для подтверждения оплаты сетевой комиссии
Подробнее о ключевых понятиях платежей см. в разделе Как работают платежи в Solana.
Ниже приведены основные этапы процесса. Полный рабочий код смотрите в разделе Демо.
Создайте аккаунт спонсора
Сгенерируйте отдельный keypair для спонсора, который будет оплачивать комиссии за транзакции. Спонсору нужен SOL для оплаты комиссий, но не обязательно иметь токены, которые переводятся.
Создайте инструкцию перевода
Создайте инструкцию перевода токенов с отправителем в роли владельца. Отправитель владеет токенами и должен подписать перевод.
const sponsor = (await generateKeypair()).signer;const transferInstruction = getTransferInstruction({source: senderAta,destination: recipientAta,authority: sender, // Sender signs for the transferamount: 250_000n // adjusted for the mint's decimals});
Отправка с использованием спонсора как плательщика комиссии
Используйте prepareAndSend вместе с authority (отправитель, который
подписывает перевод) и feePayer (спонсор, который оплачивает комиссию). Оба
должны подписать транзакцию.
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 instructionfeePayer: sponsor, // Pays the transaction feesinstructions: [transferInstruction],version: 0});
Демонстрация
// 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 tokensconst { client, mint } = await demoSetup(sender, recipient, sponsor);console.log("\nMint Address:", mint.address);// Derive the Associated Token Accounts addresses (ATAs) for sender and recipientconst [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 addressamount: 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 transferconst signature = await client.transaction.prepareAndSend({authority: sender, // Sender signs the transfer instructionfeePayer: 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 helperconst 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 detailsconst 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// =============================================================================
Когда вы создаёте токен аккаунт для конечного пользователя, он может закрыть его и вернуть SOL, использованный для rent. Рассмотрите возможность взимать плату с пользователей за создание аккаунта в стейблкоинах или заложите эту стоимость в экономику вашего продукта.
Абстракция комиссии в масштабе с Kora
Примитив fee payer мощный, но для построения полноценной production-системы без газа нужно больше: управление кошельками спонсоров, обработка конвертации токенов (чтобы пользователи могли "оплачивать" комиссии в USDC), лимитирование и контроль безопасности.
Kora берёт на себя эту сложность. Это JSON-RPC сервер, который обеспечивает абстракцию комиссии, чтобы пользователям не требовался SOL. Вы можете полностью спонсировать комиссии или принимать оплату комиссии в любом токене.
Разверните Kora одной командой:
cargo install kora-clikora --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?