Deno & Fetch
A Deno-based script for minting CIP-25 assets on the Cardano blockchain using the Anvil API. This script generates wallets, creates policies, customizes asset metadata, builds and signs transactions,
This a a very specific example, the goal is to show how flexible and versatile the Anvil API is.
This example will be entirely done into one file named index.ts
Objectives
Mint a CIP-25 Asset
Create a Policy (an NFT Collection)
Create a custom function to customize the asset metadata
Create 2 wallets, one for the policy and one to act as the customer
Submit transaction to the network
This is only an example showing the anvil api versatility
The example is built on Cardano Preprod
Code
Dependencies
import { Buffer } from "node:buffer";
import {
Credential,
type Ed25519KeyHash,
FixedTransaction,
NativeScript,
NativeScripts,
PrivateKey,
ScriptAll,
ScriptPubkey,
TimelockExpiry,
} from "npm:@emurgo/[email protected]";
Import Helper Functions
To reduce the amount of content in this guide, you only have to import all functions defined here:
https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/utilities-functions/README.mdCardano Wallets
We have this utility to generate wallets: https://github.com/Cardano-Forge/cardano-wallet-cli/releases
~/Downloads/cardano-wallet-macos-latest --name policy --mnemonic
~/Downloads/cardano-wallet-macos-latest --name customer --mnemonic
Load wallets
// NOTE: Be sure to send ADA in this address, it will be used to pay the tx fee.
const customerWallet = JSON.parse(Deno.readTextFileSync("customer.json"));
// Wallet to create the policy with, no ADA is required for this one.
const policyWallet = JSON.parse(Deno.readTextFileSync("policy.json"));
Setup policy
Don't forget to increase the counter, otherwise you will double mint.
// Variables
const expirationDate = "2026-01-01";
const counter = 202502061237;
// Expiration date, you can interact with the policy until this date is reached.
// After that the policy is locked.
const slot = dateToSlot(new Date(expireDate));
const keyhash = get_keyhash(policyWallet.skey);
if (!keyhash) {
throw new Error("Unable to get key hash for policy, missing or invalid skey");
}
const policyAnvilApi = create_policy_script(keyhash, slot);
Collection configurations
This example requires 2 rules.
const policyAnvilApiScript = {
type: "all",
scripts: [
{
type: "sig",
keyHash: keyhash.to_hex(),
},
{
type: "before",
slot: slot,
},
],
};
Meaning that the policy has to be signed by the wallet defined in the policy wallet path parameter AND all mutations must be done before the date defined.
Create Metadata
This function is where you have to define your own data and configuration per asset.
CIP-25 enforces few fields as mandatory see here: https://cips.cardano.org/cip/CIP-25
You can also use our powerful metadata validator: https://metadraft.io
For example
const assets: {
version: string;
assetName: string;
metadata: {
name: string;
image: string | string[];
mediaType: string;
description: string;
epoch: number;
};
policyId: string;
quantity: 1;
}[] = [];
const assetMetadataTemplate = JSON.parse(
Deno.readTextFileSync("metatemplate.json")
);
// Simulate use case
assets.push({
version: "cip25",
assetName: `anvilapicip25_${counter}`,
metadata: {
...assetMetadataTemplate,
// Adding custom data just to test the flow
name: `anvil-api-${counter}`,
epoch: new Date().getTime(), // dummy data
},
policyId: get_policy_id(policyAnvilApi.mint_script),
quantity: 1,
});
The counter is used to be sure that the assetName
remains unique.
Create the transaction
const data = {
changeAddress: customerWallet.enterprise_address_preprod,
mint: assets,
preloadedScripts: [
{
type: "simple",
script: policyAnvilApiScript,
hash: get_policy_id(policyAnvilApi.mint_script),
},
],
};
const urlTX =
"https://preprod.api.ada-anvil.app/v2/services/transactions/build";
const transactionToSignWithPolicyKey = await fetch(urlTX, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
"X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9",
},
}).then((res) => res.json());
Sign with the policy wallet
// Sign transaction with policy key
const transactionToSignWithCustomerKey = FixedTransaction.from_bytes(
Buffer.from(transactionToSignWithPolicyKey.complete, "hex")
);
transactionToSignWithCustomerKey.sign_and_add_vkey_signature(
PrivateKey.from_bech32(policyWallet.skey)
);
Sign with the customer wallet
usually done using the browser extension
// Sign transaction with metaManager key
const txToSubmitOnChain = FixedTransaction.from_bytes(
Buffer.from(transactionToSignWithCustomerKey.to_hex(), "hex")
);
// This sign the tx and add vkeys to the txToSubmitOnChain, so in submit we don't need to provide signautres
txToSubmitOnChain.sign_and_add_vkey_signature(
PrivateKey.from_bech32(customerWallet.skey)
);
Submit Transaction
const urlSubmit =
"https://preprod.api.ada-anvil.app/v2/services/transactions/submit";
const submitted = await fetch(urlSubmit, {
method: "POST",
body: JSON.stringify({
signatures: [], // This empty because the txToSubmitOnChain has the vkeys
transaction: txToSubmitOnChain.to_hex(),
}),
headers: {
"Content-Type": "application/json",
"X-Api-Key": "testnet_EyrkvCWDZqjkfLSe1pxaF0hXxUcByHEhHuXIBjt9",
},
}).then((res) => res.json());
console.debug(submitted);
Launch Deno
deno run -A index.ts
Dont forget to increase the counter, otherwise you will double mint.
Explorer
The Whole File (Deno Version)
Last updated
Was this helpful?