This documentation comes from the README of
the flow-ft github repository. Please visit that repo to see more detailed documentation and examples.
Fungible Token Standard
This is a description of the Flow standard for fungible token contracts. It is meant to contain the minimum requirements to implement a safe, secure, easy to understand, and easy to use fungible token contract. It also includes an example implementation to show how a concrete smart contract would actually implement the interface.
The version of the contracts in the master branch is the
Cadence 1.0 version of the contracts and is not the same
as the ones that are currently deployed to testnet and mainnet.
See the cadence-0.42 branch for the currently deployed versions.
Import Addresses​
The FungibleToken, FungibleTokenMetadataViews, and FungibleTokenSwitchboard contracts are already deployed
on various networks. You can import them in your contracts from these addresses.
There is no need to deploy them yourself.
| Network | Contract Address |
|---|---|
| Emulator | 0xee82856bf20e2aa6 |
| PreviewNet | 0xa0225e7000ac82a9 |
| Testnet/Crescendo | 0x9a0766d93b6608b7 |
| Sandboxnet | 0xe20612a0776ca4bf |
| Mainnet | 0xf233dcee88fe0abe |
The Burner contract is also deployed to these addresses, but should not be used until after the Cadence 1.0 network upgrade.
Basics of the Standard:​
The code for the standard is in contracts/FungibleToken.cdc. An example implementation of the standard that simulates what a simple token would be like is in contracts/ExampleToken.cdc.
The exact smart contract that is used for the official Flow Network Token (FlowToken) is in the flow-core-contracts repository.
Example transactions that users could use to interact with fungible tokens are located in the transactions/ directory. These templates are mostly generic and can be used with any fungible token implementation by providing the correct addresses, names, and values.
The standard consists of a contract interface called FungibleToken that defines important
functionality for token implementations. Contracts are expected to define a resource
that implement the FungibleToken.Vault resource interface.
A Vault represents the tokens that an account owns. Each account that owns tokens
will have a Vault stored in its account storage.
Users call functions on each other's Vaults to send and receive tokens.
The standard uses unsigned 64-bit fixed point numbers UFix64 as the type to represent token balance information. This type has 8 decimal places and cannot represent negative numbers.
Core Features (All contained in the main FungibleToken interface)​
Balance Interface​
Specifies that the implementing type must have a UFix64 balance field.
access(all) var balance: UFix64
Provider Interface​
Defines a withdraw function for withdrawing a specific amount of tokens amount.
access(all) fun withdraw(amount: UFix64): @{FungibleToken.Vault}- Conditions
- the returned Vault's balance must equal the amount withdrawn
- The amount withdrawn must be less than or equal to the balance
- The resulting balance must equal the initial balance - amount
- Conditions
- Users can give other accounts a reference to their
Vaultcast as aProviderto allow them to withdraw and send tokens for them. A contract can define any custom logic to govern the amount of tokens that can be withdrawn at a time with aProvider. This can mimic theapprove,transferFromfunctionality of ERC20. FungibleToken.Withdrawnevent- Event that is emitted automatically to indicate how much was withdrawn
and from what account the
Vaultis stored in. If theVaultis not in account storage when the event is emitted,fromwill benil. - Contracts do not have to emit their own events, the standard events will automatically be emitted.
- Event that is emitted automatically to indicate how much was withdrawn
and from what account the
Defines an isAvailableToWithdraw() function
to ask a Provider if the specified number of tokens can be withdrawn from the implementing type.
Receiver Interface​
Defines functionality to depositing fungible tokens into a resource object.
deposit()function:access(all) fun deposit(from: @{FungibleToken.Vault})- Conditions
frombalance must be non-zero- The resulting balance must be equal to the initial balance + the balance of
from
- It is important that if you are making your own implementation of the fungible token interface that
you cast the input to
depositas the type of your token.let vault <- from as! @ExampleToken.VaultThe interface specifies the argument as@FungibleToken.Vault, any resource that satisfies this can be sent to the deposit function. The interface checks that the concrete types match, but you'll still need to cast theVaultbefore storing it.
- deposit event
FungibleToken.Depositedevent from the standard that indicates how much was deposited and to what account theVaultis stored in.- If the
Vaultis not in account storage when the event is emitted,towill benil. - This event is emitted automatically on any deposit, so projects do not need to define and emit their own events.
- If the
Defines Functionality for Getting Supported Vault Types
- Some resource types can accept multiple different vault types in their deposit functions,
so the
getSupportedVaultTypes()andisSupportedVaultType()functions allow callers to query a resource that implementsReceiverto see if theReceiveraccepts their desiredVaulttype in its deposit function.
Users could create custom Receivers to trigger special code when transfers to them happen,
like forwarding the tokens to another account, splitting them up, and much more.
Vault Interface​
Interface that inherits from Provider, Receiver, Balance, ViewResolver.Resolver,
and Burner.Burnable and provides additional pre and post conditions.
The ViewResolver.Resolver interface defines functionality for retrieving metadata
about a particular resource object. Fungible Token metadata is described below.
See the comments in the Burner contract for context about it.
Basically, it defines functionality for tokens to have custom logic when those tokens
are destroyed.
Creating an empty Vault resource​
Defines functionality in the contract to create a new empty vault of of the contract's defined type.
access(all) fun createEmptyVault(vaultType: Type): @{FungibleToken.Vault}- Defined in the contract
- To create an empty
Vault, the caller calls the function and provides the Vault Type that they want. They get a vault back and can store it in their storage. - Conditions:
- the balance of the returned Vault must be 0
Comparison to Similar Standards in Ethereum​
This spec covers much of the same ground that a spec like ERC-20 covers, but without most of the downsides.
- Tokens cannot be sent to accounts or contracts that don't have owners or don't understand how to use them, because an account has to have a
Vaultin its storage to receive tokens. Nosafetransferis needed. - If the recipient is a contract that has a stored
Vault, the tokens can just be deposited to that Vault without having to do a clunkyapprove,transferFrom - Events are defined in the contract for withdrawing and depositing, so a recipient will always be notified that someone has sent them tokens with the deposit event.
- The
approve,transferFrompattern is not included, so double spends are not permitted - Transfers can trigger actions because users can define custom
Receiversto execute certain code when a token is sent. - Cadence integer types protect against overflow and underflow, so a
SafeMath-equivalent library is not needed.
FT Metadata​
FT Metadata is represented in a flexible and modular way using both the standard proposed in FLIP-0636 and the standard proposed in FLIP-1087.
A guide for NFT metadata is provided on the docs site. Many of the concepts described there also apply to fungible tokens, so it is useful to read for any Cadence developer.
When writing an FT contract interface, your contract will implement
the FungibleToken contract interface which already inherits
from the ViewResolver contract interface,
so you will be required to implement the metadata functions.
Additionally, your Vault will also implement the ViewResolver.Resolver by default,
which allows your Vault resource to implement one or more metadata types called views.
Views do not specify or require how to store your metadata, they only specify the format to query and return them, so projects can still be flexible with how they store their data.
Fungible token Metadata Views​
The FungibleTokenMetadataViews contract defines four new views that can used to communicate any fungible token information:
FTView: A view that wraps the two other views that actually contain the data.FTDisplay: The view that contains all the information that will be needed by other dApps to display the fungible token: name, symbol, description, external URL, logos and links to social media.FTVaultData: The view that can be used by other dApps to interact programmatically with the fungible token, providing the information about the public and private paths used by default by the token, the public and private linked types for exposing capabilities and the function for creating new empty vaults. You can use this view to setup an account using the vault stored in other account without the need of importing the actual token contract.TotalSupply: Specifies the total supply of the given token.
How to implement metadata​
The Example Token contract shows how to implement metadata views for fungible tokens.
How to read metadata​
In the flow-ft github repository you can find examples on how to read metadata, accessing the ExampleToken display (name, symbol, logos, etc.) and its vault data (paths, linked types and the method to create a new vault).
Latter using that reference you can call methods defined in the Fungible Token Metadata Views contract that will return you the structure containing the desired information.
How to use the Fungible Token contract
To use the Flow Token contract as is, you need to follow these steps:
- If you are using any network or the playground, there is no need to deploy
the
FungibleTokendefinition to accounts yourself. It is a pre-deployed interface in the emulator, testnet, mainnet, and playground and you can import definition from those accounts:0xee82856bf20e2aa6on emulator0x9a0766d93b6608b7on testnet0xf233dcee88fe0abeon mainnet
- Deploy the
ExampleTokendefinition, making sure to import theFungibleTokeninterface. - You can use the
get_balance.cdcorget_supply.cdcscripts to read the balance of a user'sVaultor the total supply of all tokens, respectively. - Use the
setup_account.cdcon any account to set up the account to be able to useExampleToken. - Use the
transfer_tokens.cdctransaction file to send tokens from one user with aVaultin their account storage to another user with aVaultin their account storage. - Use the
mint_tokens.cdctransaction with the admin account to mint new tokens. - Use the
burn_tokens.cdctransaction with the admin account to burn tokens. - Use the
create_minter.cdctransaction to create a new MintandBurn resource and store it in a new Admin's account.
Fungible Token Switchboard
FungibleTokenSwitchboard.cdc, allows users to receive payments
in different fungible tokens using a single &{FungibleToken.Receiver}
placed in a standard receiver path /public/GenericFTReceiver.
The switchboard routes received tokens to the correct vault in the owner's account.
You can see more documentation about the switchboard in the flow-ft github repo.