How to Create a Fungible Token on Flow | Flow Developer Portal (2024)

info

This guide is an in-depth tutorial on launching a Fungible Token contract from scratch. To launch in 2 minutes using a tool check out Toucans

What are Fungible Tokens?

Fungible tokens are digital assets that are interchangeable and indistinguishable with other tokens of the same type. This means that each token is identical in specification to every other token in circulation. Think of them like traditional money; every dollar bill has the same value as every other dollar bill. Fungible tokens play a crucial role in web3 ecosystems, serving as both a means of payment and an incentive for network participation. They can take on various roles including currencies, structured financial instruments, shares of index funds, and even voting rights in decentralized autonomous organizations.

Vaults on Flow

On the Flow blockchain and in the Cadence programming language,fungible tokens are stored in structures called resources.Resources are objects in Cadence that store data,but have special restrictions about how they can be stored and transferred,making them perfect for representing digital objects with real value.

You can learn more about resources in the Cadence documentationand tutorials.

For fungible tokens specifically, tokens are represented by a resource type called a Vault:


_10

access(all) resource interface Vault {

_10

_10

/// Field that tracks the balance of a vault

_10

access(all) var balance: UFix64

_10

_10

}


Think of a Vault as a digital piggy bank.Users who own fungible tokens store vault objects that track their balancesdirectly in their account storage. This is opposed to languagesthat track user balances in a central ledger smart contract.

When you transfer tokens from one vault to another:

  1. The transferor's vault creates a temporary vault holding the transfer amount.
  2. The original vault's balance decreases by the transfer amount.
  3. The recipient's vault receives the tokens from the temporary vaultand adds the temporary vault's balance to the its own balance.
  4. The temporary vault is then destroyed.

This process ensures secure and accurate token transfers on the Flow blockchain.

Fungible Token Standard

The Fungible Token Standard defines how a fungible token should behave on Flow.Wallets and other platforms need to recognize these tokens,so they adhere to a specific interface, which defines fields like balance,totalSupply, withdraw functionality, and more.This interface ensures that all fungible tokens on Flow have a consistent structure and behavior.Clink the link to the fungible token standard to see the full standardand learn about specific features and requirements.

Learn more about interfaces here.

Setting Up a Project

To start creating a Fungible Token on the Flow blockchain, you'll first need some tools and configurations in place.

Installing Flow CLI

The Flow CLI (Command Line Interface) provides a suite of tools that allow developers to interact seamlessly with the Flow blockchain.

If you haven't installed the Flow CLI yet and have Homebrew installed,you can run brew install flow-cli. If you don’t have Homebrew,please follow the installation guide here.

Initializing a New Project

💡 Note: Here is a link to the completed code if you want to skip ahead or reference as you follow along.

Once you have the Flow CLI installed, you can set up a new projectusing the flow setup command. This command initializesthe necessary directory structure and a flow.json configuration file(a way to configure your project for contract sources, deployments, accounts, and more):


_10

flow setup FooToken


Upon execution, the command will generate the following directory structure:


_10

/cadence

_10

/contracts

_10

/scripts

_10

/transactions

_10

/tests

_10

flow.json


Now, navigate into the project directory:


_10

cd FooToken


In our configuration file, called flow.json, for the network we want to use,we are going to state the address the FungibleToken contract is deployedto via aliases in a new contracts section. Since it is a standard contract,it has already been deployed to the emulator, a tool that runs and emulatesa local development version of the Flow Blockchain, for us.You can find addresses for other networks, like Testnet and Mainnet, on the Fungible Token Standard repo.

We'll also need to add the addresses for ViewResolver, MetadataViews,and FungibleTokenMetadataViews, which are other important contracts to use.These contracts are deployed to the Flow emulator by default,so there is not need to copy their code into your repo.The addresses below are the addresses in the emulator that your contractwill import them from.


_22

"contracts": {

_22

"FungibleToken": {

_22

"aliases": {

_22

"emulator": "0xee82856bf20e2aa6"

_22

}

_22

},

_22

"FungibleTokenMetadataViews": {

_22

"aliases": {

_22

"emulator": "0xee82856bf20e2aa6"

_22

}

_22

},

_22

"ViewResolver": {

_22

"aliases": {

_22

"emulator": "0xf8d6e0586b0a20c7"

_22

}

_22

},

_22

"MetadataViews": {

_22

"aliases": {

_22

"emulator": "0xf8d6e0586b0a20c7"

_22

}

_22

}

_22

}


Writing Our Token Contract

Next let's create a FooToken contract at cadence/contract/FooToken.cdc using the boilerplate generate command from the Flow CLI:


_10

flow generate contract FooToken


This will create a new file called FooToken.cdc in the contracts directory. Let's open it up and add some code.

In this contract file, we want to import our FungibleToken contract that we've defined in flow.json.


_10

import "FungibleToken"


In this same file, let's create our contract which implements the FungibleToken contract interface (it does so by setting it following the FooToken:).We'll also include fields for standard storage and public pathsfor our resource definitions.In our init — which runs on the contract's first deployment and is used to set initial values — let’s set an starting total supply of 1,000 tokens for this example.


_16

// ...previous code

_16

_16

access(all) contract FooToken: FungibleToken {

_16

access(all) var totalSupply: UFix64

_16

_16

access(all) let VaultStoragePath: StoragePath

_16

access(all) let VaultPublicPath: PublicPath

_16

access(all) let MinterStoragePath: StoragePath

_16

_16

init() {

_16

self.totalSupply = 1000.0

_16

self.VaultStoragePath = /storage/fooTokenVault

_16

self.VaultPublicPath = /public/fooTokenVault

_16

self.MinterStoragePath = /storage/fooTokenMinter

_16

}

_16

}


Creating a Vault

Inside of this contract, we'll need to create a resource for a Vault.The FungibleToken standard requires that your vault implements the FungibleToken.Vault interface.This interface inherits from many other interfaceswhich enforce different functionality that you can learn about in the standard.


_16

import "FungibleToken"

_16

_16

access(all) contract FooToken: FungibleToken {

_16

// ...totalSupply and path code

_16

_16

access(all) resource Vault: FungibleToken.Vault {

_16

_16

access(all) var balance: UFix64

_16

_16

init(balance: UFix64) {

_16

self.balance = balance

_16

}

_16

}

_16

_16

// ...init code

_16

}


In order to give an account a vault, we need to create a functionthat creates a vault of our FooToken type and returns it to the account.This function takes a vaultType: Type argument that allows the callerto specify which type of Vault they want to create.Contracts that implement multiple Vault types can use this argument,but since your contract is only implementing one Vault type,it can ignore the argument.

A simpler version of this function with no parametershould also be added to your Vault implementation.


_24

import "FungibleToken"

_24

_24

access(all) contract FooToken: FungibleToken {

_24

// ...other code

_24

_24

access(all) resource Vault: FungibleToken.Vault {

_24

_24

// ...other vault code

_24

_24

access(all) fun createEmptyVault(): @FooToken.Vault {

_24

return <-create Vault(balance: 0.0)

_24

}

_24

_24

// ...vault init code

_24

}

_24

_24

// ...other code

_24

_24

access(all) fun createEmptyVault(vaultType: Type): @FooToken.Vault {

_24

return <- create Vault(balance: 0.0)

_24

}

_24

_24

// ...FooToken.init() code

_24

}


Inside our Vault resource, we also need a way to withdraw balances.To do that, we need to add a withdraw() function that returns a new vaultwith the transfer amount and decrements the existing balance.


_20

import "FungibleToken"

_20

_20

access(all) contract FooToken: FungibleToken {

_20

_20

// ...previous code

_20

_20

access(all) resource Vault: FungibleToken.Vault {

_20

_20

// ...other vault code

_20

_20

access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @FooToken.Vault {

_20

self.balance = self.balance - amount

_20

return <-create Vault(balance: amount)

_20

}

_20

_20

// ...vault init code

_20

}

_20

_20

// ...additional code

_20

}


As you can see, this function has an access(FungibleToken.Withdraw) access modifier.This is an example of entitlements in Cadence.Entitlementsare a way for developers to restrict access to privileged fields and functionsin a composite type like a resource when a reference is created for it.In this example, the withdraw() function is always accessible to code thatcontrols the full Vault object, but if a reference is created for it,the withdraw() function can only be called if the referenceis authorized by the owner with FungibleToken.Withdraw,which is a standard entitlementdefined by the FungibleToken contract:


_10

// Example of an authorized entitled reference to a FungibleToken.Vault

_10

<auth(FungibleToken.Withdraw) &{FungibleToken.Vault}>


Entitlements are important to understand because they are what protectsprivileged functionality in your resource objects from being accessed by third-parties.It is recommended to read the entitlements documentationto understand how to use the feature properly.

References can be freely up-casted and down-casted in Cadence, so it is importantfor privileged functionality to be protected by an entitlement so that it canonly be accessed if it is authorized.

In addition to withdrawing, the vault also needs a way to deposit.We'll typecastto make sure we are dealing with the correct token, update the vault balance,and destroy the vault. Add this code to your resource:


_22

import "FungibleToken"

_22

_22

access(all) contract FooToken: FungibleToken {

_22

_22

// ...previous code

_22

_22

access(all) resource Vault: FungibleToken.Vault {

_22

_22

// ...other vault code

_22

_22

access(all) fun deposit(from: @{FungibleToken.Vault}) {

_22

let vault <- from as! @FooToken.Vault

_22

self.balance = self.balance + vault.balance

_22

destroy vault

_22

}

_22

_22

// ...vault init

_22

_22

}

_22

_22

// ...additional code

_22

}


Many projects rely on events the signal when withdrawals, deposits, or burns happen.Luckily, the FungibleToken standard handles the definition and emissionof events for projects, so there is no need for you to add any eventsto your implementation for withdraw, deposit, and burn.

Here are the FungibleToken event definitions:


_10

/// The event that is emitted when tokens are withdrawn from a Vault

_10

access(all) event Withdrawn(type: String, amount: UFix64, from: Address?, fromUUID: UInt64, withdrawnUUID: UInt64, balanceAfter: UFix64)

_10

_10

/// The event that is emitted when tokens are deposited to a Vault

_10

access(all) event Deposited(type: String, amount: UFix64, to: Address?, toUUID: UInt64, depositedUUID: UInt64, balanceAfter: UFix64)

_10

_10

/// Event that is emitted when the global burn method is called with a non-zero balance

_10

access(all) event Burned(type: String, amount: UFix64, fromUUID: UInt64)


These events are emitted by the Vault interfacein the FungibleToken contract whenever the relevant function is called on any implementation.

One important piece to understand about the Burned event in particularis that in order for it to be emitted when a Vault is burned, it needs tobe burnt via the Burner contract's burn() method.

This will call the resource's burnCallback() function, which emits the event.You'll need to also add this function to your token contract now:


_24

import "FungibleToken"

_24

_24

access(all) contract FooToken: FungibleToken {

_24

_24

// ...previous code

_24

access(all) resource Vault: FungibleToken.Vault {

_24

_24

// ...other vault code

_24

_24

/// Called when a fungible token is burned via the `Burner.burn()` method

_24

access(contract) fun burnCallback() {

_24

if self.balance > 0.0 {

_24

FooToken.totalSupply = FooToken.totalSupply - self.balance

_24

}

_24

self.balance = 0.0

_24

}

_24

_24

// ...vault init

_24

_24

}

_24

_24

// ...additional code

_24

}


If you ever need to destroy a Vault with a non-zero balance,you should destroy it via the Burner.burn method so this important function can be called.

There are three other utility methods that need to be added to your Vaultto get various information:


_33

import "FungibleToken"

_33

_33

access(all) contract FooToken: FungibleToken {

_33

_33

// ...previous code

_33

_33

access(all) resource Vault: FungibleToken.Vault {

_33

_33

// ...other vault code

_33

_33

/// getSupportedVaultTypes optionally returns a list of vault types that this receiver accepts

_33

access(all) view fun getSupportedVaultTypes(): {Type: Bool} {

_33

let supportedTypes: {Type: Bool} = {}

_33

supportedTypes[self.getType()] = true

_33

return supportedTypes

_33

}

_33

_33

/// Says if the Vault can receive the provided type in the deposit method

_33

access(all) view fun isSupportedVaultType(type: Type): Bool {

_33

return self.getSupportedVaultTypes()[type] ?? false

_33

}

_33

_33

/// Asks if the amount can be withdrawn from this vault

_33

access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {

_33

return amount <= self.balance

_33

}

_33

_33

// ...vault init

_33

_33

}

_33

_33

// ...additional code

_33

}


Adding Support for Metadata Views

The Fungible Token standard also enforces that implementationsprovide functionality to return a set of standard views about the tokensvia the ViewResolverand FungibleTokenMetadataViews definitions.(You will need to add these imports to your contract now)These provide developers with standard ways of representing metadataabout a given token such as supply, token symbols, website links, and standardaccount paths and types that third-parties can access in a standard way.You can see the metadata views documentationfor a more thorough guide using a NFT contract as an example.

For now, you can add this code to your contract to support the important metadata views:


_83

import "FungibleToken"

_83

_83

// Add these imports

_83

import "MetadataViews"

_83

import "FungibleTokenMetadataViews"

_83

_83

access(all) contract FooToken: FungibleToken {

_83

// ...other code

_83

_83

access(all) view fun getContractViews(resourceType: Type?): [Type] {

_83

return [

_83

Type<FungibleTokenMetadataViews.FTView>(),

_83

Type<FungibleTokenMetadataViews.FTDisplay>(),

_83

Type<FungibleTokenMetadataViews.FTVaultData>(),

_83

Type<FungibleTokenMetadataViews.TotalSupply>()

_83

]

_83

}

_83

_83

access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {

_83

switch viewType {

_83

case Type<FungibleTokenMetadataViews.FTView>():

_83

return FungibleTokenMetadataViews.FTView(

_83

ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,

_83

ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?

_83

)

_83

case Type<FungibleTokenMetadataViews.FTDisplay>():

_83

let media = MetadataViews.Media(

_83

file: MetadataViews.HTTPFile(

_83

// Change this to your own SVG image

_83

url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"

_83

),

_83

mediaType: "image/svg+xml"

_83

)

_83

let medias = MetadataViews.Medias([media])

_83

return FungibleTokenMetadataViews.FTDisplay(

_83

// Change these to represent your own token

_83

name: "Example Foo Token",

_83

symbol: "EFT",

_83

description: "This fungible token is used as an example to help you develop your next FT #onFlow.",

_83

externalURL: MetadataViews.ExternalURL("https://developers.flow.com/build/guides/fungible-token"),

_83

logos: medias,

_83

socials: {

_83

"twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")

_83

}

_83

)

_83

case Type<FungibleTokenMetadataViews.FTVaultData>():

_83

return FungibleTokenMetadataViews.FTVaultData(

_83

storagePath: self.VaultStoragePath,

_83

receiverPath: self.VaultPublicPath,

_83

metadataPath: self.VaultPublicPath,

_83

receiverLinkedType: Type<&FooToken.Vault>(),

_83

metadataLinkedType: Type<&FooToken.Vault>(),

_83

createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {

_83

return <-FooToken.createEmptyVault(vaultType: Type<@FooToken.Vault>())

_83

})

_83

)

_83

case Type<FungibleTokenMetadataViews.TotalSupply>():

_83

return FungibleTokenMetadataViews.TotalSupply(

_83

totalSupply: FooToken.totalSupply

_83

)

_83

}

_83

return nil

_83

}

_83

_83

// ...other code

_83

_83

access(all) resource Vault: FungibleToken.Vault {

_83

_83

// ...other vault code

_83

_83

access(all) view fun getViews(): [Type] {

_83

return FooToken.getContractViews(resourceType: nil)

_83

}

_83

_83

access(all) fun resolveView(_ view: Type): AnyStruct? {

_83

return FooToken.resolveContractView(resourceType: nil, viewType: view)

_83

}

_83

_83

// ...other vault code

_83

}

_83

_83

// ...other FooToken code

_83

}


Creating a Minter

Let's create a minter resource which is used to mint vaults that have tokens in them. We can keep track of tokens we are minting with totalSupply

If we want the ability to create new tokens, we'll need a way to mint them. To do that, let's create another resource on the FooToken contract. This will have a mintTokenfunction which can increase the total supply of the token.


_31

import "FungibleToken"

_31

import "MetadataViews"

_31

import "FungibleTokenMetadataViews"

_31

_31

access(all) contract FooToken: FungibleToken {

_31

_31

// ...additional contract code

_31

_31

// Add this event

_31

access(all) event TokensMinted(amount: UFix64, type: String)

_31

_31

/// Minter

_31

///

_31

/// Resource object that token admin accounts can hold to mint new tokens.

_31

///

_31

access(all) resource Minter {

_31

/// mintTokens

_31

///

_31

/// Function that mints new tokens, adds them to the total supply,

_31

/// and returns them to the calling context.

_31

///

_31

access(all) fun mintTokens(amount: UFix64): @FooToken.Vault {

_31

FooToken.totalSupply = FooToken.totalSupply + amount

_31

let vault <-create Vault(balance: amount)

_31

emit TokensMinted(amount: amount, type: vault.getType().identifier)

_31

return <-vault

_31

}

_31

}

_31

_31

// ...additional contract code

_31

}


We also want to decide which account/s we want to give this ability to.In our example, we'll give it to the account where the contract is deployed.We can set this in the contract init function below the setting of total supplyso that when the contract is created the minter is stored on the same account.


_13

import "FungibleToken"

_13

import "MetadataViews"

_13

import "FungibleTokenMetadataViews"

_13

_13

access(all) contract FooToken: FungibleToken {

_13

_13

// ...additional contract code

_13

_13

init() {

_13

self.totalSupply = 1000.0 // existed before

_13

self.account.save(<- create Minter(), to: self.MinterStoragePath)

_13

}

_13

}


After each of these steps, your FooToken.cdc contract file should now look like this:


_172

import "FungibleToken"

_172

import "MetadataViews"

_172

import "FungibleTokenMetadataViews"

_172

_172

access(all) contract FooToken: FungibleToken {

_172

_172

/// The event that is emitted when new tokens are minted

_172

access(all) event TokensMinted(amount: UFix64, type: String)

_172

_172

/// Total supply of FooTokens in existence

_172

access(all) var totalSupply: UFix64

_172

_172

/// Storage and Public Paths

_172

access(all) let VaultStoragePath: StoragePath

_172

access(all) let VaultPublicPath: PublicPath

_172

access(all) let ReceiverPublicPath: PublicPath

_172

access(all) let MinterStoragePath: StoragePath

_172

_172

access(all) view fun getContractViews(resourceType: Type?): [Type] {

_172

return [

_172

Type<FungibleTokenMetadataViews.FTView>(),

_172

Type<FungibleTokenMetadataViews.FTDisplay>(),

_172

Type<FungibleTokenMetadataViews.FTVaultData>(),

_172

Type<FungibleTokenMetadataViews.TotalSupply>()

_172

]

_172

}

_172

_172

access(all) fun resolveContractView(resourceType: Type?, viewType: Type): AnyStruct? {

_172

switch viewType {

_172

case Type<FungibleTokenMetadataViews.FTView>():

_172

return FungibleTokenMetadataViews.FTView(

_172

ftDisplay: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTDisplay>()) as! FungibleTokenMetadataViews.FTDisplay?,

_172

ftVaultData: self.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?

_172

)

_172

case Type<FungibleTokenMetadataViews.FTDisplay>():

_172

let media = MetadataViews.Media(

_172

file: MetadataViews.HTTPFile(

_172

// Change this to your own SVG image

_172

url: "https://assets.website-files.com/5f6294c0c7a8cdd643b1c820/5f6294c0c7a8cda55cb1c936_Flow_Wordmark.svg"

_172

),

_172

mediaType: "image/svg+xml"

_172

)

_172

let medias = MetadataViews.Medias([media])

_172

return FungibleTokenMetadataViews.FTDisplay(

_172

// Change these to represent your own token

_172

name: "Example Foo Token",

_172

symbol: "EFT",

_172

description: "This fungible token is used as an example to help you develop your next FT #onFlow.",

_172

externalURL: MetadataViews.ExternalURL("https://developers.flow.com/build/guides/fungible-token"),

_172

logos: medias,

_172

socials: {

_172

"twitter": MetadataViews.ExternalURL("https://twitter.com/flow_blockchain")

_172

}

_172

)

_172

case Type<FungibleTokenMetadataViews.FTVaultData>():

_172

return FungibleTokenMetadataViews.FTVaultData(

_172

storagePath: self.VaultStoragePath,

_172

receiverPath: self.VaultPublicPath,

_172

metadataPath: self.VaultPublicPath,

_172

receiverLinkedType: Type<&FooToken.Vault>(),

_172

metadataLinkedType: Type<&FooToken.Vault>(),

_172

createEmptyVaultFunction: (fun(): @{FungibleToken.Vault} {

_172

return <-FooToken.createEmptyVault(vaultType: Type<@FooToken.Vault>())

_172

})

_172

)

_172

case Type<FungibleTokenMetadataViews.TotalSupply>():

_172

return FungibleTokenMetadataViews.TotalSupply(

_172

totalSupply: FooToken.totalSupply

_172

)

_172

}

_172

return nil

_172

}

_172

_172

access(all) resource Vault: FungibleToken.Vault {

_172

_172

/// The total balance of this vault

_172

access(all) var balance: UFix64

_172

_172

// initialize the balance at resource creation time

_172

init(balance: UFix64) {

_172

self.balance = balance

_172

}

_172

_172

/// Called when a fungible token is burned via the `Burner.burn()` method

_172

access(contract) fun burnCallback() {

_172

if self.balance > 0.0 {

_172

FooToken.totalSupply = FooToken.totalSupply - self.balance

_172

}

_172

self.balance = 0.0

_172

}

_172

_172

access(all) view fun getViews(): [Type] {

_172

return FooToken.getContractViews(resourceType: nil)

_172

}

_172

_172

access(all) fun resolveView(_ view: Type): AnyStruct? {

_172

return FooToken.resolveContractView(resourceType: nil, viewType: view)

_172

}

_172

_172

access(all) view fun getSupportedVaultTypes(): {Type: Bool} {

_172

let supportedTypes: {Type: Bool} = {}

_172

supportedTypes[self.getType()] = true

_172

return supportedTypes

_172

}

_172

_172

access(all) view fun isSupportedVaultType(type: Type): Bool {

_172

return self.getSupportedVaultTypes()[type] ?? false

_172

}

_172

_172

access(all) view fun isAvailableToWithdraw(amount: UFix64): Bool {

_172

return amount <= self.balance

_172

}

_172

_172

access(FungibleToken.Withdraw) fun withdraw(amount: UFix64): @FooToken.Vault {

_172

self.balance = self.balance - amount

_172

return <-create Vault(balance: amount)

_172

}

_172

_172

access(all) fun deposit(from: @{FungibleToken.Vault}) {

_172

let vault <- from as! @FooToken.Vault

_172

self.balance = self.balance + vault.balance

_172

vault.balance = 0.0

_172

destroy vault

_172

}

_172

_172

access(all) fun createEmptyVault(): @FooToken.Vault {

_172

return <-create Vault(balance: 0.0)

_172

}

_172

}

_172

_172

access(all) resource Minter {

_172

/// mintTokens

_172

///

_172

/// Function that mints new tokens, adds them to the total supply,

_172

/// and returns them to the calling context.

_172

///

_172

access(all) fun mintTokens(amount: UFix64): @FooToken.Vault {

_172

FooToken.totalSupply = FooToken.totalSupply + amount

_172

let vault <-create Vault(balance: amount)

_172

emit TokensMinted(amount: amount, type: vault.getType().identifier)

_172

return <-vault

_172

}

_172

}

_172

_172

access(all) fun createEmptyVault(vaultType: Type): @FooToken.Vault {

_172

return <- create Vault(balance: 0.0)

_172

}

_172

_172

init() {

_172

self.totalSupply = 1000.0

_172

_172

self.VaultStoragePath = /storage/fooTokenVault

_172

self.VaultPublicPath = /public/fooTokenVault

_172

self.MinterStoragePath = /storage/fooTokenMinter

_172

_172

// Create the Vault with the total supply of tokens and save it in storage

_172

//

_172

let vault <- create Vault(balance: self.totalSupply)

_172

emit TokensMinted(amount: vault.balance, type: vault.getType().identifier)

_172

self.account.storage.save(<-vault, to: self.VaultStoragePath)

_172

_172

// Create a public capability to the stored Vault that exposes

_172

// the `deposit` method and getAcceptedTypes method through the `Receiver` interface

_172

// and the `balance` method through the `Balance` interface

_172

//

_172

let fooTokenCap = self.account.capabilities.storage.issue<&FooToken.Vault>(self.VaultStoragePath)

_172

self.account.capabilities.publish(fooTokenCap, at: self.VaultPublicPath)

_172

_172

let minter <- create Minter()

_172

self.account.storage.save(<-minter, to: self.MinterStoragePath)

_172

}

_172

}


Deploying the Contract

In order to use the contract, we need to deploy it to the network we want to use it on.In our case we are going to deploy it to emulator while developing.

Back in our flow.json, let's add our FooToken to the contracts after FungibleToken with the path of the source code:


_10

"FooToken": "cadence/contracts/FooToken.cdc"


Let's also add a new deployments section to flow.json with the networkwe want to deploy it to, emulator, the account we want it deployed to emulator-account,and the list of contracts we want deployed in the array.


_10

"deployments": {

_10

"emulator": {

_10

"emulator-account": ["FooToken"]

_10

}

_10

}


Next, using the Flow CLI, we will start the emulator. As mentioned,this will give us a local development environment for the Flow Blockchain.


_10

flow emulator start


Open a new terminal and run the following to deploy your project:


_10

flow project deploy


Congrats, you've deployed your contract to the Flow Blockchain emulator.To read more about deploying your project to other environments,see the CLI docs.

Reading the Token’s Total Supply

Let's now check that our total supply was initialized with 1,000 FooTokens. Go ahead and create a script called get_total_supply.cdc using the generate command.


_10

flow generate script get_total_supply


In cadence/scripts/get_total_supply.cdc (which was just created), let's add this code which will log the totalSupply value from the FooToken contract:


_10

import "FooToken"

_10

_10

access(all) fun main(): UFix64 {

_10

return FooToken.totalSupply

_10

}


To run this using the CLI, enter this in your terminal:


_10

flow scripts execute cadence/scripts/get_total_supply.cdc


In the terminal where you started the emulator, you should see Result: 1000.0

To learn more about running scripts using Flow CLI, see the docs.

Giving Accounts the Ability to Receive Tokens

On Flow, newly created accounts cannot receive arbitrary assets.They need to be initialized to receive resources.In our case, we want to give accounts tokens and we’ll need to createa Vault (which acts as a receiver) on each account that we wantto have the ability to receive tokens. To do this, we'll need to run a transactionwhich will create the vault and set it in their storageusing the createEmptyVault() function we created earlier on the contract.

Let's first create the file at cadence/transactions/setup_ft_account.cdc using the generate command:


_10

flow generate transaction setup_ft_account


Then add this code to it.This will call the createEmptyVault function, save it in storage,and create a capability for the vault which will later allow us to read from it(To learn more about capabilities, see the Cadence docs here).


_24

import "FungibleToken"

_24

import "FooToken"

_24

_24

transaction () {

_24

_24

prepare(signer: auth(BorrowValue, IssueStorageCapabilityController, PublishCapability, SaveValue) &Account) {

_24

_24

// Return early if the account already stores a FooToken Vault

_24

if signer.storage.borrow<&FooToken.Vault>(from: FooToken.VaultStoragePath) != nil {

_24

return

_24

}

_24

_24

let vault <- FooToken.createEmptyVault(vaultType: Type<@FooToken.Vault>())

_24

_24

// Create a new FooToken Vault and put it in storage

_24

signer.storage.save(<-vault, to: FooToken.VaultStoragePath)

_24

_24

// Create a public capability to the Vault that exposes the Vault interfaces

_24

let vaultCap = signer.capabilities.storage.issue<&FooToken.Vault>(

_24

FooToken.VaultStoragePath

_24

)

_24

signer.capabilities.publish(vaultCap, at: FooToken.VaultPublicPath)

_24

}

_24

}


There are also examples of generic transactionsthat you can use to setup an account for ANY fungible token using metadata views!You should check those out and try to use generic transactions whenever it is possible.

Next let's create a new emulator account using the CLI. We'll use this account to create a new vault and mint tokens into it. Run:


_10

flow accounts create


Let's call it test-acct and select "Emulator" for the network:


_10

test-acct


This will have added a new account, called test-acct to your flow.json.

To call our setup account transaction from the CLI, we'll run the following:


_10

flow transactions send ./cadence/transactions/setup_ft_account.cdc --signer test-acct --network emulator


To learn more about running transactions using CLI, see the docs.

Reading a Vault’s Balance

Let's now read the balance of the newly created account (test-acct) to check it's zero.

Create this new script file cadence/scripts/get_footoken_balance.cdc:


_10

flow generate script get_footoken_balance


Add this code which attempts to borrow the capability from the account requested and logs the vault balance if permitted:


_13

import "FungibleToken"

_13

import "FooToken"

_13

import "FungibleTokenMetadataViews"

_13

_13

access(all) fun main(address: Address): UFix64 {

_13

let vaultData = FooToken.resolveContractView(resourceType: nil, viewType: Type<FungibleTokenMetadataViews.FTVaultData>()) as! FungibleTokenMetadataViews.FTVaultData?

_13

?? panic("Could not get vault data view for the contract")

_13

_13

return getAccount(address).capabilities.borrow<&{FungibleToken.Balance}>(

_13

vaultData.metadataPath

_13

)?.balance

_13

?? panic("Could not borrow Balance reference to the Vault")

_13

}


To run this script using the CLI, enter the following in your terminal.Note: you'll need to replace 123 with the address created by CLIin your flow.json for the test-acct address.


_10

flow scripts execute cadence/scripts/get_footoken_balance.cdc 123 // change "123" to test-acct address


You should see a balance of zero logged.

Minting More Tokens

Now that we have an account with a vault, let's mint some tokens into itusing the Minter we created on the contract account.

To do this, let's create a new transaction file cadence/transactions/mint_footoken.cdc:


_10

flow generate transaction mint_footoken


Next, let's add the following code to the mint_footoken.cdc file.This code will attempt to borrow the minting capabilityand mint 20 new tokens into the receivers account.


_30

import "FungibleToken"

_30

import "FooToken"

_30

_30

transaction(recipient: Address, amount: UFix64) {

_30

_30

/// Reference to the Example Token Minter Resource object

_30

let tokenMinter: &FooToken.Minter

_30

_30

/// Reference to the Fungible Token Receiver of the recipient

_30

let tokenReceiver: &{FungibleToken.Receiver}

_30

_30

prepare(signer: auth(BorrowValue) &Account) {

_30

_30

// Borrow a reference to the admin object

_30

self.tokenMinter = signer.storage.borrow<&FooToken.Minter>(from: FooToken.MinterStoragePath)

_30

?? panic("Signer is not the token admin")

_30

_30

self.tokenReceiver = getAccount(recipient).capabilities.borrow<&{FungibleToken.Receiver}>(FooToken.VaultPublicPath)

_30

?? panic("Could not borrow receiver reference to the Vault")

_30

}

_30

_30

execute {

_30

_30

// Create mint tokens

_30

let mintedVault <- self.tokenMinter.mintTokens(amount: amount)

_30

_30

// Deposit them to the receiever

_30

self.tokenReceiver.deposit(from: <-mintedVault)

_30

}

_30

}


To run this transaction, enter this in your terminal.Note: 123 should be replaced with address of test-acct found in your flow.json.This command also states to sign with our emulator-account on the Emulator network.


_10

flow transactions send ./cadence/transactions/mint_footoken.cdc 123 20.0 --signer emulator-account --network emulator


Let's go ahead and read the vault again. Remember to replace 123 with the correct address.


_10

flow scripts execute cadence/scripts/get_footoken_balance.cdc 123


It should now say 20 tokens are in the vault.

Transferring Tokens Between Accounts

The final functionality we'll add is the ability to transfer tokens from one account to another.

To do that, create a new cadence/transactions/transfer_footoken.cdc transaction file:


_10

flow generate transaction transfer_footoken


Let's add the code which states that the signer of the transactionwill withdraw from their vault and put it into the receiver's vaultwhich will be passed as a transaction argument.


_31

import "FungibleToken"

_31

import "FooToken"

_31

_31

transaction(to: Address, amount: UFix64) {

_31

_31

// The Vault resource that holds the tokens that are being transferred

_31

let sentVault: @{FungibleToken.Vault}

_31

_31

prepare(signer: auth(BorrowValue) &Account) {

_31

_31

// Get a reference to the signer's stored vault

_31

let vaultRef = signer.storage.borrow<auth(FungibleToken.Withdraw) &FooToken.Vault>(from: FooToken.VaultStoragePath)

_31

?? panic("Could not borrow reference to the owner's Vault!")

_31

_31

// Withdraw tokens from the signer's stored vault

_31

self.sentVault <- vaultRef.withdraw(amount: amount)

_31

}

_31

_31

execute {

_31

_31

// Get the recipient's public account object

_31

let recipient = getAccount(to)

_31

_31

// Get a reference to the recipient's Receiver

_31

let receiverRef = recipient.capabilities.borrow<&{FungibleToken.Receiver}>(FooToken.VaultPublicPath)

_31

?? panic("Could not borrow receiver reference to the recipient's Vault")

_31

_31

// Deposit the withdrawn tokens in the recipient's receiver

_31

receiverRef.deposit(from: <-self.sentVault)

_31

}

_31

}


To send our tokens, we'll need to create a new account to send them to. Let's make one more account on emulator. Run:


_10

flow accounts create


And pick the name:


_10

test-acct-2


Make sure to select Emulator as the network.

Don't forget the new account will need a vault added, so let's run the following transaction to add one:


_10

flow transactions send ./cadence/transactions/setup_ft_account.cdc --signer test-acct-2 --network emulator


Now, let's send 1 token from our earlier account to the new account. Remember to replace 123 with account address of test-acct-2.


_10

flow transactions send ./cadence/transactions/transfer_footoken.cdc 123 1.0 --signer test-acct --network emulator


After that, read the balance of test-acct-2 (replace the address 123).


_10

flow scripts execute cadence/scripts/get_footoken_balance.cdc 123


You should now see 1 token in test-acct-2 account!

The transfer transaction also has a generic version that developers are encouraged to use!

More

How to Create a Fungible Token on Flow | Flow Developer Portal (2024)
Top Articles
Tether (USDT) under scrutiny: possible crisis for stablecoin
Retirement Syndrome: How to Triumph Over It - SWM
Katie Pavlich Bikini Photos
Gamevault Agent
Hocus Pocus Showtimes Near Harkins Theatres Yuma Palms 14
Free Atm For Emerald Card Near Me
Craigslist Mexico Cancun
Hendersonville (Tennessee) – Travel guide at Wikivoyage
Doby's Funeral Home Obituaries
Vardis Olive Garden (Georgioupolis, Kreta) ✈️ inkl. Flug buchen
Select Truck Greensboro
Things To Do In Atlanta Tomorrow Night
Non Sequitur
How To Cut Eelgrass Grounded
Pac Man Deviantart
Alexander Funeral Home Gallatin Obituaries
Craigslist In Flagstaff
Shasta County Most Wanted 2022
Energy Healing Conference Utah
Testberichte zu E-Bikes & Fahrrädern von PROPHETE.
Aaa Saugus Ma Appointment
Geometry Review Quiz 5 Answer Key
Walgreens Alma School And Dynamite
Bible Gateway passage: Revelation 3 - New Living Translation
Yisd Home Access Center
Home
Shadbase Get Out Of Jail
Gina Wilson Angle Addition Postulate
Celina Powell Lil Meech Video: A Controversial Encounter Shakes Social Media - Video Reddit Trend
Walmart Pharmacy Near Me Open
A Christmas Horse - Alison Senxation
Ou Football Brainiacs
Access a Shared Resource | Computing for Arts + Sciences
Pixel Combat Unblocked
Cvs Sport Physicals
Mercedes W204 Belt Diagram
'Conan Exiles' 3.0 Guide: How To Unlock Spells And Sorcery
Teenbeautyfitness
Where Can I Cash A Huntington National Bank Check
Facebook Marketplace Marrero La
Nobodyhome.tv Reddit
Topos De Bolos Engraçados
Gregory (Five Nights at Freddy's)
Grand Valley State University Library Hours
Holzer Athena Portal
Hampton In And Suites Near Me
Hello – Cornerstone Chapel
Stoughton Commuter Rail Schedule
Bedbathandbeyond Flemington Nj
Free Carnival-themed Google Slides & PowerPoint templates
Otter Bustr
Selly Medaline
Latest Posts
Article information

Author: Trent Wehner

Last Updated:

Views: 6067

Rating: 4.6 / 5 (76 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Trent Wehner

Birthday: 1993-03-14

Address: 872 Kevin Squares, New Codyville, AK 01785-0416

Phone: +18698800304764

Job: Senior Farming Developer

Hobby: Paintball, Calligraphy, Hunting, Flying disc, Lapidary, Rafting, Inline skating

Introduction: My name is Trent Wehner, I am a talented, brainy, zealous, light, funny, gleaming, attractive person who loves writing and wants to share my knowledge and understanding with you.