Mint Native Tokens with SDK

I want to mint native asset but I keep getting an error

FuelError: The transaction reverted with an unknown reason: 0

This is my function

const mintTokens = async () => {
        if (!provider || !account) {
            return;
        }
        const wallet = Wallet.fromAddress(account, provider);
        if (!wallet) {
            return;
        }
        console.log("mintTokens");
        const token = new Token(TOKEN_CONTRACT_ID, wallet);

        const subID = "0x0000000000000000000000000000000000000000000000000000000000000000";
        const mintAmount = bn(1000);
        const recipientAddress = Address.fromString(account);
        const recipientIdentity = { Address: { bits: recipientAddress.toB256() } };
        const { waitForResult } = await token.functions.mint(recipientIdentity, subID, mintAmount).call();
        console.log("waitForResult: ", waitForResult);
        const txResult = await waitForResult();

        console.log("txResult: ", txResult);

        const mintedAssetId = getMintedAssetId(token.id.toB256(), subID);
        console.log("mintedAssetId: ", mintedAssetId);
    }

My token contract

contract;

mod errors;
mod interface;

use standards::{src20::SRC20, src3::SRC3, src5::SRC5};
use std::{
    asset::{
        burn,
        mint_to,
    },
    call_frames::msg_asset_id,
    constants::DEFAULT_SUB_ID,
    context::msg_amount,
    string::String,
};

storage {
    DECIMALS: u8 = 9u8,
    NAME: str[8] = __to_str_array("FuelCoin"),
    SYMBOL: str[4] = __to_str_array("Fuel"),
    total_supply: u64 = 0,
}

impl SRC20 for Contract {
    #[storage(read)]
     fn total_assets() -> u64 {
        1
    }

    #[storage(read)]
   fn total_supply(asset: AssetId) -> Option<u64> {
        if asset == AssetId::default() {
            Some(storage.total_supply.read())
        } else {
            None
        }
    }

    #[storage(read)]
    fn name(asset: AssetId) -> Option<String> {
        if asset == AssetId::default() {
            Some(String::from_ascii_str(from_str_array(storage.NAME.read())))
        } else {
            None
        }
    }

    #[storage(read)]
    fn symbol(asset: AssetId) -> Option<String> {
        if asset == AssetId::default() {
            Some(String::from_ascii_str(from_str_array(storage.SYMBOL.read())))
        } else {
            None
        }
    }

    #[storage(read)]
    fn decimals(asset: AssetId) -> Option<u8> {
        if asset == AssetId::default() {
            Some(storage.DECIMALS.read())
        } else {
            None
        }
    }
}

impl SRC3 for Contract {
    #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64) {
         let sub_id = match sub_id {
            Some(s) => s,
            None => DEFAULT_SUB_ID,
        };
         require(sub_id == DEFAULT_SUB_ID, "incorrect-sub-id");
          storage
            .total_supply
            .write(amount + storage.total_supply.read());
        mint_to(recipient, DEFAULT_SUB_ID, amount);
     
    }
    #[payable]
    #[storage(read, write)]
    fn burn(sub_id: SubId, amount: u64) {
        require(sub_id == DEFAULT_SUB_ID, "incorrect-sub-id");
        require(msg_amount() >= amount, "incorrect-amount-provided");
        require(
            msg_asset_id() == AssetId::default(),
            "incorrect-asset-provided",
        );
 
        storage
            .total_supply
            .write(storage.total_supply.read() - amount);
        burn(DEFAULT_SUB_ID, amount);
    }
}

Hi @KITSCHO thanks for reporting this.

Given this is a relatively complex interaction, and I don’t want to make assumptions about your setup, could you link to a repository that has a reproduction of the issue? We have a bug template that you could include in the README.md to make debugging faster.

Also just for guidance, have you explored this guide for minting a token?

We also have a demo apps of Native Assets that you could take a look at.

Here is my project: GitHub - KristjanBajuk/fuel-native-asset
I’m reading the docs and demo apps, but I can’t get it to work.

So i deployed the contract to testnet and now I want to mint the tokens to the wallet…

And when I run the app in dev mode

  const { wallet } = useWallet();

returns undefined. But if I build the app and run it with the command next start it is returning the wallet instance.

I’m connecting to the app with the browser wallet.

I’d reccomend checking out our connectors app for info on using the various hooks.

I can see you’ve got 2 environment files, .env and .env.local. One sets your provider to local and one sets it to testnet. I assume when you run dev it is not connecting to a local wallet, but you do have a wallet configured to testnet in the browser extension.

Ok I get the instance of the wallet from useWallet now.

But I still have this issue of
FuelError: The transaction reverted with an unknown reason: 0

I took the same code for native asset from sway-playground. I deployed the contract succesfully. I call total_assets function and it returns the value correctly. But when I want to initialize owner with constructor I get an error. The same error as before when i tried to call mint function.

I have this TS script

 import {Address, Provider, Wallet, TESTNET_NETWORK_URL} from 'fuels';
import { TokenFactory } from '@/sway-api/token';
import dotenv from 'dotenv';

// Load environment variables from .env file
dotenv.config();

const WALLET_PVT_KEY = process.env.NEXT_PUBLIC_WALLET_PVT_KEY;


async function deployContract() {
    try {
        // Create the provider
        const provider = await Provider.create(TESTNET_NETWORK_URL as string);
        console.log("Provider initialized successfully");

        // Create the wallet
        const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY as string, provider);
        if (!wallet) {
            return;
        }
        console.log("Wallet created successfully");


        const factory = new TokenFactory(wallet);
        console.log("Factory instance created");

        // Deploy the contract using the factory instance
        const deployResult = await factory.deploy();
        console.log("Deploy initiated");

        // Wait for deployment results
        const { contract, transactionResult } = await deployResult.waitForResult();

        console.log('Contract deployed successfully with ID:', contract.id.toString());
        console.log('Transaction ID:', transactionResult.id);


        const { waitForResult } = await contract.functions.total_assets().call();
        console.log("waitForResult");
        const txResult = await waitForResult();
        console.log("total Assets: ", txResult.value.toString());

        const recipientAddress = Address.fromString(wallet.address.toString());
        const recipientIdentity = { Address: { bits: recipientAddress.toB256() } };


        // Call the constructor function with your address
        console.log("Initializing contract with owner address...");
        const { waitForResult: waitForConstructor } = await contract.functions.constructor(recipientIdentity).call();
        await waitForConstructor();
        console.log("Contract initialized with owner successfully.");

    } catch (error) {
        console.error("Error deploying contract:", error);
    }

This are the logs when I execute this script

Provider initialized successfully
Wallet created successfully
Factory instance created
Deploy initiated
Contract deployed successfully with ID: fuel19xl73swuuwryn7dwjaw56g9d3r68xw6zq36kwqtcz22lrmagtlts30el80
Transaction ID: 0x5e02e3c7ea650c1fe85ca9f165dda36d624d5140ae8724de54cbc3dbd20bda54
waitForResult
total Assets:  1
Initializing contract with owner address...
Error deploying contract: _FuelError: The transaction reverted with an unknown reason: 0


So if I remove the line where I’m updating total_supply in mint function then it works. coins get minted correctly to the recipient. But I don’t know why writing to the storage is not working.

This code does not work

  #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64) {
          let sub_id = sub_id.unwrap_or(DEFAULT_SUB_ID);
        require(sub_id == DEFAULT_SUB_ID, "incorrect-sub-id");
        require_access_owner();
        
        storage.total_supply.write(amount + storage.total_supply.read());
            
        mint_to(recipient, DEFAULT_SUB_ID, amount);
    }

This works

  #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64) {
          let sub_id = sub_id.unwrap_or(DEFAULT_SUB_ID);
        require(sub_id == DEFAULT_SUB_ID, "incorrect-sub-id");
        require_access_owner();
            
        mint_to(recipient, DEFAULT_SUB_ID, amount);
    }

This also works

  #[storage(read, write)]
    fn mint(recipient: Identity, sub_id: Option<SubId>, amount: u64) {
          let sub_id = sub_id.unwrap_or(DEFAULT_SUB_ID);
        require(sub_id == DEFAULT_SUB_ID, "incorrect-sub-id");
        require_access_owner();

        storage.total_supply.write(100);
            
        mint_to(recipient, DEFAULT_SUB_ID, amount);
    }

Upon further trial and error i guess that the reading from storage is the problem.

even if i try this I get the same error.

#[storage(read)]
    fn get_total_supply() -> u64 {
        storage.total_supply.read()
    }

It seems as if there are some other errors unrelated to the original issue that you are now encountering, you may want to explore introducing logs in your contract as well as looking at the transaction receipts to better understand why it’s reverting. They are returned in the Transaction Response in the TS SDK.

We also have methods for getting the logs when a transaction is reverted

I have put logs in the smart contract and catch the error in the typescript. but All i get is unknown error.

smart contract

Is it maybe a problem with a versions? I get this message before deploy.

The Fuel Node that you are trying to connect to is using fuel-core version 0.35.0,
which is not supported by the version of the TS SDK that you are using.
Things may not work as expected.
Supported fuel-core version: 0.34.0.

Here are my active versions

Installed toolchains
--------------------
beta-3-aarch64-apple-darwin
latest-aarch64-apple-darwin (default)
nightly-aarch64-apple-darwin
nightly-2024-05-28-aarch64-apple-darwin
testnet-aarch64-apple-darwin

active toolchain
----------------
latest-aarch64-apple-darwin (default)
  forc : 0.63.5
    - forc-client
      - forc-deploy : 0.63.5
      - forc-run : 0.63.5
    - forc-crypto : 0.63.5
    - forc-debug : 0.63.5
    - forc-doc : 0.63.5
    - forc-fmt : 0.63.5
    - forc-lsp : 0.63.5
    - forc-tx : 0.63.5
    - forc-wallet : 0.9.0
  fuel-core : 0.35.0
  fuel-core-keygen : 0.35.0

fuels versions
--------------
forc : 0.66.4
forc-wallet : 0.66.0

I’m deploying and testing this on the TESTNET not local fuel node.

So the solution and the problem is the same as here: Failed to deploy contract using typescript sdk - #17 by yuduan0220

The problem is that storage variables are somehow uninitialized.

Here the variable total_supply is somehow uninitialized.

storage {
    total_supply: u64 = 0,
    owner: State = State::Uninitialized,
}

I put this in the constructor

#[storage(read, write)]
    fn constructor(owner_: Identity) {
        storage.total_supply.write(0);
        storage.owner.write(State::Initialized(owner_));
    }

Now everything works fine. Mint function work as intended. Why is that?

Hi @KITSCHO sorry for the delayed response but it seems what’s happening here is that your storage slots are not being loaded.

Our Typegen tries to resolve, auto-load, and embed the Storage Slots for your contract so normally you shouldn’t have to override it for the variables to be initialized.

Could you try deploying the contract with the storage slots in the constructor and see if that removes the need for those lines in the contract’s constructor?

yes this works. I added storageSlots to deploy function

  const deployResult = await factory.deploy({
            storageSlots
        });

And now it works without that workaround in the constructor.

1 Like

Thanks for reporting that @KITSCHO I created an issue as I believe this may be a bug.

1 Like

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.