Skip to main content
Version: v1.4

Mint an NFT

About NFTs

The Stardust update allows you to create your own NFTs. You can also use IRC27 for NFTs. This guide will show you how to create an IRC27 L1 NFT using a L2 smart contract.

Example Code

Ownership

You might want to look into making the function ownable with, for example, OpenZeppelin so only owners of the contract can call certain functionalities of your contract.

1. Check the Storage Deposit

Check if the amount paid to the contract is the same as the required storage deposit and set the allowance.

require(msg.value == _storageDeposit*(10**12), "Please send exact funds to pay for storage deposit");
ISCAssets memory allowance;
allowance.baseTokens = _storageDeposit;
Payable

Instead of making the function payable, you could let the contract pay for the storage deposit. If so, you will need to change the require statement to check if the contract's balance has enough funds:

require(address(this).balance > _storageDeposit);
  1. Get the senders AgentID:

The ISCAgentID represents the identifier of the agent (user or contract) whose NFTs you want to retrieve. You can get the AgentID from the sender by calling ISC.sandbox.getSenderAccount().

ISCAgentID memory agentID = ISC.sandbox.getSenderAccount();
  1. Create an IRC27Metadata struct with all the needed data:
tip

You can refer to Get NFT Metadata guide to understand how to create an IRC27Metadata

TheIRC27Metadata struct in Solidity is designed to hold the metadata for a Non-Fungible Token (NFT) according to the IRC27 standard. This struct includes various fields that describe the NFT, such as its standard, version, MIME type, URI, and name. Here's how to create and use this struct:

IRC27NFTMetadata memory metadata = IRC27NFTMetadata({
standard: "IRC27",
version: "v1.0",
mimeType: _mimeType,
uri: _uri,
name: _name
});
  1. Create all the data for the core contract call. To do so, you should create a new ISCDict with 2 parameters like specified in the reference docs for mintNFT
  • I is the immutable metadata we fill with the IRC27 metadata and
  • a is the AgendID of the owner of the NFT
ISCDict memory params = ISCDict(new ISCDictItem[](2));
params.items[0] = ISCDictItem("I", bytes(IRC27NFTMetadataToString(metadata)));
params.items[1] = ISCDictItem("a", agentID.data);
IRC27NFTMetadataToString

The full example below calls the IRC27NFTMetadataToString function, which simply converts the IRC27Metadata struct into a string.

  1. Call the magic contract call function with all the parameters. You should specify the core contract you want to call, which in this case is the account contract, and the function for minting an NFT
ISCDict memory ret = ISC.sandbox.call(
ISC.util.hn("accounts"),
ISC.util.hn("mintNFT"),
params,
allowance
);
  1. The call return value will contain a mintID which we can use in, for example, another contract function to get the actual L1 NFT ID once it is created using the accounts.NFTIDbyMintID function
function getNFTIDFromMintID(bytes memory mintID) public view returns (bytes memory) {
ISCDict memory params = ISCDict(new ISCDictItem[](1));
params.items[0] = ISCDictItem("D", mintID);

ISCDict memory ret = ISC.sandbox.callView(
ISC.util.hn("accounts"),
ISC.util.hn("NFTIDbyMintID"),
params
);
return ret.items[0].value;
}

Full Example Code

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@iota/iscmagic/ISC.sol";

contract NFTContract {
// Event emitted when a new NFT is minted
event MintedNFT(bytes mintID);


/// @notice Mints a new NFT with the provided metadata and storage deposit
/// @param _name The name of the NFT
/// @param _mimeType The MIME type of the NFT
/// @param _uri The URI where the NFT data is stored
/// @param _storageDeposit The amount of storage deposit required

function mintNFT(string memory _name, string memory _mimeType, string memory _uri, uint64 _storageDeposit) public payable {
require(msg.value == _storageDeposit*(10**12), "Please send exact funds to pay for storage deposit");

// Create an ISCAssets object for the allowance with the base tokens
ISCAssets memory allowance;
allowance.baseTokens = _storageDeposit;

// Retrieve the sender's account ID
ISCAgentID memory agentID = ISC.sandbox.getSenderAccount();

// Create the metadata for the NFT
IRC27NFTMetadata memory metadata = IRC27NFTMetadata({
standard: "IRC27",
version: "v1.0",
mimeType: _mimeType,
uri: _uri,
name: _name
});

// Prepare the parameters dictionary for the ISC call
ISCDict memory params = ISCDict(new ISCDictItem[](2));
params.items[0] = ISCDictItem("I", bytes(IRC27NFTMetadataToString(metadata)));
params.items[1] = ISCDictItem("a", agentID.data);

// Call the ISC sandbox to mint the NFT
ISCDict memory ret = ISC.sandbox.call(
ISC.util.hn("accounts"),
ISC.util.hn("mintNFT"),
params,
allowance
);

// Emit the MintedNFT event with the returned mint ID
emit MintedNFT(ret.items[0].value);
}


/// @notice Retrieves the NFT ID associated with a given mint ID
/// @param mintID The mint ID of the NFT
/// @return The NFT ID associated with the provided mint ID

function getNFTIDFromMintID(bytes memory mintID) public view returns (bytes memory) {
// Prepare the parameters dictionary for the ISC call
ISCDict memory params = ISCDict(new ISCDictItem[](1));
params.items[0] = ISCDictItem("D", mintID);

// Call the ISC sandbox to get the NFT ID
ISCDict memory ret = ISC.sandbox.callView(
ISC.util.hn("accounts"),
ISC.util.hn("NFTIDbyMintID"),
params
);

// Return the NFT ID
return ret.items[0].value;
}


/// @notice Converts an IRC27NFTMetadata struct to a JSON string
/// @param metadata The metadata to convert
/// @return The JSON string representation of the metadata

function IRC27NFTMetadataToString(IRC27NFTMetadata memory metadata)
public
pure
returns (string memory)
{
return string.concat(
'{"standard": "',
metadata.standard,
'", "version": "',
metadata.version,
'", "type": "',
metadata.mimeType,
'", "uri": "',
metadata.uri,
'", "name": "',
metadata.name,
'"}');
}
}