Кожна транзакція в Solana потребує SOL для оплати мережевих комісій. Але користувачі, які приходять до вашого платіжного застосунку, очікують здійснювати транзакції у стейблкоїнах — а не керувати балансом другого токена. Абстракція комісій усуває це тертя, дозволяючи комусь іншому оплачувати комісії.
Цей посібник охоплює два рівні:
- Як працює спонсорство комісій — базовий примітив Solana
- Абстракція комісій у масштабі з Kora — готовий до продакшену сервіс абстракції комісій
Як працює спонсорство комісій
Транзакції Solana мають визначеного платника комісій — обліковий запис, який оплачує мережеву комісію. За замовчуванням це перший підписант. Але ви можете вказати інший обліковий запис як платника комісій, дозволяючи третій стороні ("спонсору") покривати комісії від імені відправника.
І відправник, і спонсор повинні підписати транзакцію:
- Відправник підписує для авторизації переказу своїх токенів
- Спонсор підписує для авторизації оплати мережевої комісії
Дивіться Як працюють платежі в Solana для основних концепцій платежів.
Кроки нижче показують основний потік. Дивіться Демо для повного робочого коду.
Створіть обліковий запис спонсора
Згенеруйте окрему пару ключів для спонсора, який оплачуватиме комісії транзакцій. Спонсору потрібен SOL для комісій, але йому не потрібно тримати токени, які переказуються.
Створіть інструкцію переказу
Створіть інструкцію переказу токенів з відправником як авторитетом. Відправник володіє токенами і повинен підписати переказ.
Надсилання зі спонсором як платником комісії
Створіть клієнта зі спонсором як платником комісії за допомогою
payer(sponsor), потім викличте client.sendTransaction. Спонсор підписує
транзакцію для оплати мережевої комісії, а відправник підписує для авторизації
переказу — sendTransaction автоматично збирає кожного зазначеного підписанта.
Демонстрація
// Generate keypairs for sender, recipient, and sponsor (fee payer)const sender = await generateKeyPairSigner();const recipient = await generateKeyPairSigner();const sponsor = await generateKeyPairSigner();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 = getTransferCheckedInstruction({source: senderAta,mint: mint.address,destination: recipientAta,authority: sender, // Sender signs to authorize the transferamount: 250_000n, // 0.25 tokensdecimals: 6});// Send transaction with sponsor as fee payer using @solana/kit// The sponsor (client.payer) pays fees; the sender signs for the transferconst { context } = await client.sendTransaction([transferInstruction]);const signature = context.signature;console.log("\n=== Sponsored Token Payment Complete ===");console.log("Transaction Signature:", signature.toString());// Fetch final token account balances from the RPCconst senderBalance = await client.rpc.getTokenAccountBalance(senderAta).send();const recipientBalance = await client.rpc.getTokenAccountBalance(recipientAta).send();console.log("\nSender Token Account Balance:", senderBalance.value.uiAmount);console.log("Recipient Token Account Balance:",recipientBalance.value.uiAmount);// Fetch transaction detailsconst transaction = await client.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
Примітив платника комісії є потужним, але побудова продакшн-системи без газу вимагає більшого: управління гаманцями спонсорів, обробка конвертації токенів (щоб користувачі могли "оплачувати" комісії в USDC), обмеження швидкості та контроль безпеки.
Kora керує цією складністю. Це JSON-RPC сервер, який надає абстракцію комісій, тому користувачам ніколи не потрібен SOL. Ви можете повністю спонсорувати комісії або приймати оплату комісій у будь-якому токені.
Розгорніть Kora однією командою:
cargo install kora-clikora --config path/to/kora.toml rpc start --signers-config path/to/signers.toml
Потім налаштуйте клієнт Kit на ваш сервер Kora і надсилайте транзакції за допомогою того самого API, що використовувався вище:
pnpm add @solana/kora
import { address } from "@solana/kit";import { createKitKoraClient } from "@solana/kora";// A Kora-backed Kit client: Kora is the fee payer, and users pay// fees in the SPL token of your choice instead of SOLconst kora = await createKitKoraClient({endpoint: "https://your-kora-instance",rpcUrl: "https://api.mainnet-beta.solana.com",feeToken: address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDCfeePayerWallet: sender // Signs to authorize the token fee payment});// Same instruction, same call—Kora sponsors the network feeconst { context } = await kora.sendTransaction([transferInstruction]);console.log("Signature:", context.signature);
Ресурси Kora
Is this page helpful?