Transaction Fails When Creating and Minting a New Asset

Hi everyone,

I’m working on a React form component to collect data and create a new asset on the Sui blockchain. The process includes two steps:

  1. Creating the asset** using the new_asset function from my backend.
  2. Minting tokens** for the asset using the mint function.

When I trigger the process, the wallet prompts me to approve the transaction, but it fails after approval. I suspect the issue might be related to how the data is formatted or passed as arguments.

Below is the relevant code for your reference:

const handleSubmit = async (e) => {
    e.preventDefault();

    try {
        const tx = new Transaction();

        // Form data
        const assetName = createTokenForm.asset_name || "";
        const metadata = createTokenForm.metadata || {};
        const supplyCap = createTokenForm.supply_cap[0];

        const keys = [
            "property_name",
            "location",
            "size_sqm",
            "year_acquired",
            "owner_name",
            "plot_number",
        ];

        const values = [
            metadata.property_name || "",
            metadata.location || "",
            metadata.size_sqm?.toString() || "",
            metadata.year_acquired?.toString() || "",
            metadata.owner_name || "",
            metadata.plot_number || "",
        ];

        if (supplyCap <= 0) {
            alert("Supply cap must be greater than zero.");
            return;
        }

        // Serialize arguments
        const serializedAssetName = bcs.string().serialize(assetName).toBytes();
        const serializedSupplyCap = bcs.u64().serialize(BigInt(supplyCap)).toBytes();
        const serializedKeys = bcs.vector(bcs.string()).serialize(keys).toBytes();
        const serializedValues = bcs.vector(bcs.string()).serialize(values).toBytes();

        // Create the asset
        tx.moveCall({
            target: `${packageId}::sui_land::tokenized_asset::new_asset`,
            arguments: [
                tx.pure(serializedSupplyCap),
                tx.pure(serializedAssetName),
                tx.pure(serializedKeys),
                tx.pure(serializedValues),
            ],
        });

        const result = await signAndExecuteTransaction({ transaction: tx });

        const assetCapId = result.effects?.events?.[0]?.data?.asset_cap_id;
        if (!assetCapId) {
            alert("Asset creation failed. No asset cap ID returned.");
            return;
        }

        // Mint tokens
        const mintTx = new Transaction();
        mintTx.moveCall({
            target: `${packageId}::sui_land::tokenized_asset::mint`,
            arguments: [
                tx.object(assetCapId),
                tx.pure(serializedKeys),
                tx.pure(serializedValues),
                tx.pure(serializedSupplyCap),
            ],
        });

        const mintResult = await signAndExecuteTransaction({
            transaction: mintTx,
        });

        if (mintResult.effects?.status === "success") {
            alert("Token minted successfully!");
        } else {
            alert("Minting failed.");
        }
    } catch (error) {
        console.error("Error creating and minting token:", error);
        alert("An error occurred while creating and minting the token.");
    }
};

The backend implementation of the new_asset function can be found in the [MystenLabs Asset Tokenization GitHub repository

public fun new_asset<T: drop>(
        witness: T,
        total_supply: u64,
        symbol: ascii::String,
        name: String,
        description: String,
        icon_url: Option<Url>,
        burnable: bool,
        ctx: &mut TxContext
    ): (AssetCap<T>, AssetMetadata<T>) {
        assert!(sui::types::is_one_time_witness(&witness), EBadWitness);
        assert!(total_supply > 0, EInsufficientTotalSupply);

        let asset_cap = AssetCap {
            id: object::new(ctx),
            supply: balance::create_supply(witness),
            total_supply,
            burnable
        };

        let asset_metadata = AssetMetadata {
            id: object::new(ctx),
            name,
            total_supply,
            symbol,
            description,
            icon_url
        };

        emit(AssetCreated {
            asset_metadata: object::id(&asset_metadata),
            name: type_name::into_string(type_name::get<T>()) 
        });

        (asset_cap, asset_metadata)
    }

I’ll appreciate any help i can get regards this