Кожна транзакція в Solana потребує SOL для оплати мережевих комісій. Але користувачі, які приходять до вашого платіжного застосунку, очікують здійснювати транзакції у стейблкоїнах — а не керувати балансом другого токена. Абстракція комісій усуває це тертя, дозволяючи комусь іншому оплачувати комісії.
Цей посібник охоплює два рівні:
- Як працює спонсорство комісій — базовий примітив Solana
- Абстракція комісій у масштабі з Kora — готовий до продакшену сервіс абстракції комісій
Як працює спонсорство комісій
Транзакції Solana мають визначеного платника комісій — обліковий запис, який оплачує мережеву комісію. За замовчуванням це перший підписант. Але ви можете вказати інший обліковий запис як платника комісій, дозволяючи третій стороні ("спонсору") покривати комісії від імені відправника.
І відправник, і спонсор повинні підписати транзакцію:
- Відправник підписує для авторизації переказу своїх токенів
- Спонсор підписує для авторизації оплати мережевої комісії
Дивіться Як працюють платежі в Solana для основних концепцій платежів.
Кроки нижче показують основний потік. Дивіться Демо для повного робочого коду.
Створіть обліковий запис спонсора
Згенеруйте окрему пару ключів для спонсора, який оплачуватиме комісії транзакцій. Спонсору потрібен SOL для комісій, але йому не потрібно тримати токени, які переказуються.
Створіть інструкцію переказу
Створіть інструкцію переказу токенів з відправником як авторитетом. Відправник володіє токенами і повинен підписати переказ.
Надсилання зі спонсором як платником комісії
Використовуйте prepareAndSend з обома authority (відправник, який підписує
переказ) та feePayer (спонсор, який оплачує комісії). Обидва мають підписати
транзакцію.
Демонстрація
// 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
Примітив платника комісії є потужним, але побудова продакшн-системи без газу вимагає більшого: управління гаманцями спонсорів, обробка конвертації токенів (щоб користувачі могли "оплачувати" комісії в 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?