Join our community of builders on

Telegram!Telegram
Tokens

Fungible Token

Source Code

Fungible tokens represent assets where each unit is identical and interchangeable, such as currencies, commodities, or utility tokens. On Stellar, you can create fungible tokens where each token has the same value and properties, with balances and ownership tracked through Soroban smart contracts.

Overview

The fungible module provides three different Fungible Token variants that differ in how certain features like token transfers and approvals are handled:

The module provides several implementation options to suit different use cases:

  1. Base implementation (FungibleToken with Base contract type): Suitable for most standard token use cases.
  2. AllowList extension (FungibleToken with AllowList contract type): For tokens that require an allowlist mechanism to control who can transfer tokens.
  3. BlockList extension (FungibleToken with BlockList contract type): For tokens that need to block specific addresses from transferring tokens.

These implementations share core functionality and a common interface, exposing identical contract functions as entry-points. However, the extensions provide specialized behavior by overriding certain functions to implement their specific requirements.

Usage

We’ll create a simple token for a game’s in-game currency. Players can earn tokens by completing tasks, and they can spend tokens on in-game items. The contract owner can mint new tokens as needed, and players can transfer tokens between accounts.

Here’s what a basic fungible token contract might look like:

use soroban_sdk::{contract, contractimpl, Address, Env, String};
use stellar_tokens::fungible::{burnable::FungibleBurnable, Base, ContractOverrides, FungibleToken};
use stellar_access::ownable::{self as ownable, Ownable};
use stellar_macros::{default_impl, only_owner};

#[contract]
pub struct GameCurrency;

#[contractimpl]
impl GameCurrency {
    pub fn __constructor(e: &Env, initial_owner: Address) {
        // Set token metadata
        Base::set_metadata(
            e,
            8, // 8 decimals
            String::from_str(e, "Game Currency"),
            String::from_str(e, "GCUR"),
        );

        // Set the contract owner
        ownable::set_owner(e, &initial_owner);
    }

    #[only_owner]
    pub fn mint_tokens(e: &Env, to: Address, amount: i128) {
        // Mint tokens to the recipient
        Base::mint(e, &to, amount);
    }
}

#[default_impl]
#[contractimpl]
impl FungibleToken for GameCurrency {
    type ContractType = Base;
}

#[default_impl]
#[contractimpl]
impl FungibleBurnable for GameCurrency {}

Extensions

The following optional extensions are provided:

- Burnable

Source Code

The FungibleBurnable trait extends the FungibleToken trait to provide the capability to burn tokens. To fully comply with the SEP-41 specification, a contract must implement both the FungibleToken and FungibleBurnable traits.

- Capped

Source Code

Unlike other extensions, the capped extension does not expose a separate trait. Instead, it offers helper functions designed to assist in implementing the mint function, enforcing a supply cap.

- AllowList

Source Code

The FungibleAllowList trait extends the FungibleToken trait to provide an allowlist mechanism that can be managed by an authorized account. This extension ensures that only allowed accounts can transfer/receive tokens or approve token transfers.

- BlockList

Source Code

The FungibleBlockList trait extends the FungibleToken trait to provide a blocklist mechanism that can be managed by an authorized account. This extension ensures that blocked accounts cannot transfer/receive tokens, or approve token transfers.

Stellar Asset Contract (SAC)

The Stellar Asset Contract (SAC) is a special built-in implementation of CAP-46-6 Smart Contract Standardized Asset and SEP-41 Token Interface. The SAC acts as a bridge between traditional Stellar assets and Soroban smart contracts.

SAC automatically wraps existing Stellar assets (like USDC, XLM, or any issued asset) so they can be used in smart contracts. Think of it as the "smart contract version" of any Stellar asset.

Key points to know:

  • Every Stellar asset gets its own SAC instance with a deterministic contract address
  • No bridging or wrapping tokens needed - it's the same asset, just accessible via smart contracts
  • Anyone can deploy a SAC for any asset (the original issuer doesn't need to be involved)
  • When you transfer between Stellar accounts and contracts, the balances are stored differently but represent the same underlying asset
  • SAC implements the same SEP-41 interface as OpenZeppelin fungible tokens

When to use SAC vs OpenZeppelin tokens:

  • Use SAC: When you want to interact with existing Stellar assets (USDC, XLM, etc.) in your smart contracts
  • Use OpenZeppelin: When creating new custom tokens with specialized logic, access controls, or unique tokenomics

For example, if you want to build a DeFi protocol that uses USDC, you'd deploy the SAC for USDC rather than creating a new token. Users can then interact with the same USDC they hold in their Stellar wallets directly through your smart contract.

Every SAC has an admin interface for privileged operations like minting or clawback. The OpenZeppelin fungible token module provides utilities that enable you to set a separate contract as the admin for these operations:

- SAC Admin Generic

Source Code

Provides generic admin functionality similar to the Stellar Asset Contract (SAC). This approach leverages the __check_auth function to handle authentication and authorization logic while maintaining a unified interface.

For detailed documentation, see SAC Admin Generic.

- SAC Admin Wrapper

Source Code

Provides a wrapper around the SAC admin functionality for easier integration. This approach defines specific entry points for each admin function and forwards calls to the corresponding SAC functions.

For detailed documentation, see SAC Admin Wrapper.

Compatibility and Compliance

The module is designed to ensure full compatibility with SEP-0041. It also closely mirrors the Ethereum ERC-20 standard, facilitating cross-ecosystem familiarity and ease of use.

To comply with the SEP-41 specification, a contract must implement both the FungibleToken and FungibleBurnable traits. These traits together provide all the necessary methods to conform to soroban_sdk::token::TokenInterface.

TTL Management

The library handles the TTL (Time-To-Live) of only temporary and persistent storage entries declared by the library. The instance TTL management is left to the implementor due to flexibility. The library exposes default values for extending the TTL: INSTANCE_TTL_THRESHOLD and INSTANCE_EXTEND_AMOUNT.

For a comprehensive understanding of Soroban's three storage types (temporary, persistent, and instance) and their archival behavior, see the State Archival documentation.