Skip to content
On this page

Base classes

The FA2 library has three base classes that you can use as a foundation to develop your own smart contracts. Each class provides FA2-compliant functionality for a type of token:

  • main.Nft: Non-fungible tokens, which are unique digital assets
  • main.Fungible: Fungible tokens, which are interchangeable assets, like tez or other cryptocurrencies
  • main.SingleAsset: Single-asset tokens, which are a simplified case of fungible tokens, allowing only one token type per contract

You can import only one of these base classes in a single contract. Then you can add mixins to add features to the contract, such as the ability to mint and burn tokens.

The classes share a common entrypoints interface for transferring tokens, updating operators, etc; see entrypoints.

main.Nft

This class is for Non-Fungible Tokens (NFTs). In this case each token is distinct and thus not interchangeable ("fungible") with another. This makes it suitable for unique assets like digital artwork, real estate, or unique digital goods.

The main.Nft class utilizes a ledger of type sp.big_map[sp.nat, sp.address], mapping where the key represents the token id and the value signifies the address of the token owner. Thus every NFT is associated with a unique owner.

Use case: An artist wants to release a collection of digital art pieces as NFTs. Each artwork is represented as a unique token on the blockchain.

For an example, open the Example FA2 NFT contract in the online IDE.

main.Fungible

This class is designed for fungible tokens, which are interchangeable ("fungible") with each other. This type of token is useful when the individual units are essentially identical, such as with cryptocurrencies or utility tokens. Each unit of a certain token type holds the same value and properties.

Use case: A gaming company wants to issue multiple in-game currencies. For each currency, each unit holds the same value and properties.

For an example, open the Example FA2 fungible contract in the online IDE.

main.SingleAsset

This class is a specialized version of the main.Fungible class. It is used when your use-case involves only a single fungible token. main.SingleAsset is optimized for this specific scenario and can provide performance benefits over the more general main.Fungible. If you're creating a smart contract for a single cryptocurrency, this class could be the right choice.

Use case: A gaming company wants to issue a single in-game currency. Each unit of this currency holds the same value and properties.

For an example, open the Example FA2 single asset token contract in the online IDE.

Origination

The three classes takes the following parameters:

  • metadata (sp.big_map[sp.string, sp.bytes]): This is the contract's metadata bigmap. The metadata bigmap should never be empty. See contract metadata.

  • ledger: Used to create the ledger bigmap, it contains the pre-minted tokens. The ledger type varies based on the class - it's sp.map[sp.nat, sp.address] for Nft, sp.map[sp.pair[sp.address, sp.nat], sp.address] for Fungible, and sp.map[sp.address, sp.nat] for SingleAsset. Most of the time, this will be empty: {}.

  • token_metadata: Used to create the token metadata bigmap, see token metadata. It contains the pre-minted tokens' metadata. It's a list of metadata map: (sp.list[sp.map[sp.string, sp.bytes]]) for Nft and Fungible, or the unique metadata map (sp.map[sp.string, sp.bytes]) for SingleAsset. Most of the time, your contract is empty at the beginning so its value is [] or {}.

Examples with empty contract:

python
import smartpy as sp
from smartpy.templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("Nft", fa2.main)
    sc.h2("Without pre-minted tokens")
    c1 = fa2.main.Nft(sp.big_map(), {}, [])
    sc += c1
python
import smartpy as sp
from smartpy.templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("Fungible", fa2.main)
    sc.h2("Without pre-minted tokens")
    c1 = fa2.main.Fungible(sp.big_map(), {}, [])
    sc += c1
python
import smartpy as sp
from smartpy.templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("SingleAsset", fa2.main)
    sc.h2("Without pre-minted tokens")
    c1 = fa2.main.SingleAsset(sp.big_map(), {}, {})
    sc += c1

Example with pre-minted tokens

python
import smartpy as sp
from smartpy.templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("Nft", fa2.main)
    sc.h2("With pre-minted tokens")
    alice = sp.test_account("alice")
    bob = sp.test_account("bob")
    tok0_md = fa2.make_metadata(name="Token Zero", decimals=1, symbol="Tok0")
    tok1_md = fa2.make_metadata(name="Token One", decimals=1, symbol="Tok1")
    tok2_md = fa2.make_metadata(name="Token Two", decimals=1, symbol="Tok2")
    c1 = fa2.main.Nft(
        metadata = sp.big_map(),
        ledger = {
            0: alice.address,
            1: bob.address,
            2: alice.address,
        },
        token_metadata = [tok0_md, tok1_md, tok2_md]
    )
    sc += c1
python
import smartpy as sp
from smartpy.templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("Fungible", fa2.main)
    sc.h2("With pre-minted tokens")
    alice = sp.test_account("alice")
    tok0_md = fa2.make_metadata(name="Token Zero", decimals=1, symbol="Tok0")
    tok1_md = fa2.make_metadata(name="Token One", decimals=1, symbol="Tok1")
    tok2_md = fa2.make_metadata(name="Token Two", decimals=1, symbol="Tok2")
    c1 = fa2.main.Fungible(
        metadata = sp.big_map(),
        ledger = {
            (alice.address, 0): 42,
            (alice.address, 1): 42,
            (alice.address, 2): 42,
        },
        token_metadata = [tok0_md, tok1_md, tok2_md]
    )
    sc += c1
python
import smartpy as sp
from smartpy.templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("SingleAsset", fa2.main)
    sc.h2("With pre-minted tokens")
    alice = sp.test_account("alice")
    tok0_md = fa2.make_metadata(name="Token Zero", decimals=1, symbol="Tok0")
    c1 = fa2.main.SingleAsset(
        metadata=sp.big_map(),
        token_metadata=tok0_md,
        ledger={alice.address: 42}
    )
    sc += c1

Inheritance

In practice you should create your own class and inherit from one of the base classes. This allows you to add storage fields and override methods, thus modifying the behaviour of your tokens.

Example:

smartpy
class ExampleNft(main.Nft):
        def __init__(self, metadata, ledger, token_metadata):
            main.Nft.__init__(self, metadata, ledger, token_metadata)

        # ...
smartpy
class ExampleFungible(main.Fungible):
        def __init__(self, metadata, ledger, token_metadata):
            main.Fungible.__init__(self, metadata, ledger, token_metadata)

        # ...
smartpy
class ExampleSingleAsset(main.SingleAsset):
        def __init__(self, metadata, ledger, token_metadata):
            main.SingleAsset.__init__(self, metadata, ledger, token_metadata)

        # ...