Tokens

Solana SPL Tokens & NFTs


Tokens in Nautilus programs can be represented in a variety of ways, including two useful aggregator objects:

  • Token will allow you to create SPL tokens and their metadata all in one command
  • Nft is similar to Token but with some added features and functionality specific to NFTs

Let's start out by considering a Mint, which is simply an SPL token mint without any associated metadata.

You can create a Mint the same way you'd create any other Nautilus object - like the Wallet example in the previous section:

use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn create_mint<'a>(
        mut new_mint: Create<'a, Mint<'a>>,
        decimals: u8,
        mint_authority: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_mint.create(decimals, mint_authority.clone(), Some(mint_authority))
    }

    fn create_mint_with_payer<'a>(
        mut new_mint: Create<'a, Mint<'a>>,
        decimals: u8,
        mint_authority: Signer<Wallet<'a>>,
        rent_payer: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_mint.create_with_payer(
            decimals,
            mint_authority.clone(),
            Some(mint_authority),
            rent_payer,
        )
    }
}

The account state data for Mint objects uses the SPL Token state for Mint. Here's an example of reading the data of a Mint:

use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn read_mint(mint: Mint) -> ProgramResult {

        info!(" * Mint Public Key: {}", &mint.key());
        print_mint_data(&mint.data);
        Ok(())
    }
}

fn print_mint_data(data: &MintState) {
    info!(" * Mint Data:");
    info!("      Mint Authority:         {:#?}", data.mint_authority);
    info!("      Supply:                 {}", data.supply);
    info!("      Decimals:               {}", data.decimals);
    info!("      Is Initialized:         {}", data.is_initialized);
    info!("      Freeze Authority:       {:#?}", data.freeze_authority);
}

Once you've created a Mint, or if you have some other existing SPL token mint, you can create only the Metadata for a token like so:

use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn create_metadata<'a>(
        mut new_metadata: Create<'a, Metadata<'a>>,
        mint: Mint<'a>,
        title: String,
        symbol: String,
        uri: String,
        mint_authority: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_metadata.create(
            title,
            symbol,
            uri,
            mint,
            mint_authority.clone(),
            mint_authority,
        )
    }

    fn create_metadata_with_payer<'a>(
        mut new_metadata: Create<'a, Metadata<'a>>,
        mint: Mint<'a>,
        title: String,
        symbol: String,
        uri: String,
        mint_authority: Signer<Wallet<'a>>,
        rent_payer: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_metadata.create_with_payer(
            title,
            symbol,
            uri,
            mint,
            mint_authority.clone(),
            mint_authority,
            rent_payer,
        )
    }
}

This will effectively create metadata for any SPL token so long as you are authorized to do so.

The account state data for Metadata objects uses the Metaplex state for Metadata. Here's an example of reading the data of a Metadata:

use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn read_metadata(metadata: Metadata) -> ProgramResult {

        info!(" * Metadata Public Key: {}", &metadata.key());
        print_metadata_data(&metadata.data);
        Ok(())
    }
}

fn print_metadata_data(data: &MetadataState) {
    info!(" * Metadata Data:");
    info!("      Mint:                   {:#?}", data.mint);
    info!(
        "      Primary Sale Happened:  {}",
        data.primary_sale_happened
    );
    info!("      Is Mutable:             {}", data.is_mutable);
    info!("      Edition Nonce:          {:#?}", data.edition_nonce);
    info!("      Title:                  {}", data.data.name);
    info!("      Symbol:                 {}", data.data.symbol);
    info!("      URI:                    {}", data.data.uri);
}

Let's now explore the Token object, which combines these two types of accounts so you can create tokens with metadata using one object.

use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn create_token<'a>(
        mut new_token: Create<'a, Token<'a>>,
        decimals: u8,
        title: String,
        symbol: String,
        uri: String,
        mint_authority: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_token.create(
            decimals,
            title,
            symbol,
            uri,
            mint_authority.clone(),
            mint_authority.clone(),
            Some(mint_authority),
        )
    }

    fn create_token_with_payer<'a>(
        mut new_token: Create<'a, Token<'a>>,
        decimals: u8,
        title: String,
        symbol: String,
        uri: String,
        mint_authority: Signer<Wallet<'a>>,
        rent_payer: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_token.create_with_payer(
            decimals,
            title,
            symbol,
            uri,
            mint_authority.clone(),
            mint_authority.clone(),
            Some(mint_authority),
            rent_payer,
        )
    }
}

Token combines a Mint account and a Metadata account, so you can access both accounts' state like you would in the examples above:

use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn read_token(token: Token) -> ProgramResult {

        info!(" * Token Public Key: {}", &token.key());
        print_mint_data(&token.mint.data);
        print_metadata_data(&token.metadata.data);
        Ok(())
    }
}

fn print_mint_data(data: &MintState) {
    info!(" * Mint Data:");
    info!("      Mint Authority:         {:#?}", data.mint_authority);
    info!("      Supply:                 {}", data.supply);
    info!("      Decimals:               {}", data.decimals);
    info!("      Is Initialized:         {}", data.is_initialized);
    info!("      Freeze Authority:       {:#?}", data.freeze_authority);
}

fn print_metadata_data(data: &MetadataState) {
    info!(" * Metadata Data:");
    info!("      Mint:                   {:#?}", data.mint);
    info!(
        "      Primary Sale Happened:  {}",
        data.primary_sale_happened
    );
    info!("      Is Mutable:             {}", data.is_mutable);
    info!("      Edition Nonce:          {:#?}", data.edition_nonce);
    info!("      Title:                  {}", data.data.name);
    info!("      Symbol:                 {}", data.data.symbol);
    info!("      URI:                    {}", data.data.uri);
}

Lastly, Nft behaves very similar to Token, with a few important features:

  • Minting is disabled after 1
  • Metadata can be expanded to MasterEdition, etc.
use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn create_nft<'a>(
        mut new_nft: Create<'a, Nft<'a>>,
        title: String,
        symbol: String,
        uri: String,
        mint_authority: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_nft.create(
            title,
            symbol,
            uri,
            mint_authority.clone(),
            mint_authority.clone(),
            Some(mint_authority),
        )
    }

    fn create_nft_with_payer<'a>(
        mut new_nft: Create<'a, Nft<'a>>,
        title: String,
        symbol: String,
        uri: String,
        mint_authority: Signer<Wallet<'a>>,
        rent_payer: Signer<Wallet<'a>>,
    ) -> ProgramResult {

        new_nft.create_with_payer(
            title,
            symbol,
            uri,
            mint_authority.clone(),
            mint_authority.clone(),
            Some(mint_authority),
            rent_payer,
        )
    }
}

Just like Token, you can access the NFT's state data like so:

use nautilus::*;

#[nautilus]
mod program_nautilus {

    fn read_nft(nft: Nft) -> ProgramResult {

        info!(" * NFT Public Key: {}", &nft.key());
        print_mint_data(&nft.mint.data);
        print_metadata_data(&nft.metadata.data);
        Ok(())
    }
}

fn print_mint_data(data: &MintState) {
    info!(" * Mint Data:");
    info!("      Mint Authority:         {:#?}", data.mint_authority);
    info!("      Supply:                 {}", data.supply);
    info!("      Decimals:               {}", data.decimals);
    info!("      Is Initialized:         {}", data.is_initialized);
    info!("      Freeze Authority:       {:#?}", data.freeze_authority);
}

fn print_metadata_data(data: &MetadataState) {
    info!(" * Metadata Data:");
    info!("      Mint:                   {:#?}", data.mint);
    info!(
        "      Primary Sale Happened:  {}",
        data.primary_sale_happened
    );
    info!("      Is Mutable:             {}", data.is_mutable);
    info!("      Edition Nonce:          {:#?}", data.edition_nonce);
    info!("      Title:                  {}", data.data.name);
    info!("      Symbol:                 {}", data.data.symbol);
    info!("      URI:                    {}", data.data.uri);
}
Previous
Wallets
Next
Tables