import { Buffer } from "node:buffer";
import {
FixedTransaction,
PrivateKey,
} from "npm:@emurgo/[email protected]";
import { parseArgs } from "jsr:@std/cli/parse-args";
import {
createNativeScript,
timeToSlot,
getKeyhash,
} from "../utils/shared.ts";
import { API_URL, HEADERS } from "../utils/constant.ts";
const args: {
_: [];
expireDate: string;
customerWalletPath: string;
policyWalletPath: string;
metadataTemplatePath: string;
counter: number;
submit: boolean;
} = parseArgs(Deno.args);
// deno run -A mint-cli-cip25.ts \
// --expireDate=2026-01-01 \
// --customerWalletPath=customer.json \
// --policyWalletPath=policy.json \
// --metadataTemplatePath=metatemplate.json \
// --counter=20250117 \
// --submit
// NOTE: Be sure to send ADA in this address, it will be used to pay the tx fee.
const customerWallet = JSON.parse(
Deno.readTextFileSync(args.customerWalletPath),
);
const policyWallet = JSON.parse(Deno.readTextFileSync(args.policyWalletPath));
// Date before you can interact with the policy
const slot = await timeToSlot(new Date(args.expireDate));
const keyhash = await getKeyhash(policyWallet.base_address_preprod);
if (!keyhash) {
throw new Error("Unable to get key hash for policy, missing or invalid skey");
}
const nativeScript = await createNativeScript(keyhash, slot);
const assets: {
version: string;
assetName: { name: string; format: "utf8" | "hex" };
metadata: {
name: string;
image: string | string[];
mediaType: string;
description: string;
epoch: number;
};
policyId: string;
quantity: 1;
}[] = [];
const assetMetadataTemplate = JSON.parse(
Deno.readTextFileSync(args.metadataTemplatePath),
);
const counter = args.counter;
// Simulate use case
assets.push({
version: "cip25",
assetName: { name: `anvilapicip25_${counter}`, format: "utf8" },
metadata: {
...assetMetadataTemplate,
// Adding custom data just to test the flow
name: `anvil-api-${counter}`,
epoch: new Date().getTime(), // dummy data
},
policyId: nativeScript.hash,
quantity: 1,
});
//
// Generic calls to create, sign and submit tx
//
const data = {
changeAddress: customerWallet.enterprise_address_preprod,
mint: assets,
preloadedScripts: [
{
type: "simple",
script: nativeScript.script,
hash: nativeScript.hash,
},
],
};
const urlTX = `${API_URL}/transactions/build`;
const transactionToSignWithPolicyKeyResponse = await fetch(urlTX, {
method: "POST",
body: JSON.stringify(data),
headers: HEADERS,
});
const transactionToSignWithPolicyKey =
await transactionToSignWithPolicyKeyResponse.json();
console.log(transactionToSignWithPolicyKey);
console.debug(
"transactionToSignWithPolicyKey: ",
transactionToSignWithPolicyKey,
);
//
// policy signature
//
const transactionToSignWithCustomerKey = FixedTransaction.from_bytes(
Buffer.from(transactionToSignWithPolicyKey.complete, "hex"),
);
transactionToSignWithCustomerKey.sign_and_add_vkey_signature(
PrivateKey.from_bech32(policyWallet.skey),
);
console.debug(
"transactionToSignWithCustomerKey: ",
transactionToSignWithCustomerKey.to_hex(),
);
//
// customer signature
//
const txToSubmitOnChain = FixedTransaction.from_bytes(
Buffer.from(transactionToSignWithCustomerKey.to_hex(), "hex"),
);
console.debug("Customer Wallet", customerWallet);
// 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),
);
console.debug("txToSubmitOnChain: ", txToSubmitOnChain.to_hex());
console.debug(
"txToSubmitOnChain JSON: ",
txToSubmitOnChain.witness_set().to_json(),
);
//
// Submit tx
//
if (args.submit) {
const urlSubmit = `${API_URL}/transactions/submit`;
const submittedResponse = await fetch(urlSubmit, {
method: "POST",
body: JSON.stringify({
signatures: [], // This empty because the txToSubmitOnChain has the vkeys
transaction: txToSubmitOnChain.to_hex(),
}),
headers: HEADERS,
});
const submitted = await submittedResponse.json();
console.log(submitted);
} else {
console.log(txToSubmitOnChain.to_hex());
}