# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.ada-anvil.io/guides/nft-and-ft/mint-nft-cip-68/smart-contract/update-logic.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
