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)
Learn how to mint native assets on the Cardano blockchain. You will:
- Understand tokens on Cardano.
- Build simple minting transactions.
- Understand community standards for NFTs.
Project Setup
Install Bun (see course material #1).
Initialize a new directory and run:
bun init
bun install @meshsdk/coreGenerate and fund a Cardano wallet. Create /scripts/brew.ts and use MeshSDK to generate a wallet and log information.
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:
bun run scripts/brew.tsCopy your public address and fund the wallet (course material #2). Obtain a Blockfrost Preprod project (course material #3).
Copy your mnemonic and Blockfrost key into a .env file.
Mint Your Collection
In index.ts, use 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 minting ruleset using a native script 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);Write a function to generate on-chain metadata for 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,
};
}Loop to create 9 unique tokens using the 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 transaction hash and view the transaction on a chain explorer.