Understanding the Token Transfer Fee Extension

·

The Transfer Fee extension introduces a powerful mechanism for token creators to enforce fees on every token transfer. This feature operates at the protocol level, ensuring consistent fee collection without requiring additional instructions or specialized programs. Much like service charges applied during financial transactions, this extension enables automated fee deductions directly within the token's ecosystem.

How Transfer Fee Extension Works

The TransferFee extension configures a transfer fee directly on the Mint Account, allowing fees to be collected systematically. During each token transfer, the designated fee amount is automatically set aside within the recipient's Token Account. These withheld funds remain inaccessible to the recipient and can only be retrieved by the designated Withdraw Authority.

This architecture pools transfer fees at recipient accounts to maximize transaction parallelization. Without this design, a single configured fee recipient account would experience write-locking between parallel transfers, significantly reducing protocol throughput.

👉 Explore transfer fee implementation strategies

Key Configuration Parameters

When setting up a token with transfer fees, several critical parameters must be defined:

Implementation Guide

Prerequisites and Setup

Before implementing transfer fees, ensure you have the necessary dependencies installed. The process requires both @solana/web3.js and @solana/spl-token libraries for proper functionality.

import { Connection, Keypair, SystemProgram, Transaction } from "@solana/web3.js";
import { ExtensionType, TOKEN_2022_PROGRAM_ID, createInitializeTransferFeeConfigInstruction } from "@solana/spl-token";

Mint Account Configuration

Creating a mint account with transfer fee capabilities requires careful configuration:

const mintKeypair = Keypair.generate();
const mint = mintKeypair.publicKey;
const decimals = 2;
const mintAuthority = pg.wallet.publicKey;
const transferFeeConfigAuthority = pg.wallet.keypair;
const withdrawWithheldAuthority = pg.wallet.keypair;
const feeBasisPoints = 100; // 1% fee
const maxFee = BigInt(100); // Maximum fee amount

Calculating Space Requirements

Token Extensions require additional account space based on the extensions enabled:

const mintLen = getMintLen([ExtensionType.TransferFeeConfig]);
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);

Transaction Construction

Build the necessary instructions for mint creation and transfer fee initialization:

const createAccountInstruction = SystemProgram.createAccount({
  fromPubkey: payer.publicKey,
  newAccountPubkey: mint,
  space: mintLen,
  lamports,
  programId: TOKEN_2022_PROGRAM_ID,
});

const initializeTransferFeeConfig = createInitializeTransferFeeConfigInstruction(
  mint,
  transferFeeConfigAuthority.publicKey,
  withdrawWithheldAuthority.publicKey,
  feeBasisPoints,
  maxFee,
  TOKEN_2022_PROGRAM_ID
);

Transfer Execution and Fee Calculation

When transferring tokens, the system automatically calculates and deducts the appropriate fee:

const transferAmount = BigInt(1000_00);
const fee = (transferAmount * BigInt(feeBasisPoints)) / BigInt(10_000);
const feeCharged = fee > maxFee ? maxFee : fee;

transactionSignature = await transferCheckedWithFee(
  connection,
  payer,
  sourceTokenAccount,
  mint,
  destinationTokenAccount,
  payer.publicKey,
  transferAmount,
  decimals,
  feeCharged,
  undefined,
  undefined,
  TOKEN_2022_PROGRAM_ID
);

Fee Management and Withdrawal

Identifying Accounts With Withheld Fees

To locate token accounts holding accumulated fees:

const allAccounts = await connection.getProgramAccounts(TOKEN_2022_PROGRAM_ID, {
  commitment: "confirmed",
  filters: [{ memcmp: { offset: 0, bytes: mint.toString() } }],
});

const accountsToWithdrawFrom = [];
for (const accountInfo of allAccounts) {
  const account = unpackAccount(accountInfo.pubkey, accountInfo.account, TOKEN_2022_PROGRAM_ID);
  const transferFeeAmount = getTransferFeeAmount(account);
  if (transferFeeAmount !== null && transferFeeAmount.withheldAmount > 0) {
    accountsToWithdrawFrom.push(accountInfo.pubkey);
  }
}

Withdrawal from Token Accounts

The Withdraw Authority can retrieve fees from multiple token accounts simultaneously:

transactionSignature = await withdrawWithheldTokensFromAccounts(
  connection,
  payer,
  mint,
  destinationTokenAccount,
  withdrawWithheldAuthority,
  undefined,
  accountsToWithdrawFrom,
  undefined,
  TOKEN_2022_PROGRAM_ID
);

Fee Harvesting to Mint Account

Token accounts containing withheld fees cannot be closed until cleared. The harvest function transfers fees to the mint account:

transactionSignature = await harvestWithheldTokensToMint(
  connection,
  payer,
  mint,
  [destinationTokenAccount],
  undefined,
  TOKEN_2022_PROGRAM_ID
);

Final Withdrawal from Mint

Fees accumulated in the mint account can be withdrawn by the authorized authority:

transactionSignature = await withdrawWithheldTokensFromMint(
  connection,
  payer,
  mint,
  destinationTokenAccount,
  withdrawWithheldAuthority,
  undefined,
  undefined,
  TOKEN_2022_PROGRAM_ID
);

Important Considerations

The Transfer Fee extension only deducts fees in the same token denomination. For cross-token fee structures, consider implementing the Transfer Hook extension instead. This approach maintains protocol efficiency while providing flexible fee mechanisms.

👉 Discover advanced token extension techniques

Frequently Asked Questions

What is the purpose of the Transfer Fee extension?
The Transfer Fee extension enables automatic fee collection during token transfers at the protocol level. It eliminates the need for additional instructions or custom programs while ensuring consistent fee application across all transactions involving the token.

How are transfer fees calculated?
Fees are calculated based on the configured basis points (percentage) of the transfer amount, up to a predefined maximum fee. The system automatically determines the exact fee during each transfer operation.

Who can withdraw accumulated fees?
Only the designated Withdraw Authority can access and withdraw fees accumulated in token accounts or harvested to the mint account. This authority is established during the initial mint configuration.

Can token accounts with withheld fees be closed?
No, token accounts containing withheld transfer fees cannot be closed until the fees are either harvested to the mint account or withdrawn by the authorized authority.

What's the difference between harvesting and withdrawing fees?
Harvesting moves fees from token accounts to the mint account, while withdrawing transfers fees from either token accounts or the mint account to a designated destination account.

Is the Transfer Fee extension compatible with all token types?
The extension works specifically with tokens created under the Token 2022 program and requires proper configuration during mint creation. It cannot be added to existing tokens that weren't initially created with this extension.

The Transfer Fee extension provides a robust framework for implementing automated fee structures within token ecosystems. By leveraging this functionality, developers can create sustainable token economies with built-in revenue mechanisms while maintaining high transaction throughput and protocol efficiency.