Create in-game assets
Estimate time to complete: 20 minutes
This tutorial covers one of the most important parts in constructing your web3 game. Here you will mint, and configure NFTs to power your in-game items. As part of this, you'll learn how to do this with our preset contracts for ERC721.
Note: This tutorial will only cover minting and will not cover when and how you would initiate a mint within a gameplay context.
This is a beginners guide, lots of the helpful context, important links and preparation material are in the blue collapsed sections. For those more confident, you can skip those.
We cover minting NFTs by contract function call and Minting API. For more information on the Minting API, check out our following product page.
To start, you may want to first familiarise yourself with these concepts
Concepts | Definition |
---|---|
In-game items | In-game items include cards, consumables, collectables, weapons, clothing and equipment that power the aesthetics and mechanics of your game. |
On-chain assets (NFTs) | In Web3 games, one of the fundamental value propositions is to power some, if not all your in-game items with on-chain assets. This tutorial uses ERC721 smart contracts to do this. |
Collections | When minting NFTs, individual NFTs are created as part of a collection. This guide will teach you how to do this. |
Metadata | To enable NFTs to be more descriptive, smart contracts can contain metadata, which is information about the special characteristics of each NFT. The metadata of an NFT can describe its characteristics and properties, such as its name, description, transaction history, traits, link to the hosted image, and more. This tutorial will teach you how to configure this. |
Task 1: Set up basic metadata hosting
To enable NFTs to be more descriptive, metadata is used to contain information about the special characteristics of each token. This can include its name, description, transaction history, traits, link to the hosted image, and more.
At scale, you will need to provide this information through a server, but we'll use Github Pages today to minimize the infrastructure setup. Begin by forking our Sample Project Metadata repository:
Sample Project Metadata
You should now have your very own repository! Next, we need to make this repository visible through Github Pages:
- Your Repository > Settings > Pages
- Set the Source to "deploy from a branch".
- Set the Branch to
main
ormaster
branch and click save.
Visual guide for setting up your Metadata repo
From the Sample Project Metadata repository you can select "Fork" in the toolbar.
Once forked, you should see "Settings" appear in your forked version of the repository.
Once in Settings, locte "Pages" on the left hand side menu and set up according to the instructions.
- Set the Source to "deploy from a branch".
- Set the Branch to
main
ormaster
branch and click save.
Task 2: Customise your NFT metadata
We've already provided you with 3 sample metadata files and images out of the box - let's make a few quick changes to customise your collection!
The minimum files you'll need to update are:
- Metadata files in the ../tokens/ directory: URLs for your images for each NFT (file name contains Token ID value: e.g. 1, 2, 3) under the
image
field; replacing thehttps://immutable.github.io/sample-project-metadata/tokens/token1.webp
,../tokens/token2.webp
and../tokens/token3.webp
values from the 3 samples provided. - collection.json: Name of your collection under the
name
anddescription
fields. Also populate the collection image URL under theimage
field
https://<your github name>.github.io/<your fork name>/<directory>/<filename>
{
"name": "YOUR_COLLECTION_NAME",
"description": "YOUR_COLLECTION_DESCRIPTION",
"image": "YOUR_IMAGE_URL",
"external_link": "YOUR_WEBSITE_URL"
}
To modify the metadata of the individual NFTs, we need to modify the json files in the /tokens
folder. You'll note that the files are named 1
, 2
and 3
: this corresponds exactly to the unique token_id
of each token. NFT metadata follows the standard format defined in ERC-721, and should look roughly like this:
{
"id": 1,
"image": "YOUR_IMAGE_URL",
"token_id": "1",
"background_color": null,
"animation_url": null,
"youtube_url": null,
"name": "Test zkEVM NFT",
"description": "This NFT is my first zkEVM NFT created on Immutable",
"external_url": null,
"attributes": [
{
"trait_type": "Hat",
"value": "Top Hat"
},
{
"trait_type": "Coat",
"value": "Tails"
},
{
"trait_type": "Neck",
"value": "Bow Tie"
}
]
}
As you've probably noticed, modifying this metadata for each NFT individually is slow and frustrating. This is OK for our quickstart tutorial, but is not recommended for real games! If you want to learn how to set up metadata hosting at scale, see our metadata documentation.
We now have two very important URLs - remember these for later!
- Contract Metadata URI:
https://<your github name>.github.io/<your fork name>/collection.json
- Token Base URI:
https://<your github name>.github.io/<your fork name>/tokens/
Blockscout currently requires a '/' at the end of the Token Base URI to index metadata correctly. Immutable's Blockchain Data API does not require this, however for consistency we recommend adding a '/' to the end of the Token Base URI for Blockscout compatibility.
Task 3: Setup admin wallet
For the purpose of this tutorial, all you'll need to do is download the Metamask browser wallet and follow the steps to store your seed phrase in a safe location.
Immutable Hub will take care of setting up the chain network details and faucet the tIMX
tokens (for Testnet only) you'll need to pay for gas automatically in Task 4 as you deploy your contract.
If you need to manually top up your tIMX
tokens you can do so via Immutable Hub by visiting the Faucet.
Task 4: Deploy ERC-721 preset contract
Go to the project you've previously set up in Immutable Developer Hub.
Connect your minting Metamask wallet by clicking the Connect Wallet
button on the top right menu of Hub and navigate to your project and environment.
In the Collections
submenu, click Deploy contract
:
- Create a Name for your collection
- Create a Symbol for your collection that is 2-3 characters long
- Paste the
Token Base URI
from above into theBase URI
field - Paste the
Contract Metadata URI
from above into theCollection Metadata URI
field
- Enter the wallet address you created earlier as the royalty recipient
- Enter
5
for Royalty fee. For the purpose of this tutorial, we will set royalty to be 5% default. - Click on the
Use connected wallet
button forMinter Address
. This will automatically populate the field with your connected wallet address, allowing it to mint tokens. Alternatively, you can enter the address of another wallet. - If you want to use the Minting API, check the
Enable Minting API
checkbox. - Click
Deploy
- Hub will automatically check your wallet's connected network. If your network is not already connected to the respective chain (IMX zkEVM Testnet or IMX zkEVM Mainnet), then you will prompted in Metamask to switch. Here, select
Switch Network
. If you are deploying on Testnet,tIMX
will be automatically given to you via our faucet as part of the process.
- You will be prompted to sign a transaction with your wallet. This step costs gas and will deploy your contract.
- You will be prompted to sign a message with your wallet. This step is gasless and will link your collection to your project's environment.
- Finally, you will be prompted to sign a transaction with your wallet. This step costs gas and will assign your wallet the minter role, so that you can mint tokens on the collection.
- Set up your admin wallet and the zkEVM Testnet Network by following instructions here
- Follow instructions on deploying your contracts via code and request to be allowlisted
Task 5: Set up your project backend (node.js)
We have already created a sample project which you can clone to mint quickly and easily:
Basic Preset Minting
git clone https://github.com/immutable/basic-preset-minting
cd basic-preset-minting
npm i
Task 6: Grant minting permissions
This step is not required if you are using the Minting API.
To mint NFTs, we need to use our "owner" wallet (which deployed our contract) to grant another wallet minting rights for our collection. In this tutorial, we're going to add the "minter" role to our existing "owner", so you don't need to set up a new wallet.
First, we will need to export your owner wallet's private key so it can be used in local scripts.
6.1 Locate your wallet's private key
- Open Metamask
- Make sure the network at the top of the screen is the test network you wish to deploy to
- Open Account details by clicking on the following button and selecting
Account details
- Select
Show Private Key
and enter your password - Copy the long string provided (your private key) as this will be needed in the next step.
6.2 Add your private key to the .env
file
We now need to configure your project with your private key in order to grant it the requisite authorisation to mint tokens. First, copy the code in .env.sample
into a new .env
file. Next, add the two variables below - the remaining variables will be set later:
CONTRACT_ADDRESS
- The contract address of your collection - you can find this on Immutable Hub.OWNER_PRIVATE_KEY
- The private key you exported.
6.3 Perform the role grant
To complete the grant, simply run the following script:
npm run grant-role
Task 7: Mint tokens
You can mint tokens in three different ways: via a contract function call, via the Minting API with a reference ID, or via the Minting API with a token ID. Select the option that best suits your needs below:
- Minting API - Gas Efficient
- Minting API with Token ID
- Contract Function Call
To gain access to the minting API on mainnet, please complete the following steps in Hub. Kindly note that during the initial launch period, the minting API is exclusively available to partners under contract with Immutable.
The Minting API is available to all developers using Immutable's preset contracts on testnet.
The Minting API allows you to mint NFTs along with associated metadata through a straightforward API call.
This option uses mintBatchByQuantity() function which is the most gas efficient method for minting.
The assets token_id
is generated by the platform and published via the Blockchain Data API once the mint is completed.
For testing the Minting API, we suggest using an API platform such as Postman.
The following fields will be required to use the Minting API:
CONTRACT_ADDRESS
- The contract address of your collection - you can find this on Immutable Hub.SECRET_API_KEY
- Secret key for your environment obtained from your Hub account. Follow this guide to set yours up. This is not your wallets private key.REFERENCE_ID_ONE
- The internal content creator reference ID of the first asset being minted. This internal ID is used to link the request to the system-generated token ID. Each number must be unique for each collection's successful mints.REFERENCE_ID_TWO
- The internal content creator reference ID of the second asset being minted. This internal ID is used to link the request to the system-generated token ID. Each number must be unique for each collection's successful mints.RECIPIENT_ONE
- The first address of the wallet that will own the NFTs being minted in the batch.RECIPIENT_TWO
- The second address of the wallet that will own the NFTs being minted in the batch.
7.1.1: Authorizing Minting API for collection minting
The initial step involves authorizing the Minting API to perform minting operations for your collection. This is done by giving the Minting API the minter role, similar to what is done in Step 6 if you are minting tokens via the contract function call.
Our sample project contains a script to assist with this step. After completing steps 6.1 and 6.2, run the following command from the sample project basic-preset-minting
:
npm run grant-role-minting-api
7.1.2: Minting assets with metadata: Minting without token_id
for maximum gas efficiency
In this example, we will mint 2 NFTs destined for 2 different recipients.
It's important to note that every minting request through the Minting API must include an internal, unique reference_id
. Once a mint is successful, this reference_id
cannot be reused for additional mints within the same collection.
- SDK
- API
const chainName = 'imtbl-zkevm-testnet';
const contractAddress = CONTRACT_ADDRESS;
const response = await client.createMintRequest({
chainName,
contractAddress,
createMintRequestRequest: {
assets: [
{
owner_address: RECIPIENT_ONE,
reference_id: REFERENCE_ID_ONE,
metadata: {
image:
'https://immutable.github.io/sample-project-metadata/tokens/token1.webp',
animation_url: null,
youtube_url: null,
name: 'Test zkEVM NFT #1',
description:
'This NFT is my first zkEVM NFT created on Immutable',
external_url: null,
attributes: [
{
trait_type: 'Hat',
value: 'Top Hat',
},
{
trait_type: 'Coat',
value: 'Tails',
},
{
trait_type: 'Neck',
value: 'Bow Tie',
}
]
}
},
{
owner_address: RECIPIENT_TWO,
reference_id: REFERENCE_ID_TWO,
metadata: {
image:
'https://immutable.github.io/sample-project-metadata/tokens/token2.webp',
animation_url: null,
youtube_url: null,
name: 'Test zkEVM NFT #2',
description:
'This NFT is my second zkEVM NFT created on Immutable',
external_url: null,
attributes: [
{
trait_type: 'Hat',
value: 'Top Hat',
},
{
trait_type: 'Coat',
value: 'Tails',
},
{
trait_type: 'Neck',
value: 'Bow Tie',
}
]
}
}
]
}
});
POST {baseURL}/v1/chains/{chain_name}/collections/{contract_address}/nfts/mint-requests
7.1.3: Retrieving the Token ID
Execute the following command to retrieve the token_id
of your newly minted NFT:
- SDK
- API
const chainName = 'imtbl-zkevm-testnet';
const contractAddress = CONTRACT_ADDRESS;
const referenceId = REFERENCE_ID_ONE;
const response = await client.getMintRequest({chainName, contractAddress, referenceId});
GET {baseURL}/v1/chains/{chain_name}/collections/{contract_address}/nfts/mint-requests/{reference_id}
If the status in the results is "pending", wait a few seconds and try again. Once the mint is successful, the status will change to "succeeded".
The returned results should resemble the following sample, with the status indicating "succeeded" to confirm the successful mint.
{
"page": {
"next_cursor": null,
"previous_cursor": null
},
"result": [
{
"chain": {
"id": "eip155:13473",
"name": "imtbl-zkevm-testnet"
},
"collection_address": "0xe2e94d611d50370612e9721254807b7874093fb6",
"created_at": "2024-02-12T05:35:35.743242Z",
"error": null,
"owner_address": "0x68209e7086032941a8Cb14352c2F43b086288791",
"reference_id": "REFERENCE_ID_ONE",
"status": "succeeded",
"token_id": "TOKEN_ID_ONE",
"activity_id": "4e28df8d-f65c-4c11-ba04-6a9dd47b179b",
"transaction_hash": "0x6890c450a43e6f3e90b311e2c0e80e1e6880cbc93ab977fc5357ac66cd255800",
"updated_at": "2024-02-12T05:35:35.743242Z"
},
]
}
Get the corresponding token_id
for both REFERENCE_ID_ONE
and REFERENCE_ID_TWO
.
7.1.4: Updating metadata files
If you've been following this guide, you'll need to update the corresponding metadata file that you set up in Task 2 for each asset.
Here are the changes you need to make:
- Rename the file to match the system-generated
token_id
obtained from the previous step. - Update the
id
attribute to match the system-generatedtoken_id
obtained from the previous step. - Update the
token_id
attribute to match the system-generatedtoken_id
obtained from the previous step. - Save the file.
For future mints, you can delay Task 2 until you've obtained the token_id
.
However, after minting, ensure that the metadata file is created and that the metadata submitted with the mint request matches this file. Some ecosystem partners use the metadata source file rather than the Blockchain Data API. To ensure a consistent experience across the platform, always ensure that the Blockchain Data API and the local metadata file are consistent.
To gain access to the minting API on mainnet, please complete the following steps in Hub. Kindly note that during the initial launch period, the minting API is exclusively available to partners under contract with Immutable.
The Minting API is available to all developers using Immutable's preset contracts on testnet.
The Minting API allows you to mint NFTs along with associated metadata through a straightforward API call.
This option uses mintBatch() function which is a less gas efficient method for minting than mintBatchByQuantity(), however allows the minter to specify token_id
as part of the mint request. reference_id
are required to check the status of the mint request. token_id
and reference_id
can be the same provided they are unique to the collection.
For testing the Minting API, we suggest using an API platform such as Postman.
The following fields will be required to use the Minting API:
CONTRACT_ADDRESS
- The contract address of your collection - you can find this on Immutable Hub.SECRET_API_KEY
- Secret key for your environment obtained from your Hub account. Follow this guide to set yours up. This is not your wallets private key.TOKEN_ID_ONE
- The ID of the first NFT your are creating. All NFTs belonging to a single collection must have a uniquetoken_id
.TOKEN_ID_TWO
- The ID of the second NFT your are creating. All NFTs belonging to a single collection must have a uniquetoken_id
.REFERENCE_ID_ONE
- The internal content creator reference ID of the first asset being minted. This internal ID is used to link the request to the system-generated token ID. Each number must be unique for each collection's successful mints. Can be the same value astoken_id
.REFERENCE_ID_TWO
- The internal content creator reference ID of the second asset being minted. This internal ID is used to link the request to the system-generated token ID. Each number must be unique for each collection's successful mints. Can be the same value astoken_id
.RECIPIENT_ONE
- The first address of the wallet that will own the NFTs being minted in the batch.RECIPIENT_TWO
- The second address of the wallet that will own the NFTs being minted in the batch
7.2.1: Authorizing Minting API for collection minting
The initial step involves authorizing the Minting API to perform minting operations for your collection. This is done by giving the Minting API the minter role, similar to what is done in Step 6 if you are minting tokens via the contract function call.
Our sample project contains a script to assist with this step. After completing steps 6.1 and 6.2, run the following command from the sample project basic-preset-minting
:
npm run grant-role-minting-api
7.2.2: Prepare the assets metadata
Ensure you have completed Task 2 and that your metadata file is saved in your local directory.
Even though you supply metadata with the Minting API request some ecosystem partners index metadata from your local metadata directory.
7.2.3: Minting assets with metadata
In this example, we will mint 2 NFTs destined for 2 different recipients.
The mintBatch()
function is triggered by providing token_id
in the mint request.
It's important to note that every minting request through the Minting API must include an internal, unique reference_id
. Once a mint is successful, this reference_id
cannot be reused for additional mints within the same collection. reference_id
and token_id
can be the same value.
- SDK
- API
const chainName = 'imtbl-zkevm-testnet';
const contractAddress = CONTRACT_ADDRESS;
const response = await client.createMintRequest({
chainName,
contractAddress,
createMintRequestRequest: {
assets: [
{
owner_address: RECIPIENT_ONE,
reference_id: REFERENCE_ID_ONE,
token_id: TOKEN_ID_ONE,
metadata: {
image:
'https://immutable.github.io/sample-project-metadata/tokens/token1.webp',
animation_url: null,
youtube_url: null,
name: 'Test zkEVM NFT #1',
description:
'This NFT is my first zkEVM NFT created on Immutable',
external_url: null,
attributes: [
{
trait_type: 'Hat',
value: 'Top Hat',
},
{
trait_type: 'Coat',
value: 'Tails',
},
{
trait_type: 'Neck',
value: 'Bow Tie',
}
]
}
},
{
owner_address: RECIPIENT_TWO,
reference_id: REFERENCE_ID_TWO,
token_id: TOKEN_ID_TWO,
metadata: {
image:
'https://immutable.github.io/sample-project-metadata/tokens/token2.webp',
animation_url: null,
youtube_url: null,
name: 'Test zkEVM NFT #2',
description:
'This NFT is my second zkEVM NFT created on Immutable',
external_url: null,
attributes: [
{
trait_type: 'Hat',
value: 'Top Hat',
},
{
trait_type: 'Coat',
value: 'Tails',
},
{
trait_type: 'Neck',
value: 'Bow Tie',
}
]
}
}
]
}
});
POST {baseURL}/v1/chains/{chain_name}/collections/{contract_address}/nfts/mint-requests
7.2.4: Checking the status of the mint
Execute the following command to check the status of the mint request:
- SDK
- API
const chainName = 'imtbl-zkevm-testnet';
const contractAddress = CONTRACT_ADDRESS;
const referenceId = REFERENCE_ID_ONE;
const response = await client.getMintRequest({chainName, contractAddress, referenceId});
GET {baseURL}/v1/chains/{chain_name}/collections/{contract_address}/nfts/mint-requests/{reference_id}
If the status in the results is "pending", wait a few seconds and try again. Once the mint is successful, the status will change to "succeeded".
The returned results should resemble the following sample, with the status indicating "succeeded" to confirm the successful mint.
{
"page": {
"next_cursor": null,
"previous_cursor": null
},
"result": [
{
"chain": {
"id": "eip155:13473",
"name": "imtbl-zkevm-testnet"
},
"collection_address": "0xe2e94d611d50370612e9721254807b7874093fb6",
"created_at": "2024-02-12T05:35:35.743242Z",
"error": null,
"owner_address": "0x68209e7086032941a8Cb14352c2F43b086288791",
"reference_id": "REFERENCE_ID_ONE",
"status": "succeeded",
"token_id": "TOKEN_ID_ONE",
"activity_id": "4e28df8d-f65c-4c11-ba04-6a9dd47b179b",
"transaction_hash": "0x6890c450a43e6f3e90b311e2c0e80e1e6880cbc93ab977fc5357ac66cd255800",
"updated_at": "2024-02-12T05:35:35.743242Z"
},
]
}
Immutable's recommended ERC721 preset contract has multiple batch minting strategies tailored for different minting scenarios, which can save significant gas costs while minting.
7.3.1: Minting directly via the collection's mint function
To mint our first NFTs, we're once again going to use a sample script from the repository we cloned earlier. In the sample script, we mint 3 NFTs and distribute them to 2 recipient wallets - you can change this to suit your requirements.
All we need to do is ensure the remaining variables are now set in our .env
file:
MINTER_PRIVATE_KEY
- The private key of the wallet we just granted the minter role (may be same as Owner Private Key)RECIPIENT_ONE
- The first address of the wallet that will own the NFTs being minted in the batchRECIPIENT_TWO
- The second address of the wallet that will own the NFTs being minted in the batch
Now all that's left is to execute the mint itself:
npm run mint
8: Have Immutable verify your assets
Once you've deployed your collection or token, Immutable recommends all games verify their asset contracts, as it enhances player and trader confidence in engaging with transactions involving verified assets. For detailed information about Immutable's asset verification procedures, please refer to this guide.
To start the verification process, contact your Immutable representative or reach out to Immutable support on Discord.
Once verified, you can monitor the verification status of your assets in your Hub account.
If you deploy an erroneous collection on mainnet, you can notify Immutable to mark it as Inactive
. This status indicates to your gaming community and marketplaces that the assets associated with this collection should be ignored.