> For the complete documentation index, see [llms.txt](https://dev.ada-anvil.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example.md).

# Admin Update Example

{% hint style="info" %}
**Want to understand the smart contract validation logic first?** Check out the [Update Logic Guide](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic.md) to learn how the contract ensures proper authorization for metadata updates. Otherwise, you can dive right into the implementation below!
{% endhint %}

### Full Code Example

{% hint style="info" %}
**Want to see the complete code first?** Check out the [full working implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/admin-update.ts) on GitHub, then come back here for the step-by-step breakdown.
{% endhint %}

### Implementation Overview

This example demonstrates:

* Spending existing reference tokens for metadata updates
* Using spend validators with proper redeemer structures
* Admin authorization patterns for metadata changes
* Complete end-to-end update workflow

### Prerequisites

This guide assumes you have completed the [Minting Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example.md). Additionally, you need:

1. **Existing CIP-68 tokens** - Reference and user tokens from a successful mint
2. **Current reference token UTXO** - Transaction hash and output index where the reference token currently resides
3. **Understanding of spend validators** - Updates consume and recreate reference tokens with new metadata

### Step-by-Step Implementation

Let's build a CIP-68 metadata update script step by step. Each section will explain the concept and show the corresponding code.

#### Step 1: Setup and Configuration

Using the same setup from the minting example:

```typescript
import { Buffer } from "node:buffer";
import { FixedTransaction, PrivateKey } from "npm:@emurgo/cardano-serialization-lib-nodejs@14.1.1";
import customer from "./wallet-customer.json" with { type: "json" };
import adminWallet from "./wallet-mint-sc-policy.json" with { type: "json" };
import blueprint from "../aiken-mint-cip-68/plutus.json" with { type: "json" };
import { API_URL, HEADERS } from "../../../utils/constant.ts";

// Configuration - Update these values for your specific tokens
const CUSTOMER_ADDRESS = customer.base_address_preprod; // Change address for transaction
const ADMIN_KEY_HASH = adminWallet.key_hash; // Admin authorization key
const ASSET_NAME = "test"; // Name of the CIP-68 token to update

// NEW: Reference token UTXO to update
const TX_HASH = "d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861";
const TX_OUTPUT_INDEX = "0";
```

**Key difference from minting:**

* **TX\_HASH and TX\_OUTPUT\_INDEX** specify which existing reference token to update

#### Step 2: Apply Parameters to Blueprint

The apply-params process is identical to the minting example:

```typescript
const applyParamsResult = await fetch(`${API_URL}/blueprints/apply-params`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({
    params: { [blueprint.validators[0].hash]: [ADMIN_KEY_HASH] },
    blueprint: blueprint
  }),
}).then(res => res.json());

const parameterizedScript = applyParamsResult.preloadedScript;
const SCRIPT_HASH = Object.keys(applyParamsResult.addresses)[0];
const SC_ADDRESS = applyParamsResult.addresses[SCRIPT_HASH].bech32;
```

**Result:** Same parameterized script and address as your original mint

#### Step 3: Build the Update Transaction

Now we build the spend transaction that will update the metadata. The key insight is that we **spend** the existing reference token and **recreate** it with new metadata:

```typescript
const input = {
  changeAddress: CUSTOMER_ADDRESS,
  message: "CIP-68 Admin Update Example",
  
  // Script Interactions - Define how to interact with the smart contract during transaction execution
  scriptInteractions: [
    {
      // Purpose: "spend" tells Cardano this is spending a UTXO locked at a smart contract address
      purpose: "spend",
      
      // Hash: The parameterized script hash of our CIP-68 validator
      hash: SCRIPT_HASH,
      
      // OutputRef: Specifies exactly which UTXO we want to spend (the reference token)
      outputRef: {
        txHash: TX_HASH,        // Transaction hash containing the reference token UTXO
        index: TX_OUTPUT_INDEX, // Output index within that transaction (usually 0)
      },
      
      // Redeemer: The data passed to the smart contract's spend validator for authorization
      redeemer: {
        type: "json",
        value: {
          // output_index: Tells the validator which transaction output contains the updated reference token
          output_index: 0,
          
          // update: "AdminUpdate" triggers admin authorization path in the smart contract
          // This makes the validator check that the transaction is signed by the admin key
          // (as opposed to "UserUpdate" which would require fee payment)
          update: "AdminUpdate"
        },
      },
    },
  ],
```

**What's happening here:**

* **Script interactions**: Tells the smart contract we're spending a UTXO locked at the contract address
* **OutputRef**: Specifies exactly which reference token UTXO we want to update
* **Redeemer**: Contains the authorization data - "AdminUpdate" means admin signature is required

#### Step 4: Define the Updated Output

We need to send the reference token back to the smart contract with updated metadata:

```typescript
  outputs: [
    {
      // Send the updated reference token back to the smart contract address with the new metadata
      address: SC_ADDRESS,
      assets: [
        {
          assetName: {name: assetName, label: 100, format: "utf8"},
          policyId: SCRIPT_HASH,
          quantity: 1,
        },
      ],
      // Details how and where the datum is stored to be used later.
      datum: {
        type: "inline",
        shape: {
          validatorHash: SCRIPT_HASH,
          purpose: "spend", // Marked as spend purpose so the spend validator can update the metadata later.
        },
        value: {
          // Updated CIP-68 Metadata goes here. Follows official CIP-68 spec metadata format. (metadata, version, datum)
          metadata: [
            ["name", "test"],
            ["nickname", "admin_updated"] // Updated nickname to show the admin update
          ],
          version: 1
        }
      },
    },
  ],
  
  // Admin must sign to pass smart contract validation
  requiredSigners: [META_MANAGER_KEY_HASH],
  preloadedScripts: [parameterizedScript]
};
```

**What's happening here:**

* **Output**: The reference token is sent back to the smart contract with updated metadata
* **Datum**: Contains the new metadata following CIP-68 specification
* **Required signers**: Admin must sign the transaction for authorization
* **Preloaded scripts**: Include the parameterized script for validation

#### Step 5: Build and Submit Transaction

Now we build, sign, and submit the transaction:

```typescript
const contractDeployed = await fetch(`${API_URL}/transactions/build`, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify(input),
});

if (!contractDeployed.ok) {
  const errorText = await contractDeployed.text();
  console.error("❌ Transaction build failed:", errorText);
  Deno.exit(1);
}

const transaction = await contractDeployed.json();
console.log("✅ Transaction built successfully");

// Sign the transaction using CSL.
const txToSubmitOnChain = FixedTransaction.from_bytes(Buffer.from(transaction.complete, "hex"));
txToSubmitOnChain.sign_and_add_vkey_signature(PrivateKey.from_bech32(adminWallet.skey));

// Submit the transaction to the blockchain.
const urlSubmit = `${API_URL}/transactions/submit`;
const submitted = await fetch(urlSubmit, {
  method: "POST",
  headers: HEADERS,
  body: JSON.stringify({transaction: txToSubmitOnChain.to_hex()}),
});

const output = await submitted.json();
if (!submitted.ok) {
  console.error("❌ Transaction submission failed:", output);
  Deno.exit(1);
}

console.log("✅ Admin update transaction submitted successfully");
console.log("Transaction hash:", output.transactionId);
```

**What's happening here:**

* The tx-builder validates our spend transaction and creates a Cardano transaction
* We sign with the admin's private key (required for "AdminUpdate" authorization)
* The transaction is submitted to the Cardano network
* On success, we get back a transaction hash for tracking

### Running the Example

1. **Find your reference token UTXO**:
   * Use CardanoScan to search for your policy ID from the mint
   * Locate the reference token (label 100) at the smart contract address
   * Update `TX_HASH` and `TX_OUTPUT_INDEX` in your script
2. **Modify the metadata** in the `datum.value.metadata` section as desired
3. **Run the update script**:

   ```bash
   deno run --allow-all admin-update.ts
   ```

#### Expected Output

```
🔄 Applying parameters to blueprint...
✅ Parameters applied successfully
Parameterized script hash: 5da506b0b8d9c7d9df247c7684aead8eb6fdac1cbfb2dd7cc33d6499
Script address: addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6
✅ Transaction built successfully
✅ Admin update transaction submitted successfully
Transaction hash: d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861
```

### Understanding the Update Process

#### Spend Validator Logic

The CIP-68 update process uses a **spend validator** that:

1. **Validates Authorization**: Checks that the admin has signed the transaction
2. **Preserves Token Integrity**: Ensures the reference token stays at the smart contract
3. **Allows Metadata Changes**: Permits updates to the inline datum containing metadata
4. **Maintains CIP-68 Compliance**: Enforces proper metadata structure and versioning

#### Authorization Patterns

**Admin Update (shown in this example):**

* Requires admin signature
* No fee payment needed
* Full metadata update permissions

**User Update (alternative pattern):**

* Requires user token ownership proof
* Fee payment to admin
* Limited metadata update permissions

#### Transaction Flow

1. **Input**: Spend the existing reference token UTXO
2. **Validation**: Smart contract validates admin signature
3. **Output**: Create new reference token UTXO with updated metadata
4. **Result**: Metadata is updated while preserving token integrity

### Key Requirements

* **Track UTXO locations**: Reference tokens move with each update
* **Use same script hash**: From your original mint parameterization
* **Admin signature required**: Only the minting admin can perform admin updates

### Next Steps

This example demonstrates the complete CIP-68 admin update workflow. For production use:

1. **Add UTXO discovery** to automatically find current reference token locations
2. **Implement user updates** with fee payment and user token validation
3. **Add batch operations** for updating multiple tokens
4. **Create metadata validation** to ensure proper format and constraints
5. **Implement update history tracking** for audit trails

### References

* [CIP-68 Specification](https://cips.cardano.org/cip/CIP-68) - Official standard
* [Smart Contract Logic Guide](https://github.com/Cardano-Forge/anvil-api/blob/main/docs/guides/nft-and-ft/mint-nft-cip-68/smart-contract/logic.md) - Validator implementation details
* [Minting Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example.md) - How to create CIP-68 tokens
* [Working example implementation](https://github.com/Cardano-Forge/anvil-api-examples/blob/main/smart-contracts/cip-68/mint-cip68/admin-update.ts)
