Endless
  • 🚀README
  • Discovery
    • 🚀Endless Web3 Genesis Cloud
    • 💎Business Model
    • 🎯Vision
    • ✈️Roadmap
    • 🪙Economics
    • 👤Team
      • Yu Xiong
      • Amit Kumar Jaiswal
      • Ned
      • 0xfun
      • Scott Trowbridge
      • Neeraj Sharma LLB
      • Amjad Suleman
      • Binu Paul
      • Eduard Romulus GOEAN
    • ❤️Developer Community
  • Endless Chain
    • Tech Docs
      • Account Address Format
      • Endless Account
      • Endless Coin(EDS)
      • Sponsored Transaction
      • On-Chain Multisig
      • Randomness
      • Safety Transaction
      • Token Locking & Distribution
    • Start
      • Learn about Endless
        • Accounts
        • Resources
        • Events
        • Transactions and States
        • Gas and Storage Fees
        • Computing Transaction Gas
        • Blocks
        • Staking
          • Delegated Staking
        • Governance
        • Endless Blockchain Deep Dive
          • Validator Nodes Overview
          • Fullnodes Overview
          • Node Networks and Synchronization
        • Move - A Web3 Language and Runtime
      • Explore Endless
      • Latest Endless Releases
      • Networks
    • Build
      • Tutorials
        • Your First Transaction
        • Your First Fungible Asset
        • Your First NFT
        • Your First Move Module
        • Your First Multisig
      • Learn the Move Language
        • The Move Book
          • Getting Started
            • Introduction
            • Modules and Scripts
          • Primitive Types
            • Move Tutorial
            • Integers
            • Bool
            • Address
            • Vector
            • Signer
            • References
            • Tuples and Unit
          • Basic Concepts
            • Local Variables and Scope
            • Equality
            • Abort and Assert
            • Conditionals
            • While, For, and Loop
            • Functions
            • Structs and Resources
            • Constants
            • Generics
            • Abilities
            • Uses and Aliases
            • Friends
            • Packages
            • Package Upgrades
            • Unit Tests
          • Global Storage
            • Global Storage - Structure
            • Global Storage - Operators
          • Reference
            • Libraries
            • Move Coding Conventions
        • Advanced Move Guides
          • Objects
            • Creating Objects
            • Configuring objects
            • Using objects
          • Move Scripts
            • Writing Move Scripts
            • Compiling Move Scripts
            • Running Move Scripts
            • Move Scripts Tutorial
          • Resource Accounts
          • Modules on Endless
          • Cryptography
          • Gas Profiling
          • Security
      • Endless Standards
        • Object
        • Endless Fungible Asset Standard
        • Endless Digital Asset Standard
        • Endless Wallet Standard
      • Endless APIs
        • Fullnode Rest API
        • Indexer Restful API
          • Indexer Installation
        • GRPC Transaction Stream
          • Running Locally
          • Custom Processors
            • End-to-End Tutorial
            • Parsing Transactions
          • Self-Hosted Transaction Stream Service
      • Endless SDKs
        • TypeScript SDK
          • Account
          • SDK Configuration
          • Fetch data from chain
          • Transaction Builder
          • HTTP Client
          • Move Types
          • Testing
          • Typescript
        • Rust SDK
        • Go SDK
      • Endless CLI
        • Install the Endless CLI
          • Install On Mac
          • Install On Alibaba Cloud
          • Install On Linux
          • Install On Windows
        • CLI Configuration
        • Use Endless CLI
          • Working With Move Contracts
            • Arguments in JSON Tutorial
          • Trying Things On-Chain
            • Look Up On-Chain Account Info
            • Create Test Accounts
          • Running A Local Network
            • Running a Public Network
          • Managing a Network Node
      • Integrate with Endless
        • Endless Token Overview
        • Application Integration Guide
      • Endless VSCode extension
      • Advanced Builder Guides
        • Develop Locally
          • Running a Local Network
          • Run a Localnet with Validator
    • Nodes
      • Learn about Nodes
      • Run a Validator and VFN
        • Node Requirements
        • Deploy Nodes
          • Using Docker
          • Using AWS
          • Using Azure
          • Using GCP
        • Connect Nodes
          • Connect to a Network
        • Verify Nodes
          • Node Health
          • Validator Leaderboard
      • Run a Public Fullnode
        • PFN Requirements
        • Deploy a PFN
          • Using Pre-compiled Binary
          • Using Docker
          • Using GCP 🚧 (under_construction)
        • Verify a PFN
        • Modify a PFN
          • Upgrade your PFN
          • Generate a PFN Identity
          • Customize PFN Networks
      • Bootstrap a Node
        • Bootstrap from a Snapshot
        • Bootstrap from a Backup
      • Configure a Node
        • State Synchronization
        • Data Pruning
        • Telemetry
        • Locating Node Files
          • Files For Mainnet
          • Files For Testnet
          • Files For Devnet
      • Monitor a Node
        • Node Inspection Service
        • Important Node Metrics
        • Node Health Checker
    • Reference
      • Endless Error Codes
      • Move Reference Documentation
      • Endless Glossary
    • FAQs
  • Endless Bridge
    • Intro to Endless Bridge
    • How to use bridge
    • Liquidity Management
    • Faucet
    • Developer Integration
      • Contract Integration
        • Message Contract
        • Execute Contract
      • Server-Side Integration
        • Message Sender
        • Example of Message Listener Service (Rust)
        • Example of Token Cross-Chain (JS)
  • Endless Wallet
    • User Guide
    • Basic Tutorial
    • FAQs
    • MultiAccount
    • SDK
      • Functions
      • Events
  • GameFi
    • Intro
    • GameFi & Endless
  • Endless Modules
    • Stacks
    • Storage
    • Module List
  • Endless Ecosystem
    • Intro
    • Show Cases
    • App Demo
  • Whitepaper
  • Endless SCAN
    • User Guide
  • MULTI-SIGNATURE
    • Multi-Signature User Guide
  • Regulations
    • Privacy Policy
    • Terms of Service
    • Funding Terms - Disclaimer
Powered by GitBook
On this page
  • Your First Fungible Asset
  • Step 1: Pick an SDK
  • Step 2: Install the CLI
  • Step 3: Run the example
  • Step 4: Fungible Asset Move
  • Understanding the fa_coin.move Example Contract
  • Step 5: Fungible Asset CLI
  • Step 6: endless coin CLI in depth
  • Step 6.1: Coin CLI
  • Step 6.2: Understanding the management primitives of FACoin
  • Supporting documentation
Export as PDF
  1. Endless Chain
  2. Build
  3. Tutorials

Your First Fungible Asset

PreviousYour First TransactionNextYour First NFT

Last updated 1 month ago

Your First Fungible Asset

This tutorial introduces how you can compile, deploy, and mint your own fungible asset (FA), named FACoin. The Fungible Asset Standard provides built-in support for minting, transferring, burning, and tracking account balances, so is useful for representing fungible assets.

We will use Endless CLI to create, mint, burn and transfer coins in our Typescript demo code.

At a high level, the works through two main Objects:

  1. A Metadata Object to store information about the fungible asset.

  2. FungibleStores for each account that has the fungible asset to track their current account balance.

Sending a fungible asset to someone will cause:

  • create FungibleStore on Receiver account if FungibleStore not exists

  • update the balances for both accounts accordingly.

Step 1: Pick an SDK

Install your preferred SDK from the below list:

  • TypeScript SDK


Step 2: Install the CLI

Install the precompiled binary for the Endless CLI.


Step 3: Run the example

Clone the endless-ts-sdk repo and build it:

git clone https://github.com/endless-labs/endless-ts-sdk.git
cd endless-ts-sdk
pnpm install
pnpm build

Navigate to the Typescript examples directory:

cd examples/typescript

Install the necessary dependencies:

pnpm install

Step 4: Fungible Asset Move

Run your_fungible_asset

pnpm run your_fungible_asset

You should see an output demonstrating how the fungible assets are created and transferred that looks like this:

=== Addresses ===
Alice: 0x0c5dd7abbd67db06325fa1a2f37a1833f9a92ff2beb90f32495a9d80972429cd
Bob: 0x2a796f4255d5c23684fe6cc521069d684516031bb5ae1ad2061ddc5414450807
Charlie: 0xd824909be65a224f651ff6e9b82ec99ad5707fcef739d1003be20fc69fb93d7a

=== Compiling FACoin package locally ===
In order to run compilation, you must have the `endless` CLI installed.
Running the compilation locally, in a real situation you may want to compile this ahead of time.
endless move build-publish-payload --json-output-file move/facoin/facoin.json --package-dir move/facoin --named-addresses FACoin=0x0c5dd7abbd67db06325fa1a2f37a1833f9a92ff2beb90f32495a9d80972429cd --assume-yes
Compiling, may take a little while to download git dependencies...
INCLUDING DEPENDENCY EndlessFramework
INCLUDING DEPENDENCY EndlessStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING facoin

===Publishing FACoin package===
Transaction hash: 0x0c8a24987bdf2e5e40d8a00f6c97ac55419757bc440097d76959a64dbeafc351
metadata address: 0x2e0e90c701233467f27150f42d365e27e72eb0be8e2a74ee529c31b813bbb321
All the balances in this example refer to balance in primary fungible stores of each account.
Alice's initial balance: 0.
Bob's initial balance: 0.
Charlie's initial balance: 0.
Alice mints Charlie 100 coins.
Charlie's updated "Tutorial Token" primary fungible store balance: 0.
Alice freezes Bob's account.
Alice as the admin forcefully transfers the newly minted coins of Charlie to Bob ignoring that Bob's account is frozen.
Bob's updated "Tutorial Token" balance: 0.
Alice unfreezes Bob's account.
Alice burns 50 coins from Bob.
Bob's updated "Tutorial Token" balance: 0.
Bob transfers 10 coins to Alice as the owner.
Alice's updated "Tutorial Token" balance: 0.
Bob's updated "Tutorial Token" balance: 0.
done.

Understanding the fa_coin.move Example Contract

Let’s go step by step through how this contract is written.

1

Move.toml

The Move.toml file allows Move to import dependencies, determine which addresses to use, and includes metadata about the contract.

Regardless of which features you add to your fungible asset, your Move.toml will likely have similar fields to this at a minimum. In this case, we have the primary contract address FACoin that needs specifying at deploy time (indicated by leaving the value as “_”). It also includes the GitHub dependency to import the Fungible Asset standard from “EndlessFramework”.

Move.toml

[package]
name = "facoin"
version = "1.0.0"
authors = []
 
[addresses]
endless_framework = "0x1"
FACoin = "_"
 
[dependencies.EndlessFramework]
git = "https://github.com/endless-labs/endless-move-framework.git"
rev = "main"
subdir = "endless-framework"
2

Imports

The FACoin module uses several important modules:

  1. fungible_asset contains the logic for granting permission to mint, transfer, burn, and create your FungibleAsset.

  2. object allows for creating Endless Objects.

  3. primary_fungible_store contains the logic to track account balances for the new Fungible Asset.

FACoin.move

module FACoin::fa_coin {
    use endless_framework::fungible_asset::{Self, MintRef, TransferRef, BurnRef, Metadata, FungibleAsset};
    use endless_framework::object::{Self, Object};
    use endless_framework::primary_fungible_store;
    use std::error;
    use std::signer;
    use std::string::utf8;
    use std::option;
	
    /// Only fungible asset metadata owner can make changes.
    const ENOT_OWNER: u64 = 1;

    const ASSET_SYMBOL: vector<u8> = b"FA";
}

These imports are defined in the Move.toml file as GitHub dependencies.

3

init_module

This function is called when the module is initially published in order to set up the proper permissions and Objects. For FACoin, this is used to initialize the asset’s MetaData Object (which contains things like the asset’s name and symbol), as well as getting the relevant ref’s for how our fungible asset will be used.

The ManagedFungibleAsset standard helps keep track of which permissions this Module is allowed to use.

fa_coin.move

fun init_module(admin: &signer) {
    let constructor_ref = &object::create_named_object(admin, ASSET_SYMBOL);
    primary_fungible_store::create_primary_store_enabled_fungible_asset(
        constructor_ref,
        option::none(),
        utf8(b"FA Coin"),
        utf8(ASSET_SYMBOL),
        8,
        utf8(b"http://example.com/favicon.ico"),
        utf8(b"http://example.com"),
    );
 
    let mint_ref = fungible_asset::generate_mint_ref(constructor_ref);
    let burn_ref = fungible_asset::generate_burn_ref(constructor_ref);
    let transfer_ref = fungible_asset::generate_transfer_ref(constructor_ref);
    let metadata_object_signer = object::generate_signer(constructor_ref);
    move_to(
        &metadata_object_signer,
        ManagedFungibleAsset { mint_ref, transfer_ref, burn_ref }
    )
}
4

View Functions

When creating your own fungible asset, it can be helpful to add view functions for any data that is needed later on. In this case, we wanted to see the name of the asset in order to report which asset was being traded in our example scenario.

fa_coin.move

#[view]
public fun get_metadata(): Object<Metadata> {
    let asset_address = object::create_object_address(&@FACoin, ASSET_SYMBOL);
    object::address_to_object<Metadata>(asset_address)
}
5

Entry Functions

Every fungible asset has a similar interface (mint, transfer, burn, freeze, unfreeze, deposit, and withdraw). Here’s an example of a minimal mint function, which mints and transfers the funds to the proper recipient:

fa_coin.move

public entry fun mint(admin: &signer, to: address, amount: u128) acquires ManagedFungibleAsset {
    let asset = get_metadata();
    let managed_fungible_asset = authorized_borrow_refs(admin, asset);
    let to_wallet = primary_fungible_store::ensure_primary_store_exists(to, asset);
    let fa = fungible_asset::mint(&managed_fungible_asset.mint_ref, amount);
    fungible_asset::deposit_with_ref(&managed_fungible_asset.transfer_ref, to_wallet, fa);
}

Step 5: Fungible Asset CLI

pnpm run your_fungible_asset_cli

The application will complete, printing:

=== Addresses ===
Alice: 0x46b40431cdb78f4cd7a4be1a17b75b164873683159282a859815873cc219003f
Bob: 0x922e7a3805b69c0bac1b885a930e1e5f6745b64a12f934e855d04377051b4ba3
Alice create FACoin
FA coin metadata address: AMGSQEfMNfwSEsMEDZC4asoxzEbRb3GXr2jo7jMTYyJn
All the balances in this example refer to balance in primary fungible stores of each account.
Alice's initial FACoin balance: 0.
Bob's initial FACoin balance: 0.
Alice mints Bob 100 coins.
Bob's updated FACoin primary fungible store balance: 100.
Alice freezes Bob's account.
Alice unfreezes Bob's account.
Alice mints herself 100 coins.
Alice burns 50 coins from herself.
Alice's updated FACoin balance: 50.
Bob transfers 10 coins to Alice.
Alice's updated FACoin balance: 60.
Bob's updated FACoin balance: 90.
done.

Step 6: endless coin CLI in depth

Step 6.1: Coin CLI

Endless cli provide command to manage User customized fungible assets, including Create, Mint, Burn and Transfer, etc.

$ endless coin -h
Tool for FACoin

Usage: endless coin [OPTIONS] <COMMAND>

Commands:
  create                Create a new coin
  create-ex             Create a new coin with a custom coin address
  mint                  Mint new coins
  burn                  Burn coins
  freeze-account        FreezeAccount coins
  unfreeze-account      UnfreezeAccount coins
  transfer              Transfer coins
  balance               Balance of coin
  accounts              List all coin
  destroy-burn-cap      Destroy burn cap of coin
  destroy-mint-cap      Destroy mint cap of coin
  destroy-transfer-cap  Destroy transfer cap of coin
  set-icon-uri          Set icon_uri of coin
  set-project-uri       Set project_uri of coin
  ...

Step 6.2: Understanding the management primitives of FACoin

The creator of FACoin have several managing primitives:

  • Creating: Creating the Coin ("FACoin" metadata object).

  • Minting: Minting new coins.

  • Burning: Deleting coins.

  • Freezing/Unfreezing: Disabling/Enabling the owner of an account to withdraw from or deposit to their primary fungible store of FACoin.

  • Transfer: Withdraw from owned account and deposit to another acount.

Only Coin Creator has the authority of Minting, Burning, Freezing/Unfreezing. Endless CLI restricts Coin Creator by prevent from forceful transferring between any fungible stores no matter they are frozen or not.

Step 6.3: Creating "FACoin" metadata object

async function createCoin(admin: Ed25519Account): Promise<void> {
    const COIN_NAME = "FACoin";
    const COIN_SYM = "FA";
    const DECIMAL = 8;
    const MAX_SUPPLY = 0;
    const command = `endless coin create ${COIN_NAME} ${COIN_SYM}  ${DECIMAL} ${MAX_SUPPLY} "" "" 
      --url https://rpc-test.endless.link --sender-account ${admin.accountAddress.toString()} 
      --private-key ${admin.privateKey.toString()} --assume-yes`;
    
    const output = execSync(command, { encoding: 'utf8', maxBuffer: 1024 * 1024 * 10 });
}

FACoin has below attributes:

  • name: FACoin

  • symbol: FA

  • decimal: 8

  • max_supply: unlimited, ie. U128_MAX

  • icon_url: none

  • project_url: none

Once we create FACoin, we need to fetch FACoin's metadata address for the next coin management operations.

/** Return the address of the managed fungible asset that's created */
async function getMetadata(admin: Account): Promise<string> {
    const coinMeta = await endless.getCoinsDataCreatedBy(admin);
    const metadataAddress = coinMeta?.data[0].id;
    console.log("FA coin metadata address:", metadataAddress);
    return metadataAddress;
}

cause Alice never create any fungible asset before, so query 1st coin data will be FACoin metadata address

FA coin metadata address: AMGSQEfMNfwSEsMEDZC4asoxzEbRb3GXr2jo7jMTYyJn

Step 6.3.1: mint coins

Minting coins requires MintRef that was produced during initialization, only Coin Creator(ie. Alice) has authority to mint coins.

/** Admin mint the newly created coins to the specified receiver address */
async function mintCoin(coinAddr: string, admin: Ed25519Account, receiver: AccountAddress, amount: AnyNumber): Promise<void> {
    const command = `endless coin mint ${coinAddr} ${receiver} ${amount} 
    --url https://rpc-test.endless.link --sender-account ${admin.accountAddress.toString()} 
    --private-key ${admin.privateKey.toString()} --assume-yes`;
    
    const output = execSync(command, { encoding: 'utf8', maxBuffer: 1024 * 1024 * 10 });
}
...
...
console.log("Alice mints Bob 100 coins.");
await mintCoin(metadataAddress, alice, bob.accountAddress, 100);

Step 6.3.2: Query balance of FACoin

endless-ts-sdk provide function viewCoinBalance for user to query balance of coins, providing Coin Metadata address and account address.

const getFaBalance = async (account: Account, fungibleAssetMetadataAddress: string): Promise<bigint> => {
    return await endless.viewCoinBalance(account.accountAddress, AccountAddress.fromBs58String(fungibleAssetMetadataAddress));
};

Step 6.3.3: Transferring coins

endless-ts-sdk provide function transferFungibleAsset for user to build transaction of transfering coins from coinholder to another account.

/// Normal fungible asset transfer between primary stores
console.log("Bob transfers 10 coins to Alice.");
const transferFungibleAssetRawTransaction = await endless.transferFungibleAsset({
    sender: bob,
    fungibleAssetMetadataAddress: AccountAddress.fromBs58String(metadataAddress),
    recipient: alice.accountAddress,
    amount: 10,
});
const transferFungibleAssetTransaction = await endless.signAndSubmitTransaction({
    signer: bob,
    transaction: transferFungibleAssetRawTransaction,
});
await endless.waitForTransaction({ transactionHash: transferFungibleAssetTransaction.hash });

Supporting documentation

  • Endless CLI

  • Fungible Asset

  • TypeScript SDK

The full contract for FACoin.move can be found .

Run the TypeScript example:

provides all functions related with customized Token, you could build your customized move contract and write Typescripts code to interact with your move contract, fullfill token management as above.

Fungible Asset Standard
here
your_fungible_asset_cli
Endless Token module
Mainnet Indexer API specification