Subcurrency

The following is a simple example of a subcurrency which implements functionality to mint and send token. It is a ledger-based token, i.e. the contract maintains a ledger of user account balances.

Being a ledger-based token, this example does not use Fuel's native asset system. It is not recommended to actually use ledger-based tokens in production; this example is here purely for illustrative purposes.

contract;

use std::{
    address::Address,
    assert::assert,
    chain::auth::{AuthError, msg_sender},
    hash::sha256,
    identity::Identity,
    logging::log,
    result::*,
    revert::revert,
    storage::StorageMap,
};

////////////////////////////////////////
// Event declarations
////////////////////////////////////////

// Events allow clients to react to changes in the contract.
// Unlike Solidity, events are simply structs.
// Note: Logging of arbitrary stack types is supported, however they cannot yet
//  be decoded on the SDK side.

/// Emitted when a token is sent.
struct Sent {
    from: Address,
    to: Address,
    amount: u64,
}

////////////////////////////////////////
// ABI method declarations
////////////////////////////////////////

/// ABI for a subcurrency.
abi Token {
    // Mint new tokens and send to an address.
    // Can only be called by the contract creator.
    #[storage(read, write)]fn mint(receiver: Address, amount: u64);

    // Sends an amount of an existing token.
    // Can be called from any address.
    #[storage(read, write)]fn send(receiver: Address, amount: u64);
}

////////////////////////////////////////
// Constants
////////////////////////////////////////

/// Address of contract creator.
const MINTER: b256 = 0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b;

////////////////////////////////////////
// Contract storage
////////////////////////////////////////

// Contract storage persists across transactions.
storage {
    balances: StorageMap<Address,
    u64>, 
}

////////////////////////////////////////
// ABI definitions
////////////////////////////////////////

/// Contract implements the `Token` ABI.
impl Token for Contract {
    #[storage(read, write)]fn mint(receiver: Address, amount: u64) {
        // Note: The return type of `msg_sender()` can be inferred by the
        // compiler. It is shown here for explicitness.
        let sender: Result<Identity, AuthError> = msg_sender();
        let sender: Address = match sender.unwrap() {
            Identity::Address(addr) => {
                assert(addr == ~Address::from(MINTER));
                addr
            },
            _ => {
                revert(0);
            },
        };

        // Increase the balance of receiver
        storage.balances.insert(receiver, storage.balances.get(receiver) + amount)
    }

    #[storage(read, write)]fn send(receiver: Address, amount: u64) {
        // Note: The return type of `msg_sender()` can be inferred by the
        // compiler. It is shown here for explicitness.
        let sender: Result<Identity, AuthError> = msg_sender();
        let sender: Address = match sender.unwrap() {
            Identity::Address(addr) => {
                assert(addr == ~Address::from(MINTER));
                addr
            },
            _ => {
                revert(0);
            },
        };

        // Reduce the balance of sender
        let sender_amount = storage.balances.get(sender);
        assert(sender_amount > amount);
        storage.balances.insert(sender, sender_amount - amount);

        // Increase the balance of receiver
        storage.balances.insert(receiver, storage.balances.get(receiver) + amount);

        log(Sent {
            from: sender, to: receiver, amount: amount
        });
    }
}