Concordium NFT Minting Tutorial
In this tutorial, we are going to mint an NFT on the Concordium blockchain. First I’ll cover the basics and help to set up your development environment. Potentially, there will be some updates in the future based on the improvements such as the expected release of the web extension wallet.
All development is completed using Node/Web-SDK and node-cli and includes the following examples: deploy the minting contract, mint an NFT, get details from Concordium, check token owner, transfer that NFT, check the new owner, and attempt to send the token with the previous owner! (failed of course)
I will use my beloved cat-Trip’s photograph and store it on the IPFS. In order to do that, will install and run a node on my computer. There are other options like pinning services, but they are not in the scope of this tutorial. We can use one of them in another one. And I will create a metadata file that will be stored on IPFS too. By doing this, I eliminate the risk of deletion of my cat’s photo on a centralized photo album and definitely protect him from other cats’ jealous actions like changing his jumping skills rate! We will store this metadata’s URL on-chain and be able to read it.
Development Environment
First, you need to install “rustup” which will install Rust and Cargo to your computer to do that, you can use this command.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Then you should select “1” to continue the installation.
Finally, when Rust and Cargo are successfully installed in your system you should see something similar to below. Copy and paste the commands on a terminal. Then you need to install Wasm which will be used for building contracts.
source $HOME/.cargo/env
rustup target add wasm32-unknown-unknown
While during wasm installation in your system you should see something similar to below.
Now we need to install the Concordium software package. Navigate the cargo-concordium from this link. Download the correct version for your operating system. You need to install the tools both for the testnet and the mainnet.
First, we need to rename the “cargo-congordium-v.x.x” file to “cargo-concordium”. Then go to the directory where the file is downloaded and run this command to make it executable. You also need to move the cargo-concordium executable to the cargo folder. All steps below are configured for MacOS, if you are using another operating system I suggest you follow this link.
sudo chmod +x cargo-concordium
mv cargo-concordium ~/.cargo/bin
cargo concordium --help
If everything is alright, the last command should print something similar below. If either your cargo-concordium version, concordium client version, or node version is older than the latest one released I suggest you update it. You can check them from this link.
If you have a warning (on a mac device) like “cargo-condordium cannot be opened because the developer cannot be verified” that means it requires permission to run and you should go to System Preferences → Security and unlock it via your password and click “Allow Anyway”.
Concordium NFT Minting Tutorial
You can start by cloning this repository which includes some essential binaries, docker files, and configurations that are provided by Concordium.
There are multiple collections of sample images provided by this link and I used one of them.
You can find the detailed information about the nft-artifact’s folder in the repository but I will explain them all briefly.
In order to start the tutorial, first, you need to install Docker Compose which we are gonna use it for running a Concordium node because you need to run a node to deploy your contracts.
Run the following command on your terminal to clone this repository. It will clone the repository to your local computer.
git clone --recurse-submodules https://github.com/chainorders/concordium-nft-tutorials.git
Concordium Web Wallet Chrome Extension
If you are using the Concordium Wallet browser extension you can follow this link to extract your key and skip the Concordium Mobile Wallet and Wallet Decryption sections! You can find detailed information about it in this link from the website.
Concordium Mobile Wallet
Now you need a Concordium wallet, please use the link to install a Concordium testnet wallet to your mobile. Follow the instructions described on this link to install and run the software. First, if you are using an IOS device you are going to need the TestFlight application and then Concordium Testnet Wallet.
When you have the Concordium Testnet Mobile Wallet on your phone installed, you need to create an account. If you follow this link, you can create your account and claim your 2000 CCD faucet from the app.
After that step, you need to transfer your account’s backup file to your local computer from your phone. You can do it in a couple of steps from your mobile with your password. And also check the steps from this link. When your export process is done, send it to your local machine. You can set an email address while exporting, it should send the backup file via e-mail. Or you can transfer it manually.
Once you receive the wallet backup file make sure that it’s copied to the same folder as the rest of the repository.
IPFS — Interplanetary File System
There are multiple ways of storing data in IPFS you can do it from a user interface by running a node or you can use a pinning service. For this tutorial, I prefer to download and run the IPFS and store the both NFT metadata and image itself. You can download and install IPFS from this link. I strongly suggest to check this if you are having IPFS gateway issues or want a perfect IPFS experience.
Uploading Data to IPFS
After that, you need to import your NFT. I like cats and that is why I choose a picture of my cat. Using the IPFS app you need to click the “Import” button and select the image to upload.
Now you should be able to see its CID or URL from the user interface. This is the important part because when you buy an NFT, it will be a hyperlink that refers to the exact location of your digital asset. So you basically are buying a link that points to a location, you want to make sure that no one can change it, right? The CID value guarantees that by creating unique hash values. When you import something to IPFS it generates a Content Identifier (CID) hash value to represent the asset with strings. A hash value is an output of a one-way cryptic function that takes literally anything as an input like a word, text, image, movie, book, etc. but produces only a string of fixed size to refer to that asset. You can read more about hash from this link.
NFT Metadata
Anyway let’s get back to the topic, the CID of your asset is unique in the IPFS storage, and when you put that value in your metadata file, the buyer will always be able to check it using the URL and compare it to what you have and what IPFS shows.
Concordium CIS-2 standard allows the creation of your NFT metadata in the following format, and you can find more details in this Concordium CIS-2 token standard document link. For the sake of the minting process, you have to follow the same formatted .json file, but as you can see you are also allowed to add additional attributes to the metadata file, or remove them.
Now, until this point, we installed the required libraries, prepared a setup for our development environment, installed Docker and IPFS, and import our first image to IPFS. Well done!
Build Smart Contract
Now, in order to deploy our contract we need to change directory (with “cd” command) into the cis2-nft folder. Then run the commands below to build and deploy your contract. I’d like to highlight something important here. In this tutorial we are going to use the sample smart contract shared by Concordium in this repository. Upcoming tutorials will include some modifications, additions, and newly developed smart contracts but for now, these are not on the table.
cd cis2-nft
mkdir -p ../dist/smart-contract
cargo concordium build --out ../dist/smart-contract/module.wasm --schema-out ../dist/smart-contract/schema.bin
After these steps, you should be able to see something like the above. We are almost ready to mint our first NFT on Concordium to do that we need to run a local node, which in this tutorial where we use a Mac is a Docker image in the repository. To start it run the command below. Docker file configurations can be found in the docker-compose.yml file as described below. Dont forget the set a name for your node with the parameter CONCORDIUM_COLLECTOR_NODE_NAME. The docker-compose configuration is inspired by the docker docs from the Concordium website. If your node running on a Mac M1 then uncomment platform: linux/amd64 this lines.
version: '3'
services:
node:
# platform: linux/x86_64
# platform: linux/amd64
container_name: testnet-node
image: concordium/testnet-node:latest
pull_policy: always
ports:
- ${CONCORDIUM_NODE_PORT}:${CONCORDIUM_NODE_PORT}
working_dir: /
environment:
## Refer https://github.com/Concordium/concordium-node/blob/main/VARIABLES.md#grpc
CONCORDIUM_NODE_RPC_SERVER_ADDR: 0.0.0.0
CONCORDIUM_NODE_RPC_SERVER_PORT: ${CONCORDIUM_NODE_PORT}
CONCORDIUM_NODE_LISTEN_ADDRESS: 0.0.0.0
CONCORDIUM_NODE_CONSENSUS_GENESIS_DATA_FILE: /testnet-genesis.dat
CONCORDIUM_NODE_CONFIG_DIR: "/node/concordium-node-data"
CONCORDIUM_NODE_DATA_DIR: "/node/concordium-node-config"
CONCORDIUM_NODE_CONNECTION_BOOTSTRAP_NODES: bootstrap.testnet.concordium.com:8888
CONCORDIUM_NODE_CONNECTION_HARD_CONNECTION_LIMIT: 20
CONCORDIUM_NODE_CONNECTION_THREAD_POOL_SIZE: 2
CONCORDIUM_NODE_CONNECTION_BOOTSTRAPPING_INTERVAL: 1800
volumes:
- ./dist/node/data:/node/concordium-node-data
- ./dist/node/config:/node/concordium-node-config
entrypoint:
- ./concordium-node
testnet-node-collector:
# platform: linux/amd64
container_name: testnet-node-collector
image: concordium/testnet-node:latest
pull_policy: always
environment:
# Settings that should be customized by the user.
CONCORDIUM_NODE_COLLECTOR_NODE_NAME: <YOUR NODE NAME>
# Environment specific settings.
CONCORDIUM_NODE_COLLECTOR_URL: https://dashboard.testnet.concordium.com/nodes/post
# Collection settings.
# How often to collect the statistics from the node.
CONCORDIUM_NODE_COLLECTOR_COLLECT_INTERVAL: 5000
# The URL where the node can be reached. Note that this will use the
# docker created network which maps `testnet-node` to the internal IP of
# the `testnet-node`. If the name of the node service is changed from
# `testnet-node` then the name here must also be changed.
CONCORDIUM_NODE_COLLECTOR_GRPC_HOST: http://node:10001
entrypoint: [ "/node-collector" ]
This will run a docker image of a node and this step currently takes some time potentially hours based on your device configuration, because your node is freshly started and needs to recover all the previous blocks. In the near future, hopefully, there will be a tool that will eliminate this step and allows you to connect via a URL to a node. Like the Infura. Once the height value is the same as the height in ccdscan, then you can continue with the development.
Remember we are working on the testnet. Let’s check if our node collector is up and running. Look for the name of your node that is specified in the docker-compose.yml file in the network section of the dashboard.
Install Required Packages
We are going to invoke some functions from our deployed contract using ts-client and will cover minting and transferring NFTs. You can install all the dependent packages with either “yarn” or “npm”, if you dont have node in your system you should install it first.
cd node-cli
yarn install
yarn add -g ts-node
Wallet Decryption
Navigate the cli.ts file inside of node-cli folder and add the following lines to it. Remember, the cloned repository already has it, if you have it already you should not add it again.
const cli = new commander.Command();
cli
.parseAsync(process.argv)
.catch((e) => console.error(e))
.then((res) => console.log("cli exited"));
cli.showHelpAfterError().showSuggestionAfterError().allowUnknownOption(false);
Now, you need to decrypt your wallet backup file in order to make some function calls to do that use the following command. It should create a concordium-backup.concordiumwallet.json file. Open up that file and navigate the signKey and address. We are gonna need them while making function calls.
ts-node ./src/cli.ts decrypt --wallet ../concordium-backup.concordiumwallet --password YOUR_PASSWORD --out ../concordium-backup.concordiumwallet.json
Deploy Smart Contract
In order to deploy the contract please add the following lines to your cli.ts file. You need to specify the compiled module file and the other arguments will be passed from the terminal.
Run the command below on your terminal. Paste the signKey value and the address from the decrypted concordium-backup.concordiumwallet.json file.
ts-node ./src/cli.ts deploy \
--wasm ../dist/smart-contract/module.wasm \
--sender <ACCOUNT-ADDRESS> \
--sign-key <SIGN-KEY>
If you have the output below, congrats! You’ve successfully deployed your first smart contract on Concordium! You can also verify it either by looking at the ccdscan or the testnet dashboard lookup section.
As you can see here my NFT minting contract is deployed and it allows you to verify the time, sender account and the block itself. It costs me ~148 CCD which is less than 1.9 euros currently and to be honest it’s not bad for a 39.8 KB contract. One note here, of course, you can check the remaining balance in your mobile wallet too.
Now what we need to do is go to the dashboard and get the hash value from there, using the url in the terminal. Click on the “Deployed module with reference” and copy the hash value. We are going to use it while initializing the contract in the next section.
Initialize Smart Contract
Now we need to initialize the deployed contract, it’s a lot easier than the previous steps. After deploying a contract we have to initialize it, it’s like object-oriented programming. We create a class which is a module, and then we initialize it to create an object, right? Which is the same here. An object of a class is a way to store both states of the class and its functionality. This time we are going to use the hash value we got in the previous step. First, make sure initialize function is implemented in your cli.ts file.
Run the code below, here we use hash value in the <Module Hash> part, signKey from your decrypted wallet file, contract name as <Your Contract Name> (in this case cis2-nft), and the address of your account. That will create another transaction on the chain obviously.
ts-node ./src/cli.ts init --module <Module Hash> --sender <ACCOUNT-ADDRESS> --sign-key <SIGN-KEY> --contract cis2-nft
If you have this output that means you are successfully initialized your contract. Now let’s go to that URL to get our contracts index value. From the dashboard, you can easily see the index, my account address as sender, event details, and transaction hash.
Mint Function
Now, we are ready to call our mint function. First, you should place the setupCliUpdateContract
function below in your cli.ts file. In this step, we serialize the parameters taken from the terminal that is going to be input for our update function.
Run the command below with the index value you get in the previous step, your account address, and the signKey from your decrypted wallet.json file. Also, you need to set the mint parameters in mint-params.json file described below. The account address is my wallet address, we are going to generate 1 copy of it since it’s a non-fungible token (a semi-fungible token tutorial will be published soon.), URL is the IPFS link of the metadata file and hash is the SHA-256 output of the link.
ts-node ./src/cli.ts mint --params ../nft-artifacts/mint-params.json --schema ../dist/smart-contract/schema.bin --index <YOUR INDEX> --sender <ACCOUNT-ADDRESS> --sign-key <SIGN-KEY>
Let’s check the dashboard one more time.
We have just minted our first NFT on Concordium successfully! Let’s get the metadata on-chain and see what we have in there. In order to do that, we will use setupCliInvokeContract
and we are gonna use view functions since with this function we are not going to change the state of the blockchain, there will be no transaction fee. This is almost the same with the setupCliUpdateContract
except there are no internal state changes in the smart contract. View functions will read the current state of the contract.
We expect the IPFS url that we add in metadata-json file and a hash value as we add it in the mint function. I just wanted to show you how can you store on-chain another value in addition to the url. In order to get the details on-chain run the following command. If you are using cargo-concordium 2.1.0 you dont need to specify the schema but for newer versions add <YOUR SCHEMA NAME>. You can check the version of your cargo-concordium with the commands cargo-concordium — help or cargo-concordium — version
ts-node src/cli.ts view --index <YOUR INDEX> --sender <YOUR ADDRESS> --schema ../cis2-nft
Huh, is it done, really? Let’s visit the URL we stored on-chain on a browser.
Yes, apparently we stored our metadata on IPFS successfully. If you are implementing a project I suggest you to run your own IPFS node and pin the data for guaranteeing that at least one participant has it. If you’d like to see Trip, you can check the link.
Transfer Function
Now let’s transfer it with the following command. We will check the balance of our account and the other wallet in the following steps. Sometimes you just don’t want to hide this beauty for yourself, right?
Before that, you should go and change the sender account and receiver account in this file ../nft-artifacts/transfer-params.json. One little note here, you dont have to do it like this, I just decided to read all these values from a JSON file because it’s easier to understand&follow otherwise it can get quickly messy. So make sure you made the adjustments of addresses accordingly like the one I shared below. I created another account on my mobile wallet (and this time I made some changes to my data like revealing my nationality and country of residence. Which is one of the strongest parts of Concordium, explore it!) and will transfer this token to that.
One reminder, you should be the owner of it to be able to transfer it, so try not to get confused in this step. The original minter account should be in the “from” key’s value and the receiver will be located in the “to” key’s value.
ts-node ./src/cli.ts transfer --params ../nft-artifacts/transfer-params.json --schema ../dist/smart-contract/schema.bin --index <YOUR INDEX> --sender <ACCOUNT-ADDRESS> --sign-key <SIGN-KEY> --contract CIS2-NFT
Congratulations! The transfer is successfully completed. Now let’s check the state of the token once more with the view function.
ts-node src/cli.ts view --index <YOUR INDEX> --sender <YOUR ADDRESS> --schema ../cis2-nft
Voila! As you can see now my second account is the owner of the asset and I can definitely see the previous owners. As a final step, let’s try If I’d like to transfer it with my first account again. This should not be possible!
As expected, it can not be, right? Because the owner is changed. My second account is the owner and we need to use its signKey and address in order to transfer it. This was the last part of this tutorial, stay tuned to implement amazing things on Concordium. To be continued.