수수료 추상화

모든 Solana 트랜잭션은 네트워크 수수료를 지불하기 위해 SOL이 필요합니다. 하지만 결제 애플리케이션을 사용하는 사용자는 두 번째 토큰 잔액을 관리하는 것이 아니라 스테이블코인으로 거래하기를 기대합니다. 수수료 추상화는 다른 사람이 수수료를 지불하도록 하여 이러한 마찰을 제거합니다.

이 가이드는 두 가지 수준을 다룹니다:

  1. 수수료 후원 작동 방식 — 기본 Solana 프리미티브
  2. Kora를 사용한 대규모 수수료 추상화 — 프로덕션 준비가 완료된 수수료 추상화 서비스

수수료 후원 작동 방식

Solana 트랜잭션에는 네트워크 수수료를 지불하는 계정인 지정된 수수료 지불자가 있습니다. 기본적으로 이것은 첫 번째 서명자입니다. 하지만 다른 계정을 수수료 지불자로 지정할 수 있어 제3자("후원자")가 발신자를 대신하여 수수료를 부담할 수 있습니다.

발신자와 후원자 모두 트랜잭션에 서명해야 합니다:

  • 발신자는 토큰 전송을 승인하기 위해 서명합니다
  • 후원자는 네트워크 수수료 지불을 승인하기 위해 서명합니다

핵심 결제 개념은 Solana에서 결제 작동 방식을 참조하세요.

아래 단계는 핵심 흐름을 보여줍니다. 완전히 실행 가능한 코드는 데모를 참조하세요.

후원자 계정 생성

트랜잭션 수수료를 지불할 후원자를 위한 별도의 키페어를 생성합니다. 후원자는 수수료를 위해 SOL이 필요하지만 전송되는 토큰을 보유할 필요는 없습니다.

전송 명령 생성

발신자를 권한으로 하여 토큰 전송 명령을 생성합니다. 발신자는 토큰을 소유하며 전송에 서명해야 합니다.

스폰서를 수수료 지불자로 하여 전송하기

authority(전송에 서명하는 발신자)와 feePayer(수수료를 지불하는 스폰서) 모두와 함께 prepareAndSend를 사용하세요. 두 당사자 모두 트랜잭션에 서명해야 합니다.

후원자 계정 생성

트랜잭션 수수료를 지불할 후원자를 위한 별도의 키페어를 생성합니다. 후원자는 수수료를 위해 SOL이 필요하지만 전송되는 토큰을 보유할 필요는 없습니다.

전송 명령 생성

발신자를 권한으로 하여 토큰 전송 명령을 생성합니다. 발신자는 토큰을 소유하며 전송에 서명해야 합니다.

스폰서를 수수료 지불자로 하여 전송하기

authority(전송에 서명하는 발신자)와 feePayer(수수료를 지불하는 스폰서) 모두와 함께 prepareAndSend를 사용하세요. 두 당사자 모두 트랜잭션에 서명해야 합니다.

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.

최종 사용자를 위해 토큰 계정을 생성할 때, 사용자는 해당 계정을 닫고 rent에 사용된 SOL을 회수할 수 있습니다. 스테이블코인으로 계정 생성 비용을 사용자에게 청구하거나, 이 비용을 제품 경제 모델에 반영하는 것을 고려하세요.

Kora를 통한 대규모 수수료 추상화

수수료 지불자 프리미티브는 강력하지만, 프로덕션 가스리스 시스템을 구축하려면 더 많은 것이 필요합니다: 스폰서 지갑 관리, 토큰 변환 처리(사용자가 USDC로 수수료를 "지불"할 수 있도록), 속도 제한 및 보안 제어가 필요합니다.

Kora는 이러한 복잡성을 처리합니다. 사용자가 SOL을 전혀 필요로 하지 않도록 수수료 추상화를 제공하는 JSON-RPC 서버입니다. 수수료를 완전히 스폰서하거나 모든 토큰으로 수수료 지불을 받을 수 있습니다.

단일 명령으로 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 솔라나 재단.
모든 권리 보유.
연결하기