Introduction to Solana SPL Token-2022

Hello fellow Solanauts! Today we're diving into the shiny new Token-2022 Solana Program Library (SPL) token standard and what it means for you as a Solana developer using the Anchor framework. Strap in, because this upgrade brings some major benefits and unlocks exciting new possibilities for Solana tokens!

But first, let's make sure we're all on the same page by recapping the original SPL token program...

Background: The Original SPL Token Program

The original SPL token program provided a standard for creating and managing fungible and non-fungible tokens on Solana. It defined a common interface and a set of instructions for minting tokens, transferring them between accounts, setting metadata, and more.

Here's a simple example of minting an SPL token using the original program:

let mint = Pubkey::new_unique();

let token = Keypair::new();
let token_account = Keypair::new();

let (mint_authority, _) = Pubkey::find_program_address(&[b"mint"], &spl_token::id());

let mut transaction = Transaction::new_with_payer(
    &[
        system_instruction::create_account(
            &payer.pubkey(),
            &token.pubkey(),
            rent.minimum_balance(spl_token::state::Mint::LEN),
            spl_token::state::Mint::LEN as u64,
            &spl_token::id(),
        ),
        spl_token::instruction::initialize_mint(
            &spl_token::id(), 
            &token.pubkey(), 
            &mint_authority, 
            None, 
            9
        )?,
    ],
    Some(&payer.pubkey()),
);

As you can see, it takes a fair bit of boilerplate to create a token mint, an associated token account, and initialize everything. And that's before we even get to minting any tokens!

The original token program worked well, but it had some limitations:

  • Required separate initialization instructions for mints and token accounts
  • Limited ability to extend token functionality
  • Didn't utilize some newer Solana features like program derived addresses (PDAs)

Enter SPL Token-2022, a new and improved token standard that addresses these shortcomings and brings some great new capabilities...

What's New in SPL Token-2022?


1. Extensions

One of the biggest changes with Token-2022 is the introduction of extensions. Extensions allow developers to add new functionality to tokens in a modular way.

The base token program (sometimes called the "vanilla" program) provides core features like minting, burning, and transferring tokens. But with extensions, you can now add superpowers to your tokens, like:

  • Transfer fees
  • Confidential transfers for increased privacy
  • Interest accrual to make tokens more attractive to hold
  • Time locks to restrict when tokens can be transferred

Here's an example of creating a mint with a transfer fee extension using Token-2022 and Anchor:


use anchor_spl::token::transfer_fee::*;

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct CreateMintWithFees {
    pub transfer_fee_basis_points: u16,
    pub maximum_fee: u64,
}

pub fn handler(ctx: Context, args: CreateMintWithFees) -> Result<()> {
    // Create the base mint 
    let cpi_accounts = MintTo {
        mint: ctx.accounts.mint.to_account_info(),
        to: ctx.accounts.destination.to_account_info(),
        authority: ctx.accounts.payer.to_account_info(), 
    };
    let cpi_program = ctx.accounts.token_program.to_account_info();
    let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
    token::mint_to(cpi_ctx, 1)?;

    // Initialize the transfer fee extension
    let cpi_accounts = InitializeTransferFeeConfig {
        mint: ctx.accounts.mint.to_account_info(),
        transfer_fee_config_authority: ctx.accounts.fee_authority.to_account_info(),
        fee_withdraw_authority: ctx.accounts.withdraw_authority.to_account_info(),
        transfer_fee_config: ctx.accounts.transfer_fee_config.to_account_info(),
        token_program: ctx.accounts.token_program.to_account_info(),     
    };
    let cpi_program = ctx.accounts.token_program.to_account_info();
    let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);

    transfer_fee::initialize_transfer_fee_config(
        cpi_ctx,
        args.transfer_fee_basis_points,
        args.maximum_fee,
    )?;

    Ok(())
}
The transfer fee extension allows charging a fee whenever the token is transferred. This could be used to collect royalties, fund ecosystem development, or incentivize holding.

With Anchor, adding extensions is straightforward - after interacting with the base mint, we simply make a CPI call to initialize the extension and pass in the relevant parameters.

The ability to add functionality through extensions makes Token-2022 much more flexible and extensible than the original token program. Developers can mix and match extensions to create tokens tailored for their specific use case.

2. Native Account Support

Another key improvement in Token-2022 is better integration with native Solana features like program derived addresses (PDAs).

With the original token program, initializing token accounts required a separate InitializeAccount instruction:


spl_token::instruction::initialize_account(
    &spl_token::id(),
    token_account.key,
    mint.key,
    owner.key,
)?,

Token-2022 simplifies this by allowing native token accounts to be created using Solana's system_program. When transferring to a non-existent token account, the program will automatically create an Associated Token Account (ATA) owned by the recipient.

For example:

let accounts = Transfer {
    token_program: ctx.accounts.token_program.to_account_info(),
    from: ctx.accounts.from.to_account_info(),
    to: ctx.accounts.to.to_account_info(),
    authority: ctx.accounts.from_authority.to_account_info(),
};
token::transfer(ctx.accounts.into(), 100)?;

This transfer instruction will automatically create a token account for the to address if it doesn't already exist, using a PDA derived from the token mint address and owner. No separate initialization required!

Using native accounts and PDAs has a few benefits:

  1. Reduced rent-exemption requirements, since PDAs don't need to be rent-exempt
  2. Deterministic account addresses based on mint + owner
  3. Cleaner transaction history by reducing initialization clutter

So in many cases, you can now work with token accounts just like normal Solana accounts, without extra initialization overhead.

3. Decoupled Authorities

Token-2022 also introduces the concept of Decoupled Authorities for more granular control over token permissions.

With the original token program, mint and freeze authorities were defined during mint initialization and could only be changed later by the current authority.

Token-2022 allows these authorities to be defined on a per-account basis. Each token account can have its own mint and freeze authority, separate from the base mint.

For example, when minting new tokens:

let cpi_accounts = MintToChecked {
    mint: ctx.accounts.mint.to_account_info(),
    to: ctx.accounts.to.to_account_info(),
    mint_authority: ctx.accounts.mint_authority.to_account_info(),
};
let cpi_program = ctx.accounts.token_program.to_account_info();
let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);

mint_to_checked(cpi_ctx, 1000, 9)?;

The mint_authority here is specific to the to account, rather than a global authority for the entire mint.

Why is this useful? A few potential benefits:

  • Allows more granular permission control
  • Supports use cases like per-account freezing
  • Can help implement features like token vesting

By providing more flexibility over authorities, Token-2022 expands the design space for tokens and how they can be managed.

This just scratches the surface of what's possible with decoupled authorities - feel free to let your imagination run wild!

Migrating to Token-2022

"This all sounds great," you may be thinking, "but what about my existing SPL tokens? Do I need to start from scratch?"

Fear not! The Token-2022 program is fully backward-compatible with the original token program. Your existing tokens will continue to function as normal.

However, to take advantage of the new Token-2022 features for existing tokens, you will need to migrate them to the new format. This involves:

  1. Deploying the Token-2022 program
  2. Creating a new Token-2022 mint
  3. Transferring token balances from the old accounts to new Token-2022 accounts
  4. Mapping legacy token metadata to the new format

Here's an example of how you might migrate a legacy token holder to Token-2022 using Anchor:

use anchor_spl::token;
use anchor_spl::token::Burn;

pub fn handler(ctx: Context) -> ProgramResult {
  // Burn the tokens from the legacy account 
  let burn_ctx = CpiContext::new(
    ctx.accounts.token_program.to_account_info(),
    Burn {
        mint: ctx.accounts.legacy_mint.to_account_info(),
        to: ctx.accounts.legacy_token_account.to_account_info(),
        authority: ctx.accounts.legacy_token_account.to_account_info(),
    },
  );
  token::burn(burn_ctx, ctx.accounts.legacy_token_account.amount)?;

  // Mint tokens to the new Token-2022 account
  let mint_to_ctx = CpiContext::new(
    ctx.accounts.token_program.to_account_info(),
    MintTo {
        mint: ctx.accounts.new_mint.to_account_info(),
        to: ctx.accounts.new_token_account.to_account_info(),
        authority: ctx.accounts.mint_authority.to_account_info(),
    },
  );
  token::mint_to(mint_to_ctx, ctx.accounts.legacy_token_account.amount)?;

  Ok(())
}

The general process is:

  1. Burn the tokens from the legacy account
  2. Mint an equal number of tokens to a new Token-2022 account
  3. (Not shown) Update any references to the old token account to point to the new one

Since Token-2022 is still a relatively new standard, we recommend thorough testing when migrating legacy tokens. But the good news is, once migrated, you'll be able to enjoy all the benefits Token-2022 has to offer!

Conclusion

Token-2022 is a major leap forward for Solana tokens, offering improvements like:

  • Modular extensions for custom token functionality
  • Native account support for simpler initialization
  • Decoupled authorities for granular permission control
  • Full backward compatibility with the original token program

While there is some work involved in migrating legacy tokens, we believe the benefits of Token-2022 far outweigh the costs for most projects. The standard is designed with the future of Solana tokens in mind.

As an Anchor developer, you're well-positioned to start taking advantage of Token-2022. The new features play nicely with Anchor's account and instruction abstractions, making it straightforward to integrate into your programs.

We're excited to see what the creative minds in the Solana ecosystem will build with Token-2022. From innovative DeFi products to entirely new use cases - the possibilities are endless.

So dive in, start experimenting, and don't be afraid to push the boundaries of what tokens can do!

And if you have any questions or need help with the migration, the Solana developer community is always ready to lend a hand. We're all in this together, building the future of decentralized finance and web3.

Until next time, happy coding!

Comments

Popular posts from this blog

CAP Theorem and blockchain

Length extension attack

Contract upgrade anti-patterns