Mint an NFT Collection
Course Materials:
- Install Bun (https://bun.sh/docs/installation)
- Fund Your Preprod Wallet (https://docs.cardano.org/cardano-testnets/tools/faucet)
- Create a Blockfrost Preprod Project (https://blockfrost.io/dashboard)
- CIP-20 Definition (https://cips.cardano.org/cip/CIP-20)
We are going to learn how to mint native assets on top of the cardano blockchain. You will:
- Gain knowledge about tokens on cardano.
- Learn how to build simple minting transactions.
- Learn the basics of community accepted standards for non-fungible tokens.
Project Setup
At course material #1 install the bun toolchain.
Initialize a new directory and run bun init
& bun install @meshsdk/core
We need to generate and fund a Cardano wallet. Create a new file in /scripts/brew.ts
and use the MeshSDK to generate a new wallet and log information about it.
import { MeshWallet } from "@meshsdk/core";
const words = MeshWallet.brew() as string[];
const mnemonicString = words.join(" ");
console.log("mnemonic:", mnemonicString);
const wallet = new MeshWallet({
key: { type: "mnemonic", words },
networkId: 0,
});
console.log("Public Change Address:", await wallet.getChangeAddress());
Run the script with bun run scripts/brew.ts
.
Copy your public address and fund the wallet using course material #2. Obtain a blockfrost preprod project using course material #3.
Copy your mnemonic and blockfrost preprod project api key into a .env
file at the root of your project.
Mint Your Collection
In the index.ts
file generated by initializing your project, use core Mesh building blocks to interact with the blockchain.
import {
MeshTxBuilder,
MeshWallet,
BlockfrostProvider
} from "@meshsdk/core"
const provider = new BlockfrostProvider(process.env.BLOCKFROST_KEY!);
const words = process.env.MNEMONIC!.split(" ");
const wallet = new MeshWallet({
key: { type: "mnemonic", words },
networkId: 0,
fetcher: provider,
submitter: provider,
});
const txBuilder = new MeshTxBuilder({ fetcher: provider });
const address = await wallet.getChangeAddress();
const utxos = await wallet.getUtxos();
const { pubKeyHash } = deserializeAddress(address);
Create a ruleset for minting your NFT using a native script. This will be used to derive the policy id.
const nativeScript: NativeScript = {
type: "all",
scripts: [
{
type: "before",
slot: "90000000", // Any value beyond the current preprod absolute slot. When you read this, it's possible this slot has passed.
},
{ type: "sig", keyHash: pubKeyHash },
],
};
const forgeScript = ForgeScript.fromNativeScript(nativeScript);
const policyId = resolveScriptHash(forgeScript);
Let's write a function to generate the onchain metadata used to give our tokens an image and attributes.
type AssetMetadata = {
files: {
mediaType: string;
name: string;
src: string;
}[];
image: string;
mediaType: string;
name: string;
};
function get721Metadata(
name: string,
attributes?: Record<string, unknown>
): AssetMetadata {
return {
...attributes,
files: [
{
mediaType: "image/png",
name,
src: "ipfs://QmPS4PBvpGc2z6Dd6JdYqfHrKnURjtRGPTJWdhnAXNA8bQ",
},
],
image: "ipfs://QmPS4PBvpGc2z6Dd6JdYqfHrKnURjtRGPTJWdhnAXNA8bQ",
mediaType: "image/png",
name,
};
}
Now we can write a loop to create 9 unique tokens using our forge script. Commit, sign, and submit the transaction.
const metadata: {
[policyId: string]: {
[assetName: string]: AssetMetadata;
};
} = { [policyId]: {} };
for (let i = 1; i < 10; i++) {
const tokenName = "Asset #" + i;
const tokenHex = stringToHex(tokenName);
txBuilder.mint("1", policyId, tokenHex).mintingScript(forgeScript);
metadata[policyId]![tokenName] = get721Metadata(tokenName, { Attribute: i });
}
const unsignedTx = await txBuilder
.metadataValue(721, metadata)
.changeAddress(address)
.invalidHereafter(90000000)
.selectUtxosFrom(utxos)
.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log("Submitted TX Hash:", txHash);
Read the returned transaction hash, and use a chain explorer to view the preprod transaction.