Creating Policy Scripts
Comprehensive guide to creating and managing policy scripts for minting tokens on Cardano using the Anvil API.
Introduction
Policy scripts are crucial components when minting tokens on Cardano. They define who has permission to mint or burn assets under a specific policy ID. This guide will walk you through creating policy scripts using the Anvil API's utility endpoints.
Workflow Overview
The process of creating and using policy scripts with the Anvil API follows these key steps:
Parse Wallet Address - obtain key hash(es) for signature requirements (supports multisig with multiple addresses)
Convert Date to Slot - create a time constraint (optional but recommended)
Create JSON Policy Script - combine key hash(s) and time constraints
Convert to Native Script - use CSL to create a machine-readable format
Generate Policy ID - derive the unique identifier for your assets
Use in Transaction - include the policy when minting tokens

Prerequisites
An Anvil API key
A new dedicated private wallet for your token collection (preferred) or an existing wallet address that will be used for signing, minting, and burning transactions
Understanding of policy scripts and their role in minting tokens
Security Best Practice: When creating policy scripts, it's recommended to generate a dedicated private wallet specifically for each collection rather than using an existing wallet address. Using a wallet address in your policy script means that wallet must sign every minting transaction, which creates security risks if building a minting service as it would require giving the service access to your wallet's signing capabilities.
Using the Utils Endpoints
The Anvil API provides several utility endpoints to assist with creating and managing policy scripts:
/utils/addresses/parse
Parses an address and returns its payment and stake credentials
/utils/native-scripts/get-info
Returns information about a provided native script (e.g., policy ID)
/utils/network/time-to-slot
Converts a date/time to a Cardano slot number
/utils/network/slot-to-time
Converts a Cardano slot number to a date/time
Step-by-Step Guide to Creating a Policy Script
Step 1: Obtain a key used for policy actions.
To create a signature-based policy script, you first need to obtain the key hash of the wallet that will sign the minting transactions. You can get this by parsing a wallet address using the address parsing endpoint:
// Example request
await fetch('https://prod.api.ada-anvil.app/v2/utils/addresses/parse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
address: 'stake_address_or_payment_address_here'
})
});
// Example response
{
"payment": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"stake": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
}
The payment
value from the response is what you'll use as the keyHash
in your policy script.
Step 2: Convert Date to Slot
For NFT collections, adding a time constraint ensures your collection becomes immutable after a certain date. To add a time constraint, you need to convert a future date to a Cardano slot number using the time-to-slot endpoint:
// Example request
await fetch('https://prod.api.ada-anvil.app/v2/utils/network/time-to-slot', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
timestamp: '2030-01-01T00:00:00Z'
})
});
// Example response
{
"slot": 98765432
}
This slot number will be used in your policy script to create a time-limited minting policy. While this step is optional, it's highly recommended for NFT collections to ensure they cannot be minted indefinitely.
Step 3: Create JSON Policy Script
Now that you have both the key hash (from Step 1) and optionally a slot number for time constraint (from Step 2), you can create the JSON structure for your policy script.
Policy scripts in the Anvil API follow a specific JSON structure that maps to Cardano's native script format. Each script uses a discriminated union with a type
field that determines the script's behavior.
Here's how to combine the signature requirement and time constraint into a complete policy script:
{
"type": "all",
"scripts": [
{
"type": "sig",
// Key hash from Step 1
"keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
},
{
"type": "before",
// Slot number from Step 2
"slot": 98765432
}
]
}
This JSON structure creates a policy that requires both the signature from the specified key hash AND the transaction to be submitted before the specified slot number.
Step 4: Convert to Native Script
While the JSON representation is useful for understanding, to actually use the policy script on the Cardano blockchain, you need to convert it to a native script format using the Cardano Serialization Library (CSL).
What is CSL? The Cardano Serialization Library (CSL) is a collection of tools that allows developers to interact with the Cardano blockchain at a low level. It provides functions to create, serialize, and deserialize Cardano data structures, including native scripts used for policy scripts.
import {
Ed25519KeyHash,
NativeScript,
NativeScripts,
ScriptAll,
ScriptPubkey,
TimelockExpiry
} from "@emurgo/cardano-serialization-lib-nodejs";
// Function to create a policy script with CSL
function createPolicyScript(policyKeyHash, ttl, withTimelock = true) {
// Create a collection of scripts
const scripts = NativeScripts.new();
// Add the signature requirement
const keyHashScript = NativeScript.new_script_pubkey(
ScriptPubkey.new(Ed25519KeyHash.from_hex(policyKeyHash))
);
scripts.add(keyHashScript);
// Add the timelock if requested
if (withTimelock) {
const timelock = TimelockExpiry.new(ttl);
const timelockScript = NativeScript.new_timelock_expiry(timelock);
scripts.add(timelockScript);
}
// Combine everything with an 'all' script
const mintScript = NativeScript.new_script_all(ScriptAll.new(scripts));
return mintScript;
}
This function converts the conceptual JSON policy script from Step 3 into a real native script object. Here's how you would use it with the values obtained in previous steps:
// Create the native script using the function above
const nativeScript = createPolicyScript(
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", // key hash from Step 1
98765432, // slot number from Step 2
true // include timelock
);
// Convert to hex format for use with APIs
const hexEncodedScript = nativeScript.to_hex();
Step 5: Generate Policy ID
Once you have created the native script, you can generate the policy ID. This unique identifier will be associated with all tokens minted under this policy.
You can get the policy ID by using the Anvil API endpoint:
// Get the hex representation for the API
const hexEncodedScript = nativeScript.to_hex();
// Use the API to get the policy ID
await fetch('https://prod.api.ada-anvil.app/v2/utils/native-scripts/get-info', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': 'YOUR_API_KEY'
},
body: JSON.stringify({
script: hexEncodedScript
})
});
// Example response
{
"policyId": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
}
Step 6: Use in Transaction
The final step is to use your policy script when building transactions that mint or burn assets. You'll include the policy script in the preloadedScripts
section of your transaction request:
TODO: Update payload based on changes made to the metadata
const data = {
changeAddress: "addr_test...",
utxos: ["8282...", "8282..."],
mint: [
{
version: "cip25",
assetName: { name: "MyAsset", format: "utf8" },
metadata: {
name: "My Asset",
image: ["ipfs://Qm..."],
// Additional metadata fields
},
policyId: policyId, // The policy ID from Step 5
quantity: 1
}
],
outputs: [
{
address: "recipient_address",
assets: [
{
assetName: "MyAsset",
policyId: policyId,
quantity: 1
}
]
}
],
// This is required for the first mint transaction. Anvil API will fetch the scripts from the provided policy ID for any subsequent transactions.
preloadedScripts: [
{
type: "simple",
script: {
// Your policy script from Step 3
type: "all",
scripts: [
{
type: "sig",
keyHash: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
},
{
type: "before",
slot: 98765432
}
]
},
hash: policyId // The policy ID from Step 5
}
]
};
const tx = await fetch(`https://prod.api.ada-anvil.app/v2/services/transactions/build`, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY"
}
});
This transaction, when signed and submitted, will mint a new asset under the policy ID you generated.
Common Policy Script Patterns
Basic Single-Signature Policy
{
"type": "sig",
"keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
Time-Limited Single-Signature Policy (Recommended for NFTs)
{
"type": "all",
"scripts": [
{
"type": "sig",
"keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
},
{
"type": "before",
"slot": 98765432
}
]
}
Multi-Signature Policy (Any of Two Keys)
{
"type": "any",
"scripts": [
{
"type": "sig",
"keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
},
{
"type": "sig",
"keyHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
}
]
}
Multi-Signature Policy with Threshold (2 of 3 Keys Required)
{
"type": "atLeast",
"required": 2,
"scripts": [
{
"type": "sig",
"keyHash": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
},
{
"type": "sig",
"keyHash": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
},
{
"type": "sig",
"keyHash": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
}
]
}
Conclusion
Creating policy scripts is a fundamental step in minting tokens on Cardano. By using the Anvil API utility endpoints, you can easily construct policy scripts with various constraints to match your project's requirements.
Remember to consider security implications when designing your policy scripts:
For NFT collections, using a time-limited policy provides authenticity and immutability.
For fungible tokens that may require future minting, consider using a multi-signature approach without time constraints.
Always keep your signing keys secure, as they are crucial for maintaining control over your minting ability.
Use dedicated policy keys only for signing minting and burning operations, not for other transactions.
Consider creating a separate treasury wallet to collect ADA from sales, keeping it separate from minting operations.
Related Resources
Last updated
Was this helpful?