Understanding Policy Scripts
Comprehensive guide to Cardano policy scripts, their structure, components, and best practices when minting tokens with the Anvil API.
Introduction
Policy scripts are a fundamental component of the Cardano blockchain that define who has permission to mint or burn assets under a specific policy ID. These scripts create the cryptographic foundation of your NFT or token collection and establish the rules for minting authority. This guide explains how policy scripts work, their structure, and best practices for implementation.
What is a Policy Script?
A policy script is a logical expression that defines the conditions under which tokens can be minted or burned under a specific policy ID. The policy ID is derived by hashing the policy script, creating a unique identifier for your asset collection on the blockchain.
When included in a minting or burning transaction, policy scripts are evaluated by the Cardano network to verify that the transaction satisfies all conditions before allowing new assets to be created or burned.
For a detailed, step-by-step guide on creating policy scripts using the Anvil API, see our Creating Policy Scripts guide.
Policy Script Structure
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:
// Base structure for all policy scripts
{
"type": "sig" | "before" | "after" | "all" | "any" | "atLeast",
// Additional properties based on the type
}
The underlying zod schema validates the following structures:
// Main Policy Script Schema
export const policyScriptSchema = z.discriminatedUnion("type", [
// Signature requirement
z.object({ type: z.literal("sig"), keyHash: z.string() }),
// Time constraints
z.object({ type: z.enum(["before", "after"]), slot: z.number() }),
// Logical AND/OR
z.object({
type: z.enum(["any", "all"]),
scripts: z.array(/*recursive policy scripts*/)
}),
// At least N of M
z.object({
type: z.literal("atLeast"),
required: z.number(),
scripts: z.array(/*recursive policy scripts*/)
}),
]);
Script Types
Signature Scripts
Signature scripts (sig
) require a specific key to sign the transaction. This is the most common constraint used to ensure only authorized parties can mint tokens:
{
"type": "sig",
"keyHash": "KEY_HASH_HERE"
}
The keyHash
is the hash of the verification key that must sign the transaction.
Time Constraints
Time constraints limit minting to specific time windows using slot numbers:
{
"type": "before",
"slot": 100000000
}
or
{
"type": "after",
"slot": 50000000
}
before
: The transaction must be submitted before the specified slot numberafter
: The transaction must be submitted after the specified slot number
Logical Operators
In Simple Terms:
all
means "AND" (everything must be true),any
means "OR" (at least one thing must be true). So if you want to allow either Alice OR Bob to sign, useany
. If you need both Alice AND Bob to sign, useall
.
Logical operators combine multiple constraints:
{
"type": "all",
"scripts": [
// Array of other scripts that must ALL evaluate to true
]
}
or
{
"type": "any",
"scripts": [
// Array of other scripts where at least ONE must evaluate to true
]
}
Threshold Requirements
The atLeast
operator allows you to specify a minimum number of conditions that must be met:
{
"type": "atLeast",
"required": 2,
"scripts": [
// Array of scripts where at least 'required' number must evaluate to true
]
}
This is particularly useful for multi-signature scenarios where you might want to require, for example, at least 2 out of 3 possible signers to authorize minting.
Best Practices
Security Considerations
Always Include a Signature Requirement: While technically optional, you should always include at least one signature constraint in your policy scripts for security. A policy without signature requirements allows anyone to mint tokens with your policy ID.
Time-Limited Policies: For NFT collections, consider implementing time-limited minting windows using both signature and time constraints:
{ "type": "all", "scripts": [ { "type": "sig", "keyHash": "KEY_HASH_HERE" }, { "type": "before", "slot": 12345678 } ] }
This pattern ensures your collection becomes immutable after a certain date, which is important for NFT value and trust.
Multi-Signature Requirements: For projects with multiple stakeholders, consider using nested logical operators:
{ "type": "all", "scripts": [ { "type": "any", "scripts": [ { "type": "sig", "keyHash": "OWNER1_KEY_HASH_HERE" }, { "type": "sig", "keyHash": "OWNER2_KEY_HASH_HERE" } ] }, { "type": "before", "slot": 12345678 } ] }
Implications for Token Operations
Time-Limited Policies and Token Operations
When using time-limited policies (with a before
constraint), understand the implications for various operations:
Minting New Tokens:
Only possible before the specified slot time
Attempting to mint after this time will fail validation
Keep track of your policy's deadline to avoid transaction failures
Burning Tokens:
Requires the same policy script verification as minting
Cannot burn tokens after the time limit expires
To burn tokens, use a negative quantity in your mint request
Metadata Updates:
On-chain metadata (label 721) cannot be modified after being written
For updatable NFTs, consider using CIP-68 which stores data in datums
Time-limitations affect your ability to issue updated versions of tokens
Important: If your use case requires future updates to tokens, avoid time-limited policies or ensure the time window is sufficient for your project's lifecycle.
Using Policy Scripts with the Anvil API
When to Include Policy Scripts
The policyScripts
object in your Anvil API request is:
Required: When minting tokens with a new policy ID that hasn't been used before
Optional: When minting additional tokens with an existing policy ID
Performance Note: Including the policy script even for existing policies improves performance. Without it, the Anvil API will automatically fetch the script from the blockchain in the background, which adds processing time to your request.
Script Verification
When you provide a policy script, the Anvil API performs several validations:
The policy script is parsed and its hash is computed
The computed hash is compared to the policy ID specified in the
policyId
fieldIf the hashes don't match, the transaction is rejected with a
Policy script and policy Id do not match
error
Example API Usage
{
"changeAddress": "CHANGE_ADDRESS_HERE",
"utxos": "UTXOS_HERE",
"mint": {
"assets": [
{
"version": "cip25",
"assetName": { "name": "mynft", "format": "utf8" },
"metadata": { /* Use https://metadraft.io to generate accurate metadata easily. */ },
"policyId": "POLICY_ID_HERE",
"quantity": 1
}
],
"policyScripts": {
"POLICY_ID_HERE": {
"type": "all",
"scripts": [
{
"type": "sig",
"keyHash": "KEY_HASH_HERE"
},
{
"type": "before",
"slot": 100000000
}
]
}
}
}
}
Common Errors and Troubleshooting
Unable to retrieve script for policy: The policy doesn't exist on-chain and wasn't provided in the request
Policy script and policy ID do not match: The provided script's hash doesn't match the specified policy ID
Missing policy scripts: Required for first-time minting but wasn't included in the request
References
Last updated
Was this helpful?