Icon SunFilledIcon MoonStars
Icon SunFilledIcon MoonStars

Icon LinkSubcurrency

The following is a simple example of a subcurrency which implements functionality to mint and send a 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::hash::sha256;
 
////////////////////////////////////////
// Event declarations
////////////////////////////////////////
//
// Events allow clients to react to changes in the contract.
// Unlike Solidity, events are simply structs.
//
/// 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 = Address::from(0x9299da6c73e6dc03eeabcce242bb347de3f5f56cd1c70926d76526d7ed199b8b);
 
////////////////////////////////////////
// Contract storage
////////////////////////////////////////
// Contract storage persists across transactions.
storage {
	balances: StorageMap<Address, u64> = StorageMap {},
}
 
////////////////////////////////////////
// ABI definitions
////////////////////////////////////////
/// Contract implements the `Token` ABI.
impl Token for Contract {
	#[storage(read, write)]
	fn mint(receiver: Address, amount: u64) {
		let sender = msg_sender().unwrap();
		let sender: Address = match sender {
			Identity::Address(addr) => {
				assert(addr == MINTER);
				addr
			},
			_ => revert(0),
		};
 
		// Increase the balance of receiver
		storage.balances.insert(receiver, storage.balances.get(receiver).try_read().unwrap_or(0) + amount);
	}
 
	#[storage(read, write)]
	fn send(receiver: Address, amount: u64) {
		let sender = msg_sender().unwrap();
		let sender = match sender {
			Identity::Address(addr) => addr,
			_ => revert(0),
		};
 
		// Reduce the balance of sender
		let sender_amount = storage.balances.get(sender).try_read().unwrap_or(0);
		assert(sender_amount > amount);
		storage.balances.insert(sender, sender_amount - amount);
 
		// Increase the balance of receiver
		storage.balances.insert(receiver, storage.balances.get(receiver).try_read().unwrap_or(0) + amount);
 
		log(Sent {
			from: sender,
			to: receiver,
			amount: amount,
		});
	}
}