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 of these classes cater to different token standards and behaviors, thus, offering the flexibility to design a contract that suits your specific needs.

The base classes

Upon inheriting from any of the base classes you're provided with a standard FA2 contract.

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 adddress 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 will be represented as a unique token on the blockchain.

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. If you're aiming to create a token where each unit holds the same value and properties, main.Fungible could be your base class of choice.

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

main.SingleAsset

This class is a specialized version of the main.Fungible class. It is used when your use-case only involves 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.

Origination

Apart from the specialized ledger the three classes share a common interface (see entrypoints) and their constructor 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 templates import fa2_lib as fa2

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

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

@sp.add_test()
def test():
    sc = sp.test_scenario("SingleAsset", [fa2.t, 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 templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("Nft", [fa2.t, 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 templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("Fungible", [fa2.t, 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 templates import fa2_lib as fa2

@sp.add_test()
def test():
    sc = sp.test_scenario("SingleAsset", [fa2.t, 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)

        # ...