Cardano Native Asset (NFT) 💰
Who is this guide for?
- For people who want to make NFT's or Native Assets on Cardano
- For people who know about Cardano
Benefits of NFT's on Cardano
- Low transaction fees
- Native on the blockchain
Prerequisites
We made this tutorial for use with Raspberry-Pi-ARM machines running on Linux OS so make sure to download the correct node.js for your local machine/CPU and OS. Currently, the Cardano-node and Cardano-cli are meant to be built from source on Linux machines. Any other OS will have its own build complexities, and we do not cover them in any of our tutorials as of right now. How to build Cardano Node from source
If you are using a Raspberry Pi machine here is an easy-to-follow tutorial we made to get a Cardano Relay Node running.
- cardano-node / cardano-cli set up on local machine
- Make sure you have a Cardano node running and fully synced to the database
- Make sure node.js installed
#Copy/Paste this into your terminal if node.js is not installed
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs
Verify everything is set up properly on our machine ⚙️
#Copy/paste into terminal window
cardano-cli version; cardano-node version
Your output should look like this 👇
cardano-cli 1.34.1 - linux-aarch64 - ghc-8.10
git rev 0000000000000000000000000000000000000000
cardano-node 1.34.1 - linux-aarch64 - ghc-8.10
git rev 0000000000000000000000000000000000000000
Verify our node.js version is correct and is on v14.16.1
#Copy/paste into terminal window
node -v
v14.19.0
Video Walk-through:
Create our project directory and initial setup
Make sure our $NODE_HOME
environment variable exists
# check for $NODE_HOME
echo $NODE_HOME
If the above command didn't return anything, you need to set the$NODE_HOME
bash environment variable or use a static path for the Cardano node's socket location in 'db' C in your Cardano node directory.
export NODE_HOME="/home/ada/pi-pool"
# Change this to where cardano-node creates socket
export CARDANO_NODE_SOCKET_PATH="$NODE_HOME/db/socket"
Now let's make our projects directory then create our package.json file and install the cardanocli-js package.
mkdir cardano-minter
cd cardano-minter
npm init -y #creates package.json)
npm install cardanocli-js --save
- Copy the Cardano node genesis latest build number from the IOHK hydra website
- Create a bash shell script to Download the latest Genesis config file needed
nano fetch-config.sh
- TESTNET
- MAINNET
echo export NODE_BUILD_NUM=$(curl https://hydra.iohk.io/job/Cardano/iohk-nix/cardano-deployment/latest-finished/download/1/index.html | grep -e "build" | sed 's/.*build\/\([0-9]*\)\/download.*/\1/g') >> $HOME/.bashrc
wget -N https://hydra.iohk.io/build/${NODE_BUILD_NUM}/download/1/testnet-shelley-genesis.json
echo export NODE_BUILD_NUM=$(curl https://hydra.iohk.io/job/Cardano/iohk-nix/cardano-deployment/latest-finished/download/1/index.html | grep -e "build" | sed 's/.*build\/\([0-9]*\)\/download.*/\1/g') >> $HOME/.bashrc
wget -N https://hydra.iohk.io/build/${NODE_BUILD_NUM}/download/1/mainnet-shelley-genesis.json
Now we need to give permissions to our new script to execute then we will run our script and download the genesis files.
sudo chmod +x fetch-config.sh
./fetch-config.sh
Next, we make our src folder/directory and then create the Cardano client.
mkdir src
cd src
nano cardano.js
If you are using testnet make sure you have the correct testnet-magic version number. You can find the current testnet version here or simply look in your testnet-shelley-genesis.json file in your cardano node directory.
- TESTNET
- MAINNET
const CardanocliJs = require("cardanocli-js");
const os = require("os");
const path = require("path");
const dir = path.join(os.homedir(), "cardano-minter");
const shelleyPath = path.join(
os.homedir(),
"pi-pool/files",
"testnet-shelley-genesis.json"
);
const cardanocliJs = new CardanocliJs({
// era: "mary",
network: 'testnet-magic 1097911063',
dir,
shelleyGenesisPath: shelleyPath,
});
module.exports = cardanocliJs
const CardanocliJs = require("cardanocli-js");
const os = require("os");
const path = require("path");
const dir = path.join(os.homedir(), "cardano-minter");
const shelleyPath = path.join(
os.homedir(),
"pi-pool/files",
"mainnet-shelley-genesis.json"
);
const cardanocliJs = new CardanocliJs({
// era: "mary",
network: 'mainnet',
dir,
shelleyGenesisPath: shelleyPath,
});
module.exports = cardanocliJs
Video Walk-through
- 1. Create Project
- 2. Get Cardano genisis files
- 3. Setup Cardano client
Create a local wallet
nano create-wallet.js
const cardano = require("./cardano");
const createWallet = (account) => {
cardano.addressKeyGen(account);
cardano.stakeAddressKeyGen(account);
cardano.stakeAddressBuild(account);
cardano.addressBuild(account);
return cardano.wallet(account);
};
createWallet("ADAPI");
cd ..
node src/create-wallet.js
Verify balance wallet balance is Zero, then we fund the wallet
- First, we need to create a get-balance.js script
cd src
nano get-balance.js
// create get-balance.js
const cardano = require("./cardano");
const sender = cardano.wallet("ADAPI");
console.log(sender.balance());
- Now, Check the balance of our wallet.
cd ..
node src/get-balance.js
- We can go ahead and send some funds (ADA) into our wallet we created, wait a few minutes, and then check the balance again to make sure the transaction was successful.
If you are using testnet you must get your tADA from the testnet faucet here.
Video Walk-through
- Create local wallet
- Send ADA to wallet
Mint our Native-Asset/NFT on Cardano
Before we proceed to mint our Native Asset we must have a few things taken care of. We need to first get our "asset" onto our IPFS node and generate the IPFS link. If you do not know about IPFS or what it actually does we recommend having a read through the documentation here or watching this video.
Since we are using an image file to be our asset we should upload a smaller thumbnail-sized version of our image (ideally less than 1MB). This will be used on sites like pool.pm to display our assets nicely in our wallets. We then upload the full-size image as our source image.
- Download IPFS
- Upload your asset's files to IPFS
- Get our image thumbnail IPFS link
- Get the src IPFS link
For reference:
- image (thumbnail version) - ipfs://QmQqzMTavQgT4f4T5v6PWBp7XNKtoPmC9jvn12WPT3gkSE
- src (full-size version) - ipfs://Qmaou5UzxPmPKVVTM9GzXPrDufP55EDZCtQmpy3T64ab9N
Create our mint-asset.js script
This script has three main components:
- Generate policy id
- Define your metadata
- Create mint transaction
nano mint-asset.js
const cardano = require("./cardano")
// 1. Get the wallet
const wallet = cardano.wallet("ADAPI")
// 2. Define mint script
const mintScript = {
keyHash: cardano.addressKeyHash(wallet.name),
type: "sig"
}
// 3. Create POLICY_ID
const POLICY_ID = cardano.transactionPolicyid(mintScript)
// 4. Define ASSET_NAME
const ASSET_NAME = "TimeWarpBerry"
// Convert Asset ASCII name to HEX
const ASSET_NAME_HEX = ASSET_NAME.split("").map(c => c.charCodeAt(0).toString(16).padStart(2, "0")).join("");
// 5. Create ASSET_ID
const ASSET_ID = POLICY_ID + "." + ASSET_NAME_HEX
// 6. Define metadata
const metadata = {
721: {
[POLICY_ID]: {
[ASSET_NAME]: {
name: ASSET_NAME,
image: "ipfs://QmUxRuzTi3UZS33rfqXzbD4Heut7zwtGUhuD7qSv7Qt584",
description: "Time Warp Berry NFT",
type: "image/png",
src: "ipfs://QmUxRuzTi3UZS33rfqXzbD4Heut7zwtGUhuD7qSv7Qt584",
// other properties of your choice
authors: ["PIADA", "SBLYR"]
}
}
}
}
// 7. Define transaction
const tx = {
txIn: wallet.balance().utxo,
txOut: [
{
address: wallet.paymentAddr,
value: { ...wallet.balance().value, [ASSET_ID]: 1 }
}
],
mint: [
{ action: "mint", quantity: 1, asset: ASSET_ID, script: mintScript },
],
metadata,
witnessCount: 2
}
if(Object.keys(tx.txOut[0].value).includes("undefined")|| Object.keys(tx.txIn[0].value.includes("undefinded"))){
delete tx.txOut[0].value.undefined
delete tx.txIn[0].value.undefined
}
// 8. Build transaction
const buildTransaction = (tx) => {
const raw = cardano.transactionBuildRaw(tx)
const fee = cardano.transactionCalculateMinFee({
...tx,
txBody: raw
})
tx.txOut[0].value.lovelace -= fee
return cardano.transactionBuildRaw({ ...tx, fee })
}
console.log(tx)
const raw = buildTransaction(tx)
// 9. Sign transaction
const signTransaction = (wallet, tx) => {
return cardano.transactionSign({
signingKeys: [wallet.payment.skey, wallet.payment.skey ],
txBody: tx
})
}
const signed = signTransaction(wallet, raw)
// 10. Submit transaction
const txHash = cardano.transactionSubmit(signed)
console.log(txHash)
- Run the minting script, then wait a few moments to check the balance in our wallet
cd ..
node src/mint-asset.js
Video Walk-through:
Sending your NFT back to Daedulus or Yoroi wallet
Now we must create a new script to send our newly minted NFT to a wallet.
cd cardano-minter/src
nano send-back-asset-to-wallet.js
There are few main parts we have to this script in order to send the asset:
- Get the wallet
- Define the transaction
- Build the transaction
- Calculate the fee
- Pay the fee by subtracting it from the sender's utxo
- Build the final transaction
- Sign the transaction
- Submit the transaction
const cardano = require("./cardano");
// 1. get the wallet
// 2. define the transaction
// 3. build the transaction
// 4. calculate the fee
// 5. pay the fee by subtracting it from the sender utxo
// 6. build the final transaction
// 7. sign the transaction
// 8. submit the transaction
const sender = cardano.wallet("ADAPI");
console.log(
"Balance of Sender wallet: " +
cardano.toAda(sender.balance().amount.lovelace) +
" ADA"
);
const receiver =
"addr_test1qqqydvg5wzd6twvernsjcdjd9akmygyqp7gky7zpm0hrmq3ccwlnumzzuum6k6ja2k47g5dv2p4kwt753mpjjzx8fsmsq2aj0p";
const txInfo = {
txIn: cardano.queryUtxo(sender.paymentAddr),
txOut: [
{
address: sender.paymentAddr,
amount: {
lovelace: sender.balance().amount.lovelace - cardano.toLovelace(1.5),
},
},
{
address: receiver,
amount: {
lovelace: cardano.toLovelace(1.5),
"9e57c3a4aa769063ab4963e3e2fc18aeafb6808b3adbc3f1670a9c00.54696d65576172704265727279": 1,
},
},
],
};
const raw = cardano.transactionBuildRaw(txInfo);
const fee = cardano.transactionCalculateMinFee({
...txInfo,
txBody: raw,
witnessCount: 1,
});
//pay the fee by subtracting it from the sender utxo
txInfo.txOut[0].amount.lovelace -= fee;
//create final transaction
const tx = cardano.transactionBuildRaw({ ...txInfo, fee });
//sign the transaction
const txSigned = cardano.transactionSign({
txBody: tx,
signingKeys: [sender.payment.skey],
});
//subm transaction
const txHash = cardano.transactionSubmit(txSigned);
console.log("TxHash: " + txHash);
cd ..
node src/send-back-asset-to-wallet.js
Final Steps to view your NFT
- View your nft in your wallet
- View your asset on cardanoassets.com
- View your asset on pool.pm (see the actual picture)
- Show the original minting metadata
- Open the src and image ipfs links in your browser to prove that it worked
Video Walk-through
If you liked this tutorial and want to see more like it please consider staking your ADA with any of our Alliance's Stake Pools, or giving a one-time donation to our Alliance https://cointr.ee/armada-alliance.