Skip to main content

Create a fee-bearing token with Token-2022

Create a Solana token that charges a transfer fee automatically, with no custom smart contract code.
Create a fee-bearing token with Token-2022 background
Challenge

Create a fee-bearing token with Token-2022

Arc theme: Token-2022

Web2 bridge: Token extensions are like middleware for your currency, adding features without changing the core protocol

The Scenario

You have spent the last arc minting NFTs using Token-2022. You stamped metadata directly on the mint. You grouped tokens into a collection. You audited every byte and even mutated a field live on devnet. Without saying it out loud, you have already been playing with token extensions.

This week you start saying it out loud. Token extensions are like middleware for your currency. In your Web2 life, if you wanted to charge a fee on every payment, you would wire up something at the payment processor layer. You would write a little function that wraps the transfer and skims off a percentage before forwarding the rest. The transfer itself stays dumb, your middleware does the smart thing.

Solana flips that. Instead of the middleware living next to the asset, the middleware lives inside the asset. You bake the fee directly into the mint. Every wallet, every program, every CLI, every dApp that transfers your token automatically respects that fee, because the rule is part of the token itself. Today you create one of those tokens and confirm with your own eyes that the rule is sitting on chain.

The Challenge

What you’ll need

  • A terminal
  • The Solana CLI already installed and pointed at devnet
  • The spl-token CLI already installed (you have been using it for NFTs in the previous arc)
  • A funded devnet keypair (a small amount of devnet SOL is enough)

Steps

  1. Confirm your CLI is pointed at devnet so you do not accidentally burn real SOL on this experiment.
  2. Create a new fungible mint using the Token-2022 program and attach the Transfer Fee extension. You will set a fee of 100 basis points (1 percent of every transfer) and a maximum fee cap of 1000000. The CLI reads this number as a UI amount, so the cap works out to 1,000,000 whole tokens, high enough that it never kicks in on a normal transfer and the full 1 percent applies every time.
  3. Copy the mint address that the CLI prints back at you. You will need it for the next steps and for tomorrow’s Build day.
  4. Create an associated token account for yourself so this mint has somewhere to land.
  5. Mint a small starting supply to your own wallet so the token is not empty.
  6. Display the mint and look for the TransferFeeConfig section in the output. That is the proof that your middleware is on chain.

Run it

Point at devnet and check the active keypair has some SOL:

solana config set --url https://api.devnet.solana.com
solana balance

Create the fee-bearing mint. The long string after --program-id is the on chain address of the Token-2022 program:

spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
  create-token \
  --transfer-fee-basis-points 100 \
  --transfer-fee-maximum-fee 1000000 \
  --decimals 6

Save the mint address that gets printed. Then create a token account for it and mint yourself a starting supply of 1,000 tokens:

spl-token create-account [YOUR_MINT_ADDRESS]
spl-token mint [YOUR_MINT_ADDRESS] 1000

Now ask the chain to describe your mint:

spl-token display [YOUR_MINT_ADDRESS]

In the output, look for a section that reads Extensions. Inside it you should see TransferFeeConfig with your basis points and maximum fee. That is the on chain receipt for the rule you just baked in.

What Just Happened

You created a token that is no longer just a number that moves around. It is a number that moves around with rules attached. The Transfer Fee extension is part of the mint account itself, which means every program on Solana that handles this token sees the same rule and applies it the same way. There is no way for a wallet to “forget” the fee or for a marketplace to “route around” it, because the fee logic is enforced by the Token-2022 program at the protocol level, not by a smart contract you wrote on the side.

Compare that to your Web2 mental model. If you were charging a fee on Stripe payments, you would have to ship a backend that wraps the charge, and you would have to trust every integration partner to call your wrapper. On Solana, you ship the rule once, on the mint, and every downstream integration inherits it for free. The “middleware” is not next to the asset, it is inside the asset.

You also got a glimpse of the broader pattern for this arc. Extensions are composable rules you snap onto a mint at creation time. Transfer Fee is one. Non-Transferable, Interest-Bearing, Default Account State, Permanent Delegate and others are all in the same family. Over the next few days you will be reaching for these the way a Web2 dev reaches for npm packages.

Resources

Submission

Take a screenshot of your terminal showing the spl-token display output with the TransferFeeConfig extension visible on your new mint. Submit it below!

Submit your project