Fan Tokens

What are Fan Tokens?

Fan tokens are a brand new phenomenon already making waves in the sports sector. A fan token is a cryptocurrency issued for the benefit of star performers — whether that’s a world-famous rock band, or an up-and-coming solo talent — and their fans.

Why does the music industry need fan tokens? Because they allow any act or artist to create their own economy, generating new ways to monetise their music and brand, and providing a unique and innovative channel to engage with fans.

BitSong’s Fan Token module allows artists to mint their own branded tokens for any purpose. But here are a few ways that they can be used:

Create a loyalty program allowing fan token holders privileged access to exclusive content such as unreleased materials or behind-the-scenes interviews

Crowdfund a tour or studio album and revenue-sharing with token holders

Give fans the opportunity to vote, for example, on the song lineup for a gig or on the cities for an upcoming tour

Accept fan tokens as payment for NFTs

The BitSong Fan Token module enables any artist to start minting their own fan tokens and list them within a few minutes, for low fees. BitSong also stands apart from other fan token platforms by offering the opportunity to link BitSong Fan Tokens to social profiles, such as Twitter.

Abstract

This document specifies the fantoken module of the BitSong chain.

The fantoken module enables the BitSong chain to support fan tokens, allowing actors in the content creation industry to create their economy. In this sense, they can generate new ways to monetize their music and brand and provide a unique and innovative channel to engage with fans. Thanks to this module, players from the content creation universe can start minting their fan tokens (which are fungible tokens) and listing them within a few minutes for low fees.

An example: Fan tokens in the music Industry

In the music industry, for example, fan tokens enable to empower a lot of different scenarios. For instance, it is possible to use them to crowdfund a tour or an album, or even to access exclusive content. The potential of such a system is very massive and, with these few examples, you can imagine what a contribution this tool can make to a world teeming with content creators.

Fan tokens in BitSong

Based on the concept of the ERC-20 Standard, BitSong fan tokens enable the user to a new way of value exchanging. Here, through tokens issued by a particular entity, the fans can deeply interact with their influencers or idols.

We can identify each fan token through its denom. Moreover, even if its denom allow the global identification of the token, each fan token is also equipped with a name and a symbol, which helps in its recognition. The name and the symbol of a fan token, together with a uri and an authority (i.e., the address of the wallet which is able to manage those data) are part of the metadata of the fan token.

More specifically:

  • denom is calculated by the tendermint crypto hash function through the block height of the transaction, the first minter, the symbol, and the name. For this reason, it is unique;

  • symbol is defined by the user and can be any string matching the pattern ^[a-z0-9]{1,64}$, so any lowercase string containing letters and digits with a length between 1 and 64 characters. It cannot be empty;

  • name, on the other hand, is also defined by the user but it can be any string containing max 128 characters. It can also be empty.

Finally, thanks to the fantoken module, users on BitSong can:

  • manage fan tokens, issuing, minting, burning, and transferring them;

  • build applications that use the fan tokens API to create completely new and custom artists' economies.

Features that may be added in the future are described in Future Improvements.

Concepts

Conventions

By looking at numbers, we separate the decimals by point and the thousands by comma. For instance, the number one thousand two hundred thirty-four and fifty-six hundredths, is written as:

Fan token

Fan tokens, conceptually based on the ERC-20 Standard, are fungible tokens issued for fan communities. They borns to create new connections between fans and any content creator, like star performers, actors, designers, musicians, photographers, writers, models, influencers, etc. They enable the growth of a private and (most importantly) custom economy creating new channels for fans' engagement. Fan tokens have enormous potential. By using them, you can build myriad applications allowing fans a deeper interaction in the artistic life of their top performers.

To provide you with some examples, you can think that it is possible to use them for creating loyalty programs to provide privileged access to exclusive content. To allow your fan to crowdfund a tour or studio album and share part of the revenue with your fans. To enable your fans with the opportunity to vote on the cities for an upcoming tour. Or even to accept fan tokens as payment for NFTs.

In the design of the fan token functionalities, big part of the reasonings were based on the OpenZeppelin standard. For example, the concept of burning the tokens lowering the totalSupply directly derives from the standard documentation.

A fan token is characterized by:

Attribute
Type
Description

denom

string

It is an hash calculated on the first Minter, the Symbol, the Name and the Block Height of the issuing transaction of the fan token. It is the hash identifying the fan token and is used to prevent the creation of identical tokens. Moreover, to fastly identify a fan token from its denom, it starts with the prefix ft.

max_supply

sdk.Int

Minter

sdk.AccAddress

It is the address of the minter for the fan token. It can be changed to trasfer the minting ability of the token during the time.

metadata

Metadata

It is generated once and it is made up of Name, Symbol, URI and Authority (i.e., is the address of the wallet which is able to perform edits on the URI). More specifically, the URI contains a link to a resource with a set of information linked to the fan token.

Metadata are characterized by:

Attribute
Type
Description

name

string

It is chosen once by the user. It should correspond to the long name the user want to associate to the symbol (e.g., Dollar, Euro, BitSong). It can also be empty and its max length is of 128 characters.

symbol

string

uri

string

It is a link to a resource which contains a set of information linked to the fan token. It can also be empty and its max length is of 512 characters.

authority

sdk.AccAddress

It is the address of the authority for the fan token metadata managment. It can be changed to trasfer the ability of changing the metadata the token during the time.

Lifecycle of a fan token

It is possible to entirely represent the lifecycle of a fan token through Finite State Machine (FSM) diagrams. We will present two representations:

  • the first refers to the fan token object. We can compare such a definition with that of currency (e.g., Euro, Dollar, BitSong);

  • the second, instead, is referred to the lifecycle of the fan token instance. Such definition is comparable with that of coin/money (e.g., the specific 1 Euro coin you could have in your pocket at a particular moment in time).

We can describe the lifecycle of a fan token object through two states.

Referring to the figure above, as detailed in the documentation, to "create" the fan token, we need to issue it. This operation leads to the birth of the object and thus to its first state, state 1. Here, the token is related to a minter, who is able to mint the token to different wallets, and an authority, that is responsible for managing the metadata. It is important to recall that some operations are reversible, while some others are not. For example, reaching the max-supply through minting operations, can be reverted by burning tokens. While, for example, the selection of an empty address for the minter (which strictly means disable minting operations) is a irreversible operation.

Referring to the lifecycle of a fan token instance, it is possible to identify two states.

Concerning to the figure above, when the fan token object is issued, we can mint it. Minting leads to the birth of a new instance, moving the fan token instance to state 1. In this state, the token can be:

  • traded, which produces the changing of the owner of the instance, without modifying the landing state. To make it clearer, it can be considered as the simple exchange of money between two users. This does not modify the landing state;

  • burned, which produces a state change to the state 2, where the authority cannot operate on the fan token instance anymore.

Uniqueness of the denom

The denom is calculated on first Minter, Symbol, Name and Block Height of the issuing transaction of the fan token.

func GetFantokenDenom(height int64, minter sdk.AccAddress, symbol, name string) string {
	bz := []byte(fmt.Sprintf("%d%s%s%s", height, minter.String(), symbol, name))
	return "ft" + tmcrypto.AddressHash(bz).String()
}

The denom of every fan token starts with the prefix ft. Follows a hash of Block Height, first Minter, Symbol and Name of the fan token. This denom is used as base denom for the fan token, and, for this reason, it should be unique. In this sense, since the hash depends both on the first Minter and the Block Height, multiple fan tokens with the same name and symbol can co-exist even created by the same address but they must be created from transactions in different blocks.

State

The fantoken module keeps track of parameters and fan tokens.

Params:			types.Params
FanTokens:		[]types.FanToken

Params

In the state definition, we can find the Params. This section corresponds to a module-wide configuration structure that stores system parameters. In particular, it defines the overall fantoken module functioning and contains the issueFee, mintFee and burnFee for the fan token. Such an implementation allows governance to decide the issue fee, but also the mint and burn fees the users have to pay to perform these operations with the tokens, in an arbitrary way - since proposals can modify it.

type Params struct {
	IssueFee	sdk.Coin
	MintFee		sdk.Coin
	BurnFee		sdk.Coin
}

Fantoken

The state contains a list of Fantokens. They are fan tokens (fungible tokens deriving by the ERC-20 Standard), and their state information is:

  • Denom, that corresponds to the identifier of the fan token. It is a string, automatically calculated on the first Minter, Symbol, Name and Block Height of the issuing transaction of the fan token as explained in concepts, and cannot change for the whole life of the token;

  • Minter, which corresponds to the address of the current minter for the token. It is an address and can change during the token lifecycle thanks to the minting ability transfer. When the minter address is set to an empty value, the token can be minted no more;

  • MetaData, which contains metadata for the fan token and is made up of the Name, the Symbol, a URI and an Authority as described in concepts.

More specifically, the metadata can change during the life of the token according to:

  • URI can be changed by the authority. It can be changed until when the authority is available;

  • Authority which can be transferred by the current authority until when the authority itself is not set to an empty value.

type FanToken struct {
	Denom		string
	MaxSupply	sdk.Int
	Minter		string
	MetaData	types.Metadata
}

type Metadata struct {
	Name		string
	Symbol      string
	URI         string
	Authority	string
}

Messages

Messages (msgs) are objects that trigger state transitions. Messages are wrapped in transactions (txs) that clients submit to the network. The BitSong SDK wraps and unwraps fantoken module messages from transactions.

MsgIssue

type MsgIssue struct {
	Symbol			string
	Name			string
	MaxSupply		sdk.Int
	Authority		string
	URI				string
	Minter			string
}

MsgDisableMint

The MsgDisableMint message is used to irreversibly disable the minting ability for an existing fan token. It takes as input Denom and Minter (described in fan token definition). Thanks to these values, the module can verify whether the modifications are lawful (i.e., requested by the Minter and in accord with the state transition definition). The message permits to change the "mintability" of the fan token. In particular, at the issuing, the fan token can be minted (in fact the Minter address is a value different from an empty one). Later on, during the lifecycle of the fan token, the minter can disable the possibility to mint new tokens (check the relative docs for more details). In such a scenario, it is possible to disable the mintability, by set an empty value as the address for the new minter) and, this operation, causes the MaxSupply of the token to be updated at the current value of the supply. At this point, an EventDisableMint event is emitted.

type MsgDisableMint struct {
	Denom			string
	Minter			string
}

MsgMint

The MsgMint message is used to mint an existing fan token. It takes as input Recipient, Coin, and Minter (all described in fan token definition except the Coin, which is an object made up of the denom of the fan token to mint and its quantity, expressed in micro unit). In such a message, the Recipient is not required and its default value is the same of Minter. Thanks to these values, the module can verify whether the minting operation is lawful (i.e., requested: by the minter, on a mintable fan token, and for a quantity that allow to do not overcome the maximum supply), recalling that only the minter for of the fan token can mint the token to any specified account. At this point, the token is minted, the supply is increased, the coins are sent to the recipient, the module deduct the mint fee from the minter wallet and an EventMint event is emitted.

type MsgMint struct {
	Recipient		string
	Coin			sdk.Coin
	Minter			string
}

MsgBurn

The MsgBurn message is used to burn fan token. It takes as input Coin, and Sender (as above, the Coin is an object made up of the denom of the fan token to burn and its quantity, expressed in micro unit, while Sender must be equal to the user who want to burn the tokens). The module can verify whether the burning operation is lawful (i.e., the sender has a sufficient amount of token, in other words check if sender balance > amount to burn). At this point, the token is burned, the supply is lowered, the module deduct the burn fee from the owner wallet and an EventBurn event is emitted. In such a way, that specific token ends its lifecycle, as shown in the relative docs.

type MsgBurn struct {
	Coin	sdk.Coin
	Sender	string
}

MsgSetAuthority

The MsgSetAuthority message is used to transfer or disable the ability to change the metadata of a fan token. It takes as input Denom, oldAuthority, and newAuthority (Denom is described in fan token definition, old and new Authorities are respectively the actual and the new addresses of the wallet who are able to change the metadata of the token). When the newAuthority is an empty address, the capability to change the metadata is irreversibly disabled. The module can verify whether the operation is lawful (i.e., the requesting account is actually the authority for the fan token, the fan token metadata can be changed and the destination account is neither blocked nor a module account). At this point, if the newAuthority is a not empty address, it becomes the new token authority. On the other hand, the fan token metadata cannot be changed anymore. Anyway, an EventSetAuthority event is emitted. This operation enable the authority transfer transition described in the lifecycle of a fan token.

type MsgTransferAuthority struct {
	Denom		string
	oldAuthority	string
	newAuthority	string
}

MsgSetMinter

The MsgSetMinter message is used to transfer the ability to mint a fan token. It takes as input Denom, oldMinter, and newMinter (Denom is described in fan token definition, old and new Minters are respectively the actual and the new addresses of the wallet who are able to mint the token). When the newMinter is an empty address, it works as the MsgDisableMint. The module can verify whether the operation is lawful (i.e., the requesting account is actually the minter for the fan token, the fan token can be minted and the destination account is neither blocked nor a module account). At this point, if the newMinter is a not empty address, it becomes the new token minter. On the other hand, the fan token cannot be minted anymore. Anyway, an EventSetMinter event is emitted. This operation enable the minter transfer transition described in the lifecycle of a fan token.

type MsgSetMinter struct {
	Denom		string
	oldMinter	string
	newMinter	string
}

MsgSetUri

The MsgSetMinter message is used to modify the URI in the fan token metadata. It takes as input Denom, new URI, and Authority (Denom and URI are described in fan token definition, Authority is the actual address of the wallet who is able to modify the fan token metadata). The module can verify whether the operation is lawful (i.e., the requesting account is actually the authority for the fan token, the fan token metadata can be changed and the new uri is a valid one, as described in Fan Token parameters definition). At this point, an EventSetUri event is emitted.

type MsgSetUri struct {
	Denom		string
	URI		string
	Authority	string
}

Events

The fantoken module emits the following events:

EventIssue

Type
Attribute Key
Attribute Value

message

action

/bitsong.fantoken.v1beta1.MsgIssue

bitsong.fantoken.v1beta1.EventIssue

denom

{denom}

EventDisableMint

Type
Attribute Key
Attribute Value

message

action

/bitsong.fantoken.v1beta1.MsgDisableMint

bitsong.fantoken.v1beta1.EventDisableMint

denom

{denom}

EventMint

Type
Attribute Key
Attribute Value

message

action

/bitsong.fantoken.v1beta1.MsgMint

bitsong.fantoken.v1beta1.EventMint

recipient

{recipient}

bitsong.fantoken.v1beta1.EventMint

coin

{coin}

EventBurn

Type
Attribute Key
Attribute Value

message

action

/bitsong.fantoken.v1beta1.MsgBurn

bitsong.fantoken.v1beta1.EventBurn

sender

{sender}

bitsong.fantoken.v1beta1.EventBurn

coin

{coin}

EventSetAuthority

Type
Attribute Key
Attribute Value

message

action

/bitsong.fantoken.v1beta1.MsgSetAuthority

bitsong.fantoken.v1beta1.EventTransferAuthority

denom

{denom}

bitsong.fantoken.v1beta1.EventTransferAuthority

old_authority

{old_authority}

bitsong.fantoken.v1beta1.EventTransferAuthority

new_authority

{new_authority}

EventSetMinter

Type
Attribute Key
Attribute Value

message

action

/bitsong.fantoken.v1beta1.MsgSetMinter

bitsong.fantoken.v1beta1.EventTransferMinter

denom

{denom}

bitsong.fantoken.v1beta1.EventTransferMinter

old_minter

{old_minter}

bitsong.fantoken.v1beta1.EventTransferMinter

new_authority

{new_minter}

EventSetUri

Type
Attribute Key
Attribute Value

message

action

/bitsong.fantoken.v1beta1.MsgSetUri

bitsong.fantoken.v1beta1.EventSetUri

denom

denom}

Parameters

Fantoken module parameters.

Key
Type
Value

IssueFee

sdk.Coin

{"denom": "ubtsg", "amount": "1000000"}

MintFee

sdk.Coin

{"denom": "ubtsg", "amount": "0"}

BurnFee

sdk.Coin

{"denom": "ubtsg", "amount": "0"}

Client

Transactions

The transactions commands allow users to issue, mint, burn, disable minting, transfer minting and editing capabilities for fan tokens.

bitsongd tx fantoken --help

issue

bitsongd tx fantoken issue \
    --name "fantoken name" \
    --symbol "bitangel" \
    --max-supply 100000000000 \
    --uri "ipfs://...." \
    --from <key-name> -b block --chain-id <chain-id> --fees <fee>

mint

bitsongd tx fantoken mint [amount][denom] \
    --recipient <address> \
    --from <key-name> -b block --chain-id <chain-id> --fees <fee>

burn

bitsongd tx fantoken burn [amount][denom] \
    --from <key-name> -b block --chain-id <chain-id> --fees <fee>

set-authority

bitsongd tx fantoken set-authority [denom] \
    --new-authority <address> \
    --from <key-name> -b block --chain-id <chain-id> --fees <fee>

set-minter

bitsongd tx fantoken set-minter [denom] \
    --new-minter <address> \
    --from <key-name> -b block --chain-id <chain-id> --fees <fee>

set-uri

bitsongd tx fantoken set-uri [denom] \
    --uri <uri> \
    --from <key-name> -b block --chain-id <chain-id> --fees <fee>

disable-mint

bitsongd tx fantoken disable-mint [denom] \
    --from <key-name> -b block --chain-id <chain-id> --fees <fee>

Query

The query commands allow users to query the fantoken module.

bitsongd q fantoken --help

denom

bitsongd q fantoken denom <denom>

authority

bitsongd q fantoken authority <address>

params

bitsongd q fantoken params

Last updated