Skip to main content

Extension Guide

The Token-2022 program provides additional functionality on mints and token accounts through an extension model.

This guide explains all of the available extensions, along with some examples of how to use them.

Please see the Token-2022 Introduction more general information about Token-2022 and the concept of extensions.


See the Token Setup Guide to install the client utilities. Token-2022 shares the same CLI and NPM packages for maximal compatibility.

All JS examples are adapted from the tests, and available in full at the Token JS examples.


Mint Close Authorityโ€‹

The Token program allows owners to close token accounts, but it is impossible to close mint accounts. In Token-2022, it is possible to close mints by initializing the MintCloseAuthority extension before initializing the mint.

Example: Initializing a mint with mint close authorityโ€‹

$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-close
Creating token C47NXhUTVEisCfX7s16KrxYyimnui7HpUXZecE2TmLdB under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Example: Closing a mintโ€‹

With the MintCloseAuthority extension on the mint and a valid authority, it's possible to close the mint account and reclaim the lamports on the mint account. Note: The supply on the mint must be 0.

$ spl-token close-mint C47NXhUTVEisCfX7s16KrxYyimnui7HpUXZecE2TmLdB 
Signature: 5nidwS9fJGJGdmaQjcwvNGVtk2ba5Zyu9ZLubjUKSsaAyzLUYvB6LK5RfUA767veBr45x7R1WW9N7WkYZ3Rqsb5B

Transfer Feesโ€‹

In the Token program, it is impossible to assess a fee on every transfer. The existing systems typically involve freezing user accounts, and forcing them to go through a third party to unfreeze, transfer, and refreeze the accounts.

With Token-2022, it's possible to configure a transfer fee on a mint so that fees are assessed at the protocol level. On every transfer, some amount is withheld on the recipient account, untouchable by the recipient. These tokens can be withheld by a separate authority on the mint.

Important note: Transferring tokens with a transfer fee requires using transfer_checked or transfer_checked_with_fee instead of transfer. Otherwise, the transfer will fail.

Example: Creating a mint with a transfer feeโ€‹

Transfer fee configurations contain a few important fields:

  • Fee in basis points: fee assessed on every transfer, as basis points of the transfer amount. For example, with 50 basis points, a transfer of 1,000 tokens yields 5 tokens
  • Maximum fee: cap on transfer fees. With a maximum fee of 5,000 tokens, even a transfer of 10,000,000,000,000 tokens only yields 5,000 tokens
  • Transfer fee authority: entity that can modify the fees
  • Withdraw withheld authority: entity that can move tokens withheld on the mint or token accounts

Let's create a mint with 50 basis point transfer fee, and a maximum fee of 5,000 tokens.

$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --transfer-fee 50 5000
Creating token Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address: Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H
Decimals: 9

Signature: 39okFGqW23wQZ1HqH2tdJvtFP5aYgpfbmNktCZpV5XKTpKuA9xJmvBmrBwcLdfAT632VEC4y4dJJfDoeAvMWRPYP

Example: Transferring tokens with the fee checkedโ€‹

As part of the extension, there is a new transfer_checked_with_fee instruction, which accepts the expected fee. The transfer only succeeds if the fee is correctly calculated, in order to avoid any surprises during the transfer.

$ spl-token create-account Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H
Creating account 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx

Signature: 6h591BMuguh9TtSdQPRPcPy97mLqJiybeaxGVZzD8mvPEsYypjZ2jjKgHzji5FGh8CJE3NAzqrqGxfyMdnbWrs7
$ solana-keygen new -o destination.json
$ spl-token create-account Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H destination.json
Creating account 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr

Signature: 2SyA17AJRWLH2j7svgxgW7nouUGioeWoRDWjz2Wq8j1eisThezSvqgN4NbHfj9uWmDh2XRp56ttZtHV1SxaUC7ys
$ spl-token mint Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H 1000000000
Minting 1000000000 tokens
Token: Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H
Recipient: 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx

Signature: 5MFJGpLaWe3yLLU8X4ax3KofeqPVzdxJsa3ScjChJJHJawKsRx4og9eaFkWn3CPF7JXaxdj5v4LdAW56LiNTuP6s
$ spl-token transfer --expected-fee 0.000005 Dg3i18BN7vzsbAZDnDv3H8nQQjSaPUTqhwX41J7NZb5H 1000000 destination.json
Transfer 1000000 tokens
Sender: 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx
Recipient: 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr

Signature: 3hc3CCiETiuCArJ6yZ76ScyfMeK1rw8CTfZ3aDGnYoEMeoqXfSNAtnM3ATFjm7UihthzEkEWzeUfWL4qqqB4ofgv

Example: Find accounts with withheld tokensโ€‹

As users transfer their tokens, transfer fees accumulate in the various recipient accounts. The withdraw withheld authority, configured at initialization, can move these tokens wherever they wish using withdraw_withheld_tokens_from_accounts or harvest_withheld_tokens_to_mint.

Before doing that, however, they must find which accounts have withheld tokens by iterating over all accounts for the mint.

CLI support coming soon!

Example: Withdraw withheld tokens from accountsโ€‹

With the accounts found, the withheld withdraw authority may move the withheld tokens.

$ spl-token withdraw-withheld-tokens 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr
Signature: 2NfjbEnRQC7kXkf86stb6u7eUtaQTGDebo8ktCdz4gP4wCD93xtx75rSJxJDQVePNAa8NqtVLjUm19ZBDRVaYurt

Note: The design of pooling transfer fees at the recipient account is meant to maximize parallelization of transactions. Otherwise, one configured fee recipient account would be write-locked between parallel transfers, decreasing throughput of the protocol.

Example: Harvest withheld tokens to mintโ€‹

Users may want to close a token account with withheld transfer fees, but it is impossible to close an account that holds any tokens, including withheld ones.

To clear out their account of withheld tokens, they can use the permissionless harvest_withheld_tokens_to_mint instruction.

The harvest instruction isn't explicitly exposed since it typically isn't needed. It is required before closing an account, however, so we can show the harvest behavior by closing the account:

$ spl-token close --address 5wY8fiMZG5wGbQmtzKgqqEEp4vsCMJZ53RXEagUUWhEr
Signature: KAKXryAdGSVFqpQhrwrvP6NCAQwLQp2Sj1WiAqCHxxwJsvRLKx4JzWgN9zYUaJNmfrZnQQw9yYoDw5Xx1YrwY6i

Signature: 2i5KGekFFtwzkX2W71cxPvQsGEH21qmZ3ieNQz7Mz2qGqp2pyzMNZhSVRfxJxQuAxnKQoZKjAb62FBx2gxaq25Le

Example: Withdraw withheld tokens from mintโ€‹

As users move the withheld tokens to the mint, the withdraw authority may choose to move those tokens from the mint to any other account.

$ spl-token withdraw-withheld-tokens --include-mint 7UKuG4W68hW9eGrDms6BenRf8DCEHKGN49xewtWyB5cx

Signature: 5KzdgcKgi3rLaBRfDbG5pxZwyKppyVjAA8TUCjTMfb1vMYv7CLQWaxgFz81jz4reUaF7oP67Gdqoc91Ted6qr1Hb

Default Account Stateโ€‹

A mint creator may want to restrict who can use their token. There are many heavy-handed approaches to this problem, most of which include going through a centralized service at the beginning. Even through a centralized service, however, it's possible for anyone to create a new token account and transfer the tokens around.

To simplify the restriction, a mint creator may use the DefaultAccountState extension, which can force all new token accounts to be frozen. This way, users must eventually interact with some service to unfreeze their account and use tokens.

Example: Creating a mint with default frozen accountsโ€‹

$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-freeze --default-account-state frozen
Creating token 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address: 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf
Decimals: 9

Signature: 5wfYvovguPEbyv2uSWxGt9JcpTWgyuP4hY3wutjS32Ahnoni4qd7gf6sLre855WvT6xLHwrvV7J8bVmXymNU2qUz

$ spl-token create-account 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf
Creating account 6XpKagP1N3K1XnzStufpV5YZ6DksEkQWgLNG9kPpLyvv

Signature: 2awxWdQMgv89ew34sEyG361vshB2wPXHHfva5iJ43dWr18f2Pr6awoXfsqYPpyS2eSbH6jhfVY9EUck8iJ4wCSN6

$ spl-token display 6XpKagP1N3K1XnzStufpV5YZ6DksEkQWgLNG9kPpLyvv
SPL Token Account
Address: 6XpKagP1N3K1XnzStufpV5YZ6DksEkQWgLNG9kPpLyvv
Program: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Balance: 0
Decimals: 9
Mint: 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf
Owner: 4SnSuUtJGKvk2GYpBwmEsWG53zTurVM8yXGsoiZQyMJn
State: Frozen
Delegation: (not set)
Close authority: (not set)
Immutable owner

Example: Updating default stateโ€‹

Over time, if the mint creator decides to relax this restriction, the freeze authority may sign an update_default_account_state instruction to make all accounts unfrozen by default.

$ spl-token update-default-account-state 8Sqz2zV8TFTnkLtnCdqRkjJsre3GKRwHcZd3juE5jJHf initialized

Signature: 3Mm2JCPrf6SrAe9awV3QzYvHiYmatiGWTmrQ7YnmzJSqyNCf75rLNMyH7jU26uZwX7q3MmBEBj1A36o5sGk9Vakb

Immutable Ownerโ€‹

Token account owners may reassign ownership to any other address. This is useful in many situations, but it can also create security vulnerabilities.

For example, the addresses for Associated Token Accounts are derived based on the owner and the mint, making it easy to find the "right" token account for an owner. If the account owner has reassigned ownership of their associated token account, then applications may derive the address for that account and use it, not knowing that it does not belong to the owner anymore.

To avoid this issue, Token-2022 includes the ImmutableOwner extension, which makes it impossible to reassign ownership of an account. The Associated Token Account program always uses this extension when creating accounts.

Example: Explicitly creating an account with immutable ownershipโ€‹

$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token
Creating token CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address: CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV
Decimals: 9

Signature: 4fT19YaE3zAscj71n213K22M3wDSXgwSn39RBCVtiCTxMX7pZhAoHywP2QMKqWpZMB5vT7diQ8QaFp3abHztpyPC
$ solana-keygen new -o account.json
$ spl-token create-account CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV account.json --immutable
Creating account EV2xsZto1TRqehewwWHUUQm68X6C6MepBSkbfZcVdShy

Signature: 5NqXiE3LPFnufnZhcwKPoZt7DaPR7qwfhmRr9W9ykhNM7rnu6MDdx7n5eTpEisiaSET2R4fZW7a91Ai6pCuskXF8

Example: Creating an associated token account with immutable ownershipโ€‹

All associated token accounts have the immutable owner extension included, so it's extremely easy to use the extension.

$ spl-token create-account CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV
Creating account 4nvfLgYMERdNbbf1pADUSp44XukAyjeWWXCMkM1gMqC4

Signature: w4TRYDdCpTfmQh96E4UNgFFeiAHphWNaeYrJTu6bGyuPMokJrKFR33Ntj3iNQ5QQuFqom2CaYkhXiX9sBpWEW23

The CLI will tell us that it's unnecessary to specify the --immutable argument if it's provided:

$ spl-token create-account CZxztd7SEZWxg6B9PH5xa7QwKpMCpWBJiTLftw1o3qyV --immutable
Creating account 4nvfLgYMERdNbbf1pADUSp44XukAyjeWWXCMkM1gMqC4
Note: --immutable specified, but Token-2022 ATAs are always immutable, ignoring

Signature: w4TRYDdCpTfmQh96E4UNgFFeiAHphWNaeYrJTu6bGyuPMokJrKFR33Ntj3iNQ5QQuFqom2CaYkhXiX9sBpWEW23

Non-Transferable Tokensโ€‹

To accompany immutably owner token accounts, the NonTransferable mint extension allows for "soul-bound" tokens that cannot be moved to any other entity. For example, this extension is perfect for achievements that can only belong to one person or account.

This extension is very similar to issuing a token and then freezing the account, but allows the owner to burn and close the account if they want.

Example: Creating a non-transferable mintโ€‹

$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --enable-non-transferable
Creating token 7De7wwkvNLPXpShbPDeRCLukb3CRzCNcC3iUuHtD6k4f under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address: 7De7wwkvNLPXpShbPDeRCLukb3CRzCNcC3iUuHtD6k4f
Decimals: 9

Signature: 2QtCBwCo2J9hf2Prd2t4CBBUxEXQCBSSD5gkNc59AwhxsKgRp92czNAvwWDxjeXGFCWSuNmzAcD19cEpqubovDDv

Required Memo on Transferโ€‹

Traditional banking systems typically require a memo to accompany all transfers. The Token-2022 program contains an extension to satisfy this requirement.

By enabling required memo transfers on your token account, the program enforces that all incoming transfers must have an accompanying memo instruction right before the transfer instruction.

Note: This also works in CPI contexts, as long as a CPI is performed to log the memo before invoking the transfer.

Example: Create account with required memo transfersโ€‹

$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token
Creating token EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address: EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N
Decimals: 9

Signature: 2mCoV3ujSUArgZMyayiYtLZp2QzpqKx3NXnv9W8DpinY39rBU2yGmYLfp2tZ9uZqVbfJ6Mf3SqDHexdCcFcDAEvc
$ spl-token create-account EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N
Creating account 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL

Signature: 57wZHDaQtSzszDkusrnozZNj5PemQhpqHMEFLWFKpqASCErcDuBuYuEky5g3evHtkjMrKgh1s3aEap1L8y5UhW5W
$ spl-token enable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
Signature: 5MnWtrhMK32zkbacDMwBNft48VAUpr4EoRM87hkT9AFYvPgPEU7V7ERV6gdfb3kASri4wnUnr13hNKuYJ66pD8Fs

Example: Enabling or disabling required memo transfersโ€‹

An account owner may always choose to flip required memo transfers on or off.

$ spl-token disable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
Signature: 5a9X8JrWzwZqb3iMonfUfSZbisQ57aEmW5cFntWGYRv2UZx8ACkMineBEQRHwLMzYHeyFDEHMXu8zqAMv5tm4u1g

$ spl-token enable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
Signature: 5MnWtrhMK32zkbacDMwBNft48VAUpr4EoRM87hkT9AFYvPgPEU7V7ERV6gdfb3kASri4wnUnr13hNKuYJ66pD8Fs


In the previous example, astute readers of the JavaScript code may have noticed that the EnableRequiredMemoTransfers instruction came after InitializeAccount, which means that this extension can be enabled after the account is already created.

In order to actually add this extension after the account is created, however, you may need to reallocate more space in the account for the additional extension bytes.

The Reallocate instruction allows an owner to reallocate their token account to fit room for more extensions.

Example: Reallocating existing account to enable required memo transfersโ€‹

The CLI reallocs automatically, so if you use enable-required-transfer-memos with an account that does not have enough space, it will add the Reallocate instruction.

$ spl-token create-account EbPBt3XkCb9trcV4c8fidhrvoeURbDbW87Acustzyi8N
Creating account 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL

Signature: 57wZHDaQtSzszDkusrnozZNj5PemQhpqHMEFLWFKpqASCErcDuBuYuEky5g3evHtkjMrKgh1s3aEap1L8y5UhW5W
$ spl-token enable-required-transfer-memos 4Uzz67txwYbfYpF8r5UGEMYJwhPAYQ5eFUY89KTYc2bL
Signature: 5MnWtrhMK32zkbacDMwBNft48VAUpr4EoRM87hkT9AFYvPgPEU7V7ERV6gdfb3kASri4wnUnr13hNKuYJ66pD8Fs

Interest-Bearing Tokensโ€‹

Tokens that constantly grow or decrease in value have many uses in the real world. The most well known example is a bond.

With Token, this has only been possible through proxy contracts that require regular rebase or update operations.

With the Token-2022 extension model, however, we have the possibility to change how the UI amount of tokens are represented. Using the InterestBearingMint extension and the amount_to_ui_amount instruction, you can set an interest rate on your token and fetch its amount with interest at any time.

Interest is continuously compounded based on the timestamp in the network. Due to drift that may occur in the network timestamp, the accumulated interest could be lower than the expected value. Thankfully, this is rare.

Note: No new tokens are ever created, the UI amount returns the amount of tokens plus all interest the tokens have accumulated. The feature is entirely cosmetic.

Example: Create an interest-bearing mintโ€‹

$ spl-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb create-token --interest-rate 10
Creating token 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address: 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj
Decimals: 9

Signature: 5dSW5QUacEsaKYb3MwYp4ycqq4jpNJ1rpLhS5rotoe3CWv9XhhjrncUFpk14R1fRamS1xprziC3NkpbYno4c8JxD

Example: Update the interest rateโ€‹

The rate authority may update the interest rate on the mint at any time.

$ spl-token set-interest-rate 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj 50
Setting Interest Rate for 7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj to 50 bps

Signature: 5DQs6hzkfGq3uotESuVwF7MGeMawwfQcm1e9RHaUeVySDV6xpUzYhzdb6ygqJfsEZqewgiDR5KuxaGzkdTMcDrTn