Minting on Immutable X

This guide will go through the basics of creating a contract and collecting the information needed by Immutable for asset minting. Blockchain interaction is required, so this guide will provide signing samples for both a programmatically generated wallet and Metamask (which will require a few more steps).

❗️

External URLs

Examples using external references or applications should be taken with caution as they are not controlled by Immutable. Blockchain transactions are permanent, so use caution and test thoroughly on the testnet before going live.

0. Setup

Create a file with the following fields. This will be used to help keep track of the important values Immutable will require to successfully register your account.

Name this file registrationConfigs.ts

{
   publicKey: "",
   metadataUri: "",
   contractAddress: "",
   metadataSchema: ""
}

1. Contract setup

There are two pieces of data needed from the contract deployment

  • Address of the deployed contract
  • The public key (not address) that is the designated minter

Developers familiar with contract development or those who already have contracts deployed, see the readme at @imtbl/imx-contracts to add the required additions for minting. Skip to Get the Contract Address once completed.

For those new to contract creation, an example is shown below using third party tools.

🚧

Contract Sample

This example depends on open source library OpenZepplin (contracts library) and Remix, a web based Ethereum IDE. Please familiarize yourself with the tools before using.

Download the Sample Contract

  1. Install the newest sample contract using npm
npm i @imtbl/imx-contracts
  1. Find the Asset.sol file in the contracts folder of the imx-contracts location of the installed node module. This will be used as the initial code for the contract.

Import the Contract into Remix

Remix is an open source web application used to build and deploy Ethereum contracts. It will be used to deploy and verify the contract without additional software installed.

  1. Navigate to the Remix IDE and create a new workspace.

  2. Copy the Asset.sol file from the previous step into the contracts folder of the workspace.

  3. Update the import for Mintable.sol to point to the remote repository.

    //import "./Mintable.sol";
    import "@imtbl/imx-contracts/contracts/Mintable.sol";
    
  4. Update the mintFor function to handle any metadata returned from the initial blueprint data, such as the asset URI, set from minting. See Asset Minting for details.

🚧

Chain Awareness

Contracts do not have the same address on the Mainnet and Testnet. Make sure your wallet is connected to the correct chain before deploying.

Use Remix to Compile and Deploy the Contract

  1. Go to the Solidity compiler tab and enable optimization and auto compile, then compile your contract.
  2. Go to the Deploy and run transactions tab and change the environment to Injected Web3 (for metamask) and the contract to Asset.sol.
  3. Expand the deploy section and add the parameters needed for the contract
  • owner - the public wallet address you choose to be the minter of the contract
  • name - The name of ERC721 Token
  • symbol - The symbol of the Token
  • imx - The address of the Immutable X contract depending on deployment location, either 0x4527be8f31e2ebfbef4fcaddb5a17447b27d2aef (Testnet) or 0x5FDCCA53617f4d2b9134B29090C87D01058e27e9 (Mainnet)
  1. Click transact to send the contract creation transaction to you wallet and complete the signing.

Use Remix to Verify the Contract (Optional)

Most experienced users expect to see the source code for a contract once deployed. Here are two ways to get the contract verified so the source code can be seen.

Sourcify

Verifying the contract with Sourcify is simple.

  1. Click on the Plugin manager tab and install the Sourcify plugin.
  2. Click on the Sourcify tab, then select the Verify section.
  3. Select the chain the contract was deployed on and input the contract address.
  4. Click Verify.

Etherscan

Verifying the contract on Etherscan is a little more complicated. The simplest way is combining the contract source code into a single file and uploading it to Etherscan. Because the code will be merged into one file, make sure all source code files have the same usage license.

  1. Use the Plugin manager tab to install the Flattener plugin.
  2. Click on the Flattener plugin and select Flatten Asset.sol
  3. Save and open the flattened file.
  4. Etherscan will not accept a file with more than one pragma solidity... statement, so remove all but the top one.
  5. Flattening the files does not guarantee the correct order for the code. You will need to fix any compilation errors shown in Remix. Usually it is just the reordering of functions.
  6. Once the file has no errors, navigate to the contract on Etherscan and select Verify and Publish on the Contract tab.
  7. For the compiler type, select Solidity (Single File), then choose the compiler and license type matching your file. Click continue.
  8. Copy the code from the flattened file into Code area, and change the Optimization drop down to Yes .
  9. Select Verify and Publish.

Get the Contract Address

Once the contract has been successfully deployed, copy the address of the deployed contract into the contractAddress field of the registrationConfigs.ts file.

Get the Public Key

The publicKey field in registrationConfigs.ts needs to be set to the public key of the contract deploying account. This is not the same as the public address of the account.

Extracting from programmatic wallet

If you have the private key for wallet, you can use the ethers library to extract the public key:

import { Wallet } from '@ethersproject/wallet';
const privateKey = '<enter private key>';
console.log(new Wallet(privateKey).publicKey);

Extracting from Metamask

If you used Metamask to publish the contract, you will need to extract the public key from the specific address. The following html page is a quick way to retrieve the signature. Just run the page from a local web server or copy and paste to a code sandbox like JSFiddle to execute.

<!DOCTYPE html>
<html>
<head>
   <title>Get Public Key</title>
</head>

<body>
   <div style="margin-bottom:5px">Enable Metamask, then click on the sign button and sign with your wallet to get your public key</div>
   <div style="margin-bottom:5px"><button onclick="enableMM()">Enable Metamask</button></div>
   <div style="margin-bottom:20px"><button onclick="findPublicKey()">Sign</button></div>
   <div>Public key: <span id='public-key'></span></div>
   <script src="https://cdn.ethers.io/lib/ethers-5.0.umd.min.js" type="application/javascript"></script>
   <script>

      function enableMM() {
         window.ethereum.send('eth_requestAccounts');
      }

      async function findPublicKey() {
         const provider = new ethers.providers.Web3Provider(window.ethereum);
         const signer = provider.getSigner();
         const message = 'Get Public Key';
         const signature = await signer.signMessage(message);
         const digest = ethers.utils.arrayify(ethers.utils.hashMessage(message));
         const publicKey = await ethers.utils.recoverPublicKey(digest, signature);
         const address = await ethers.utils.recoverAddress(digest, signature);

         document.getElementById("public-key").innerHTML = "" + publicKey;
      };

   </script>
</body>
</html>

Once you have retrieved the public key, add it to the registrationConfigs.ts file.

2. Metadata API

The Immutable metadata crawler will access /<token_id> i.e. "https://sameendpoint.com/1" at the time of minting a new token. Please be sure that your endpoint returns a JSON response for each token ID that will be minted.

See https://docs.x.immutable.com/docs/asset-metadata for schema\

Valid metadata schema example:

{
    "metadata": [{
            "name": "attack",
            "range": {
                "min": 0,
                "max": 16
            },
            "type": "discrete",
            "filterable": true
        },
        {
            "name": "collectable",
            "type": "boolean",
            "filterable": true
        },
        {
            "name": "god",
            "value": [
                "death",
                "deception",
                "light",
                "magic",
                "nature",
                "neutral",
                "war"
            ],
            "type": "enum",
            "filterable": true
        },
        {
            "name": "name",
            "type": "text"
        }

    ]
}

Metadata output example:

{
    "attack": 0,
    "effect": "Target an opponent.\u003cbr\u003eDelve a card belonging to\u003cbr\u003ethat god's domain.\u003cbr\u003e(The domains are Death, Deception, Light, Magic, Nature, and War.)",
    "god": "deception",
    "health": 0,
    "image": "https://card.godsunchained.com/?id=848\u0026q=4",
    "mana": 1,
    "name": "Stolen Plans",
    "proto": 848,
    "quality": "Meteorite",
    "rarity": "common",
    "set": "trial",
    "type": "card"
}

Once you have designed a schema:

  • Copy the stringified version of your schema
  • Open registrationConfigs.ts
  • Paste the value into metadataSchema field

Once you have a successfully deployed your metadata API

  • Copy the base metadata URI
  • Open registrationConfigs.ts
  • Paste the value into metadataUri field

🚧

Metadata Refreshing

When you mint, we will try to collect the metadata of your NFT in your api/backend-service based on the address you provided while registering your collection.

If we get a 200 response from your backend, we will not try again, even if the data is invalid or empty.

If we get any code different than 200 we will keep trying for 48 hours until we get a 200 response code. After 48h we will stop retrying.

The only way to refresh the data, if you submitted an invalid request after 48h or a valid 200 with empty or invalid metadata is contacting us.

Your metadata will be refreshed automatically by us re-calling your metadata api unless your asset is more than 48 hours old, or we’ve ever received a 200 http status code from your metadata api. In these cases, you need to submit the form for us to refresh your metadata.

3. Media asset hosting

Partners are expected to self-host their media assets. This means both the endpoints for the metadata of the assets, as well as other media defined by the metadata. Many developers consider an IPFS for hosting to maintain the decentralization of the data.

4. Collection registration

Once you have deployed your contract, you will need to register your contract with Immutable X. This is done by registering a collection, which contains your contract details.

For new users who are registering a collection in RopstenRopsten - Ethereum's testnet, used for testing, experimenting and debugging., please follow these guides.

For registering a collection in MainnetMainnet - Ethereum's mainnet, used for projects which are ready for production., please follow these guides.

Once your registration is completed you will receive a confirmation email. (Mainnnet only)

Check your collection here:
https://api.ropsten.x.immutable.com/v1/assets?collection={your_contract_address}

Check your minting has been completed here
https://ropsten.etherscan.io/address/{your_contract_address}

5. Minting assets

❗️

Warning: Minting with Royalties

NFTs minted using mint v1 will not have royalties. We will only manually add royalties to Ethereum contracts that have implemented mint v1 that are registered before Thursday 25th of November, 11.59pm AEST.

Use mint v2 to ensure that royalties are added to your NFTs.

Once the collection is registered, the minting process can begin. Before minting new assets, keep in mind the following:

  • You cannot control the IDs for the assets minted on Immutable X, only the metadata. When you create a new asset, you need to immediately store and relate the ID to specific metadata on your backend. This is necessary for the retrieval of correct metadata by Immutable X.

  • If your contract contains on-chain properties, ensure they are passed into the mint function's blueprint metadata, as those are the only extra values the mintFor receives during withdrawal.

  • Consider what data will need to be on-chain if the asset is withdrawn. If desiring to include a property on-chain, often times a base URI will be used so the rest of the data can still be retrieved without having it all on-chain.

See https://docs.x.immutable.com/docs/minting-assets-1

🚧

Minting throughput

It is preferable to have more mints per minting request instead of more minting requests with less mints per request. There is no restriction of the level of concurrent minting requests. However, please handle http status code 429 (too many requests) with a sensible retry mechanism.


Did this page help you?