How to create an NFT on the Cardano blockchain using JavaScript
This is a deprecated tutorial. Now, it will be archived just for people to use the old tutorial for their own education
Youtube Video: https://www.youtube.com/watch?v=OeOliguGn7Y
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs
cardano-cli
cardano-cli version
output should be similar:
cardano-cli 1.34.1 - linux-aarch64 - ghc-8.10
git rev 0000000000000000000000000000000000000000
cardano-node
cardano-node version
output should be similar:
cardano-node 1.34.1 - linux-aarch64 - ghc-8.10
git rev 0000000000000000000000000000000000000000
node.js
node -v
v14.19.0
verify env
create project and initial setup
#make sure our db is in our $PATH
CARDANO_NODE_SOCKET_PATH="$NODE_HOME/db/socket"
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 IOHK hydra website
Download Genesis config file needed for shelly-era
nano fetch-config.sh
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
chmod +x fetch-config.sh
./fetch-config.sh
mkdir src; cd src
nano cardano.js
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
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 cardano-minter
node src/create-wallet.js
# open text editor
cd cardano-minter/src; nano get-balance.js
// create get-balance.js
const cardano = require("./cardano");
const sender = cardano.wallet("ADAPI");
console.log(sender.balance());
cd ..
node src/get-balance.js
Download IPFS
Upload your files to IPFS
Generate policy id
Define your meta data
create mint transaction
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)
cd ..
node src/mint-asset.js
node src/get-balance.js
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);
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 ipfs to prove that it work