> 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/update-logic.md).

# Update Logic

You've successfully minted CIP-68 tokens and now want to understand how metadata updates work through the **spend validator**!

{% hint style="info" %}
**Prerequisites**: This guide assumes you understand [CIP-68 minting logic](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-logic.md) and have successfully minted tokens using the [minting example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-example.md). The spend validator builds upon the mint validator's foundation.

**Ready to implement?** Skip to the [admin update example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example.md) or [user update example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/user-update-example.md) and return here when you want to understand the underlying validation logic.
{% endhint %}

### Conceptual Foundation

#### What the Spend Validator Does

While the [mint validator](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/mint-logic.md) creates CIP-68 token pairs, the **spend validator** enables metadata updates by controlling how reference tokens can be spent and recreated.

**Core Responsibilities:**

* ✅ **Authorizes Updates**: Two paths - admin signature OR user token ownership + fee
* ✅ **Preserves Token Integrity**: Reference tokens must stay at smart contract address
* ✅ **Maintains CIP-68 Compliance**: Updated metadata follows proper structure

#### The Update Process

Every CIP-68 metadata update follows this sequence:

1. **Spend Existing**: Consume the current reference token UTXO
2. **Validate Authority**: Check authorization (admin OR user + fee)
3. **Transform Metadata**: Apply authorized changes to the datum
4. **Recreate Token**: Send updated reference token back to smart contract

{% hint style="success" %}
**Key Insight**: Updates don't modify tokens in-place. They spend the old reference token and create a new one with updated metadata.

**See it in action**: Check out [Admin Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example.md) and [User Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/user-update-example.md) for complete implementations.
{% endhint %}

#### Two Authorization Paths

**👤 User Updates:**

* Must own user token (222) and include it as transaction input
* Must pay exactly 1 ADA fee to admin address
* Can only update specific fields (e.g., "nickname")
* Other metadata fields are preserved

**👑 Admin Updates:**

* Admin must sign the transaction with their private key
* No fee payment required
* Can update any metadata fields, can modify entire metadata structure and version

#### Validation Flow Overview

The diagram below shows how the spend validator processes both authorization paths:

{% @mermaid/diagram content="flowchart TD
subgraph inputs\["📥 Transaction Inputs"]
ref\_token\["🏷️<b> Reference Token UTxO</b><br>(Current metadata)"]
user\_token\["👤 <b>User Token UTxO</b><br>(Used for Ownership Check)"]
auth\_method\["<b>🔑 Authorization<br></b>(User OR Admin)"]
new\_metadata\["📝<b> New Metadata</b><br>(Updated content)"]
end
subgraph validation\["🔍 Validation Logic"]
check\_type{"Update Type?"}
user\_path\["👤 User Path"]
admin\_path\["👑 Admin Path"]
end
subgraph user\_checks\["👤 User Validation"]
check\_ownership\["✅ User Token Ownership"]
check\_fee\["✅ 1 ADA Fee Payment"]
check\_limited\["✅ Limited Field Updates"]
end
subgraph admin\_checks\["👑 Admin Validation"]
check\_signature\["✅ Admin Signature"]
check\_full\["✅ Full Metadata Update Permissions"]
end
subgraph core\["🛡️ Core Validation"]
check\_address\["✅ Ensure Reference token stays at SC address"]
check\_integrity\["✅ Token Integrity"]
check\_format\["✅ CIP-68 Format"]
end
subgraph outputs\["📤 Transaction Outputs"]
updated\_token\["🏷️ Updated Reference Token<br>→ Same address<br>→ New metadata"]
fee\_output\["💰 Fee Payment<br>(User updates only)"]
end
inputs --> validation
check\_type -- UserUpdate --> user\_path
check\_type -- AdminUpdate --> admin\_path
user\_path --> user\_checks
admin\_path --> admin\_checks
user\_checks --> core
admin\_checks --> core
core --> success\["✅ Validation Passed"] & failure\["❌ Validation Failed"]
success --> outputs

```
 ref_token:::inputStyle
 user_token:::inputStyle
 auth_method:::inputStyle
 new_metadata:::inputStyle
 check_type:::validationStyle
 user_path:::userStyle
 admin_path:::adminStyle
 check_ownership:::userStyle
 check_fee:::userStyle
 check_limited:::userStyle
 check_signature:::adminStyle
 check_full:::adminStyle
 check_address:::coreStyle
 check_integrity:::coreStyle
 check_format:::coreStyle
 updated_token:::outputStyle
 fee_output:::outputStyle
 success:::successStyle
 failure:::failureStyle
classDef inputStyle fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
classDef validationStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef userStyle fill:#fff3e0,stroke:#f57c00,stroke-width:2px
classDef adminStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
classDef coreStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px
classDef outputStyle fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
classDef successStyle fill:#c8e6c9,stroke:#4caf50,stroke-width:3px
classDef failureStyle fill:#ffcdd2,stroke:#f44336,stroke-width:3px" %}
```

### Authorization Logic Deep Dive

The spend validator implements two distinct authorization paths, each with specific validation requirements:

#### 👤 User Update Path

Users prove ownership and pay fees to update limited metadata fields:

```typescript
// Validation logic for user updates
UserUpdate { nickname, fee_output_index, output_ref_user_token } -> {
  // Verify 1 ADA fee payment to admin
  let fee_output = self.outputs |> at(fee_output_index)
  let lovelace_paid = fee_output.value |> quantity_of("", "")
  let is_paid_to_admin = fee_output.address.payment_credential == admin_key
  
  // Verify user token ownership
  let user_token_input = find_input(self.inputs, output_ref_user_token)
  let user_token_quantity = user_token_input.output.value |> quantity_of(policy_id, user_token)
  
  // Apply selective metadata changes (only "nickname" allowed)
  let expected_datum = selective_metadata_update(datum, nickname)
  
  // All conditions must pass
  and {
    (lovelace_paid == 1000000)?,
    is_paid_to_admin?,
    (current_datum == expected_datum)?,
    (user_token_quantity == 1)?
  }
}
```

**Requirements**: User token ownership + 1 ADA fee + limited field updates

#### 👑 Admin Update Path

Admins use signature authorization for full metadata control:

```typescript
// Simple signature check for admin updates
AdminUpdate -> list.has(self.extra_signatories, signer)
```

**Requirements**: Admin signature only + full metadata permissions

#### Core Validation (Both Paths)

All updates must also pass these fundamental checks:

* **Address Preservation**: `input.address == output.address`
* **Token Integrity**: `input.value == output.value` (same token, updated metadata)
* **Datum Structure**: Valid CIP-68 metadata format

### Implementation Examples

#### User Update Transaction Structure

```typescript
{
  scriptInteractions: [{
    purpose: "spend",
    // Reference token UTXO to update
    outputRef: "d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861#0",
    redeemer: {
      output_index: 0,
      update: {
        // New nickname as hex-encoded ByteArray
        nickname: Buffer.from("my_new_nickname").toString("hex"),
        // Output index for 1 ADA fee payment
        fee_output_index: 1,
        // User token UTXO reference (proves ownership)
        output_ref_user_token: {
          transaction_id: "1750a1c191646ade084c911fd9fd8a7c6b8372923e2305c4f4983663c2b236161",
          output_index: 1
        }
      }
    }
  }],
  
  outputs: [{
    // Updated reference token (same address)
    address: "addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6",
    assets: [{
      assetName: { name: "test_token", label: 100, format: "utf8" },
      policyId: "4983663c2b236161ad8e26c36dff9aee709a6adef53be2cc33d6499",
      quantity: 1
    }],
    datum: {
      type: "inline",
      value: {
        metadata: [["name", "test_token"], ["nickname", "my_new_nickname"]],
        version: 1
      }
    }
  }, {
    // 1 ADA fee to admin
    address: "addr_test1qpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6",
    value: { lovelace: 1000000 }
  }],
  
  // Include user token as input
  requiredInputs: ["828258201750a1c191646ade084c911fd9fd8a7c6b8372923e2305c4f4983663c2b236161015820..."]
}
```

#### Admin Update Transaction Structure

```typescript
{
  scriptInteractions: [{
    purpose: "spend",
    outputRef: "d330e666c15e2f19e54b49aff64e69aa134d25242b7dadd95d6aba570a7c1861#0",
    redeemer: {
      output_index: 0,
      update: "AdminUpdate"  // Simple string, no additional fields
    }
  }],
  
  outputs: [{
    address: "addr_test1wpw62p4shrvu0kwly378dp9w4k8tdldvrjlm9htucv7kfxggmdaj6",
    assets: [{
      assetName: { name: "test_token", label: 100, format: "utf8" },
      policyId: "4983663c2b236161ad8e26c36dff9aee709a6adef53be2cc33d6499",
      quantity: 1
    }],
    datum: {
      type: "inline",
      value: {
        // Admin can update any fields and version
        metadata: [["name", "test_token"], ["description", "Updated by admin"], ["rarity", "legendary"]],
        version: 1
      }
    }
  }],
  
  // Admin signature required
  requiredSigners: ["a1b2c3d4e5f6789012345678901234567890123456789012345678901234"]
}
```

### Production Considerations

#### Security Model

* **User Updates**: Token ownership + 1 ADA fee + limited permissions
* **Admin Updates**: Signature authorization + full permissions
* **Token Integrity**: Reference tokens locked at smart contract address
* **Audit Trail**: All metadata changes recorded on-chain

#### Common Errors

**User Update Failures:**

* `user_token_quantity != 1` - User doesn't own the token
* `lovelace_paid != 1000000` - Incorrect fee amount
* `current_datum != expected_datum` - Unauthorized metadata field update

**Admin Update Failures:**

* `!list.has(extra_signatories, signer)` - Missing admin signature
* `input.address != output.address` - Address preservation failed

**Core Validation Failures:**

* `datum not Cip68Metadata` - Invalid metadata structure
* `input.value != output.value` - Token integrity violated

### Next Steps

{% content-ref url="/pages/NMtrkrYlVgEYyBzBdAwS" %}
[User Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/user-update-example.md)
{% endcontent-ref %}

{% content-ref url="/pages/WA0GZ6gX6U76AGpzBGYD" %}
[Admin Update Example](/guides/nft-and-ft/mint-nft-cip-68/smart-contract/admin-update-example.md)
{% endcontent-ref %}

### Technical References

* [CIP-68 Specification](https://cips.cardano.org/cips/cip68/) - Official standard
* [Aiken Smart Contract Language](https://aiken-lang.org/) - Contract development
