# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
This site has an agentic ask interface you may use to query the documentation dynamically by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.catalog.cat/index.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

---

# CATalog FAQ

Frequently Asked Questions about CATalog

### What is CATalog?

CATalog is a decentralized, on-chain shared database containing CAT asset details such as symbols, names, and display images. CAT issuers register their CATs and receive an NFT which can be used to change the corresponding entry's details at any time. Verifiers - usually marketplaces or sites that display CAT data - may maintain curated lists of CATs that meet their listing rules (e.g., 'The CAT is not trying to impersonate another CAT'). Because everything is on-chain and on DataLayer, all websites, wallets, and dApps can see data and act on it.

**Registration != verification.** On-chain registration establishes ownership of a TAIL hash in the shared registry. Verifier curation is an optional second layer: marketplaces and sites apply their own policies on top of the on-chain data.

### How is a CAT's owner determined?

The owner of a CAT is defined as the first entity to register that CAT's TAIL program on CATalog. For this reason, issuers **must** use a CAT issuance tool that supports CATalog in order to avoid someone else claiming their CAT's entry.

### How should I refer to CATalog?

Please always spell CATalog like this sentence does - the first three letters should be in uppercase, while the rest of the word is in lowercase. The only exception to this rule is Rust code, where you may spell CATalog as `Catalog` to meet naming conventions.

### What are the fees to list a CAT?

Registration fees are stored on-chain in the registry state as `registration_price` (CAT mojos paid to the payment CAT defined by `cat_maker_puzzle_hash`). At the time of writing, mainnet commonly uses **5 wUSDC.b** per entry, but **always check the current on-chain state** - the price singleton can change both the fee token and the amount.

CATalog entries do **not** expire and do not require renewal fees. Verifiers may require additional funds in exchange for listing or curating CATs.

Fee governance flows through a **Medieval Vault** multisig (6-of-10 [warp.green validators](https://docs.warp.green/#who-are-the-validators) on mainnet), which updates registry pricing via the `delegated_state` action.

### How do I register a CAT?

Registration is a two-phase process:

1. Create a precommitment coin (payment CAT locked with TAIL reveal and NFT recipient)
2. Wait `relative_block_height` blocks (typically 32 on mainnet), then complete registration

At launch, a web app that makes this process very easy is planned. You can also use the [slot-machine](https://github.com/Yakuhito/slot-machine) CLI with a running [Sage wallet](https://github.com/xch-dev/sage):

```bash
cargo run -- catalog register --tail-reveal <hex> --ticker ... --name ... ...
```

If the precommit becomes invalid (wrong fee, wrong CAT, or TAIL already registered), use `catalog register --refund`. Full command reference: [CLI Reference](/technical-manual/cli).

### How do I know CATalog was properly deployed?

Anyone can use [slot-machine](https://github.com/Yakuhito/slot-machine/) with [chia-wallet-sdk](https://github.com/xch-dev/chia-wallet-sdk) to verify CATalog's deployment:

```bash
cargo run -- catalog verify-deployment # mainnet
cargo run -- catalog verify-deployment --testnet11
```

This checks the launcher, premine registrations (`catalog_premine_mainnet.csv`), price schedule (`catalog_price_schedule_mainnet.csv`), and state scheduler wiring against trusted CSVs in the slot-machine repo.


# Slots

Long-term memory for singletons

*Note*: The slot puzzle is available [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/slot.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/slot.clsp)). Slot helper functions (`spend_slot`, `create_slot`) are in [slot.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/slot.rue).

A slot is a coin intended to store values that may be read at a later time/spend by the singleton that created it. In a way, slots allow singletons to have 'long-term' memory. Another way to think about slots is 'one-time tickets only redeemable by the issuing singleton.'

To read the values, the singleton will spend the slot coin by sending an empty message with mode 18 (puzzle-puzzle).

Slots are curried twice: the first time, `SINGLETON_STRUCT` and `NONCE` are curried in. Nonce is used to distinguish different slots when needed - for example, a 'user rewards' slot might have a different nonce than a 'round reward info' slot, even though they both have the same structure in their values (e.g., 3 numbers). This deters attacks where a slot of one type may be confused by a slot of another type with the same data structure, as the 1st curry depends on the slot type (nonce). Controller singletons will usually store the 1st curry hash (e.g., `SLOT_1ST_CURRY_HASH` in CATalog's register action), as the value is static and unique for every singleton controller launcher id and nonce.

The second curry adds `VALUE_HASH` to the puzzle. This is the hash of the value the controller singleton wants to consume. Slots only store hashes since this means the whole value doesn't have to be revealed in the slot's puzzle reveal, potentially saving a significant number of bytes for each slot spend. As a general rule of thumb, the full values of spent and created slots can usually be determined by looking at a singleton's solution - this is the case for CATalog, XCHandles, and the reward distributor.

The slot puzzle asserts it was created by a singleton with the same launcher id as the singleton spending the slot, ensuring the slots can't be faked. The puzzle does not, however, assert anything about the controller singleton's inner puzzle. This property accommodates more complex 'launches' where, for example, the CATalog eve singleton has a custom inner puzzle that launches two special slots (list ends, which have special properties) and then sets the new inner puzzle to the 'dApp' puzzle that allows anyone to register new CATs.

### CATalog slot values

In CATalog, each slot's value is a doubly-linked list node keyed by TAIL hash (asset id). On-chain, the value has the shape:

`(counter . (asset_id . (left_asset_id . right_asset_id)))`

The `counter` is incremented for every slot spend. List ends use sentinel values with `>s` byte-string ordering:

* **Left end:** `SLOT32_MIN_VALUE` (`0x00..00`)
* **Right end:** `SLOT32_MAX_VALUE` (`0xff..ff`)

These constants are defined in the SDK as [`CatalogSlotValue`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-types/src/puzzles/action_layer/slot_values.rs). The driver primitive is [`Slot<CatalogSlotValue>`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/primitives/action_layer/slot.rs).

**Note**: dApps should choose the hinting strategy that works best for their particular design. CATalog and XCHandles slots, for example, use the first curry hash as a hint to allow for much faster syncs. The Reward Distributor, on the other hand, uses either a reward/clawback puzzle hash or the hash of the epoch the slot is representing, minimizing the number of look-ups done for any interaction as the whole history of the singleton doesn't have to be parsed.

**Warning**: Slots do not ensure uniqueness by themselves. Users can always 'omit' a given slot to trick a naive dApp that the slot doesn't exist. For example, a naive handle registration app might be tricked into registering the same handle twice. If dApps need to ensure slots are not skipped, they should index them or build more advanced data structures such as cross-slot doubly-linked sorted lists. More information about the technique can be found in [this blog post](https://blog.fireacademy.io/p/solving-the-problem-of-uniqueness).

**Warning 2**: Slots assume the controller singleton always has an amount of 1.

**Warning 3**: Whenever slots are re-issued, it is best to include a counter in them to avoid re-entrancy-equivalent attacks.

*Written by* [*yakuhito*](https://x.com/yakuhito) *from* [*FireAcademy.io*](https://fireacademy.io/) *on Feb 11th, 2025.*


# Action Layer

A special primitive designed to accommodate the needs of Chia dApps

*Note*: The base action layer code is available [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/action.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/action.clsp)). The default finalizer code is available [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/finalizer.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/finalizer.clsp)). The reserve finalizer code is available [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/reserve_finalizer.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/reserve_finalizer.clsp)).

The action layer is intended to be a dApp singleton's inner puzzle. The puzzle maintains a state, which is usually a list of frequently-needed variables that the dApp can update.

The action layer allows anyone to select one or more actions to run. The list of actions is stored as a Merkle tree, with the corresponding `MERKLE_ROOT` being static by default (the latter property may be changed with a custom finalizer).

The layer ensures every action is part of the Merkle tree and runs it with a custom solution, also providing the State truth. An action's solution will always be: `(c State_Truth solution)` , where `solution` is user-provided and must be validated by the action. `State_Truth` is `(Ephemeral_State . Persistent_State)`. The ephemeral state starts as `()` every spend (i.e., the first action always receives the null value) and can be modified for the next action through return values (see below). The persistent state is saved in the action layer's puzzle - it is, by default, always set by the last run action, except for the very first value, which is set when the eve action layer is first created. The puzzle is also optimized such that each action's code is only revealed at most once each spend, no matter how many 'calls' are made to run the action.

The ephemeral state offers flexibility for dApps. For example, some dApps might use it as a counter which, along with the coin id, determines a nonce for asserting payments via the normal offer puzzle. Other dApps may use it as a switch to indicate whether the action being run is the first one in the spend - along with `(list ASSERT_HEIGHT_RELATIVE 0)`, the ephemeral state makes it possible to make an action that can only run as the first action in a given block.

Every action is expected to return two new values as a cons-box: `(c new_state_truth new_conditions)`. `new-conditions` will be added to the coin's output conditions list when using the default finalizer - this behavior, however, may be modified with a custom finalizer. `new_state_truth` will be provided to the next action that runs. The finalizer will usually set the next coin's `STATE` as `new_state = (r new_state_truth)` using values returned by the last evaluated action. This is the case for both the default and the reserve finalizers.

Allowing multiple `actions` to run significantly lowers cost compared to only running one action per singleton spend. Moreover, this architecture is RBF-friendly: when a spend is pending in the mempool, a user wanting to use the singleton may add their actions at the end of the array of pending actions, completely avoiding the creation of a new singleton coin.

The action layer is split into two parts. The base layer covers the functionality described above, validating actions can be run and evaluating them with the most recent state. The finalizer is used to 'finish' the spend by handling everything else.

The finalizer is given action outputs as a list where item *i* is the output of the \_i\_th action that was run. The default finalizer simply flattens the list and re-creates the action singleton using the new state (i.e., the state returned by the last action).

The finalizer, however, can be customized. A good example is the reserve finalizer, which, aside from flattening the list and re-creating the singleton, also manages an external token reserve owned by the singleton. A finalizer has access to the initial state of the singleton (i.e., the `STATE` parameter that was curried in in the current base action layer), as well as the new state. The reserve finalizer uses this information to spend the current singleton's reserve and recreate it. Moreover, actions of an action layer using the reserve finalizer can add conditions to be included in the reserve's output by prepending them with `-42`. In other words, if one of the conditions outputted by a given action is `(list -42 CREATE_COIN puzzle_hash 1 (list hint))` , the condition will be taken by the finalizer and included in the reserve's delegated puzzle, ensuring the reserve has `(list CREATE_COIN puzzle_hash 1 (list hint))` in its output.

Custom finalizers can be used to significantly reduce cost and complexity of some dApps. For example, a Reward Distributor processing 10 payouts in the same spend will only spend its reserve once. Moreover, individual actions don't have to send any type of messages to the reserves or ensure that they exist.

**Warning**: Most finalizers, including the default and reserve ones, will assume the singleton's amount to be 1.

**Warning 2**: The reserve finalizer treats the first amount in the state as the reserve amount.

### CATalog Actions

The CATalog registry uses the default finalizer and a Merkle tree of three actions (see [`CatalogRegistryInfo::action_puzzle_hashes`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/primitives/action_layer/catalog_registry_info.rs)):

1. [`CatalogRegisterAction`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/layers/action_layer/actions/catalog/register.rs)
2. [`CatalogRefundAction`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/layers/action_layer/actions/catalog/refund.rs)
3. [`DelegatedStateAction`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/layers/action_layer/actions/delegated_state.rs)

*Written by* [*yakuhito*](https://x.com/yakuhito) *from* [*FireAcademy.io*](https://fireacademy.io/) *on Feb 11th, 2025.*


# Commitments

Commit-reveals made easy

*Note*: The inner puzzle for commitment coins can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/precommit.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/precommit.clsp)).

Sometimes, dApps need to use a commit-reveal pattern. A good example is registering a CAT on CATalog: a user only needs a TAIL reveal to register their CAT. However, one cannot simply provide it to a dApp: a malicious farmer could see the transaction, extract the TAIL reveal, and register the CAT instead.

To solve this issue, CATalog and XCHandles employ a commit-reveal mechanism. Users wanting to register a CAT must create a commitment coin that contains the TAIL reveal, as well as other registration-specific data. The commitment coin must stay unspent for a given number of blocks (`relative_block_height` blocks) after creation. On mainnet this is typically 32. After the commit phase is over, the user can reveal the TAIL (along with a receiver puzzle hash for the registration) by spending the commitment. A mempool observer cannot register the same corresponding CAT, as they'd have to create a commitment coin and wait the designated number of blocks before the puzzle becomes spendable.

### CATalog Precommit Value

The precommit coin's inner puzzle is wrapped in a CAT layer using the registry's current CAT maker. The committed value stored on-chain is:

`(tail_reveal initial_nft_owner_ph cat_maker_hash . cat_maker_solution)`

### Spend Modes

When the registry spends a precommit coin via `SendMessage`, the message selects the outcome:

* **Message `0x01` (register):** funds go to the registry's payout puzzle hash (successful registration).
* **Message `0` (refund):** funds go to the refund puzzle hash specified in the precommit.

To commit assets other than XCH, commitment puzzles must be wrapped in layers such as the CAT v2 one. The puzzle itself supports this, but dApps must ensure that the controller singleton can build the correct full puzzle for commitment coins (by using CAT makers, for example - described in the next page).

It's vital to note that any dApp must also carefully consider refunds. Schemes using commitments lock funds and involve two distinct transactions. Sometimes, commitment coins that appeared valid when the first transaction might become invalid by the time the second (reveal) transaction can be made. As such, controller singletons should offer a mechanism where funds can be returned to users if the commitment can't be used for a registration. For example, CATalog can 'refund' locked funds if the CAT in which payments are made has changed, the payment amount is wrong, or if the TAIL has already been registered.

**Warning**: Refund logic must be carefully considered and reviewed. If any commitment can be refunded, for example, a malicious farmer may change a registration action to a refund one. They may then create a commitment for the same TAIL hash. The rightful owner of the CAT would not be able to register their CAT as they would have to start the registration process all over again - which they only realize after the malicious farmer's commitment coin has at least 1 confirmation (i.e., it was already included in at least the most recently farmed block).

**Warning 2**: Commitment coins are only effective if implemented correctly. For example, only committing a TAIL by itself is not useful at all - a farmer can use the same commitment coin to register the CAT to their address. CATalog commitments also contain the user's NFT receive address, as well as other data.

*Written by* [*yakuhito*](https://x.com/yakuhito) *from* [*FireAcademy.io*](https://fireacademy.io/) *on Feb 11th, 2025.*


# Other Useful Concepts

Useful concepts to have in mind when trying to decode Technical Manual(s)

### Controller Singletons

Most complex dApps - including TibetSwap, warp\[.]green, CATalog, XCHandles, and the Reward Distributor - use singletons that contain the majority of the important logic. For example, while TibetSwap pools have two reserves each, it is the pool singleton that calculates the new amount of reserves each spends and communicates the re-creation conditions, which the reserves 'blindly' follow after receiving announcements/messages from the singleton. More generally, a dApp singleton may create 'helper' coins that store information or value (be it in XCH or CATs), but the 'most important' logic lies within the singleton. Controller singletons is a term sometimes used to describe this property both in documentation and Chialisp comments/variable names. It signifies that the 'burden of logic' is located in the singleton, not its controlled (helper) coin(s).

### Malicious Farmers

A dApp developer's worst nightmare is almost certain to include a malicious farmer. This term is used as the embodiment of a malicious actor. The 'malicious farmer' case is used to exemplify potential attacks because farmers usually have the ideal conditions to profit from badly-designed dApps: they know the contents of the blocks they're about to farm and, unlike normal farmers, wish to hurt normal users, usually by maximizing farmer rewards (in terms of assets - XCH, CATs, handles in demand, and so on). Malicious actors may not be able to modify mempool items beyond RBF capabilities or modify the order of future transaction blocks at will. However, a well-designed app should withstand the 'worst-case scenario' where the adversary has control over as many aspects as possible to really be considered secure.

### CAT Makers

Managing CAT reserves is more difficult than it might initially seem. A CAT's outer and inner layer are known to a singleton (the inner puzzle is usually a 'pay to delegated puzzle by singleton puzzle') - however, the intermediary layers are not. A DAO CAT might have an intermediary layer that manages active votes, while revocable CATs have a revocation layer sitting between the outer and inner puzzles. A dApp might want to allow the flexibility of being able to 'own' or interact with CATs whose intermediary layers haven't yet been written. CATalog and XCHandles, for example, want this flexibility in the CAT used for registration payments. To have it, they allow governance (the price singleton) to set the current CAT maker instead of CAT tail hash.

*Note*: The default CAT maker can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/default_puzzles/default_cat_maker.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/default_puzzles/default_cat_maker.clsp)). The revocable CAT maker can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/default_puzzles/revocable_cat_maker.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/default_puzzles/revocable_cat_maker.clsp)).

The puzzle curries in the tail hash hash (double-hashing is simply employed for efficiency) and accepts the desired inner puzzle hash of a coin, as well as a user solution. It returns the full puzzle hash of a CAT coin with those parameters. While the user-provided solution is not used, one can imagine a case where the user could supply intermediary layer state such as active votes for a DAO CAT. Note that the maker does not return any output conditions.

To summarize, CAT makers are short, simple to write 'adapters' that produce the full puzzle hash for a CAT coin given outer layer parameters (e.g., TAIL hash) and the inner ('custody') puzzle. They're employed to support CATs with different intermediary layer structures.

### The Delegated State Action

The registration fees for CATalog and XCHandles are paid in CAT tokens (initially, these represent warped stablecoins). One design decision was to allow another singleton (sometimes referred to as the 'price singleton') to control pricing parameters and puzzles. For CATalog, this means that the pricing singleton may change the CAT the registration fees are paid in, as well as the amount. XCHandles also allows the pricing singleton to change both pricing puzzles (the one that controls the 'base' price of a handle and the one that handles expiry auctions).

Since both registries' states consist fully of pricing parameters, a 'delegated state' action was added to both. The action updates the current singleton's state to one sent by the price singleton via a message with mode 18 (puzzle-puzzle). While the action has limited potential to be useful to other dApps, it enables the pricing parameter freedom that both XCHandles and CATalog require.

*Note*: The delegated state action code can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/actions/shared/delegated_state.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/actions/shared/delegated_state.clsp)).

### Launching

Live dApps will have restrictions on what can be done - CATalog, for example, only allows slot keys to be the hash of a TAIL, and left neighbor < right neighbor. The Reward Distributor needs at least one slot containing round information to work. These rules may have to be broken during setup. For the doubly-linked lists to exist in CATalog (and XCHandles, for that matter), the special 'list ends' must be created. These ends have the lowest and highest possible values as keys (see notes below) - which we don't know the preimage (TAIL reveal) of. Similarly, the Reward Distributor launch process creates a slot for the first round.

*Note*: Earlier (draft) versions of CATalog/XCHandles used `>` to order slot values. The lowest 32-byte value when using `>` for comparisons is `0x80..00`, while the highest 32-byte value when using `>` for comparisons is `0x7f..ff`. The latest version, however, uses `>s`, which means that values are sorted as one would expect them to be (`0xff..ff` > `0x80..00` > `0x7f..ff` > `0x00..00`).

These special slots are needed for the first registration and may be needed for subsequent dApp interactions as well. To avoid a lot of special casing, CATalog uses a special inner puzzle for the eve singleton that creates the two ends as slots and re-creates the singleton using the 'regular' inner puzzle. More generally, dApps might want to allow 'breaking the rules' at launch - people verifying a dApp is truly decentralized must ensure the 'constrained' inner puzzle is currently inside the dApp singleton and the transition is only one way (i.e., the unconstrained inner puzzle can create the constrained puzzle, but not the other way around - otherwise the current inner puzzle isn't really constrained).

**Warning:** Special care must be put into ensuring that everything done when there are no restrictions is compatible with what can be done when there are restrictions. For example, CATalog must not break when it interacts with one or both of the slots representing the ends of the doubly-linked linked list.

### Premines

Premines are another way of bending the rules at launch. The number of 'unconstrained' generations of a singleton before it's upgraded to a dApp should be minimal, ideally zero (although 1 is far more practical). This minimizes the 'special case' attack surface considerably. However, some dApps might still want to 'premine' a lot of records. For example, CATalog might need to 'import' CATs registered before launch. This is likely a multi-block endeavor requiring going through the 'normal' registration process with more constraints - only the deployer should be able to do it, and it should be easily auditable afterwards. From parsing and security perspectives, this is much easier done by creating a special payment CAT that only the deployer owns. The deployer registers all 'imported' CATs before switching the payment CAT to a non-exclusive one (e.g., wUSDC.b) and transferring control of the price singleton to its intended party. This initial 'gated initiation' can then easily be parsed and verified by software that checks whether a registry was correctly launched.

*Written by* [*yakuhito*](https://x.com/yakuhito) *from* [*FireAcademy.io*](https://fireacademy.io/) *on Feb 11th, 2025.*


# Puzzles

CATalog puzzle inventory in slot-machine

Canonical CATalog puzzle sources live in [slot-machine](https://github.com/Yakuhito/slot-machine). Rue sources compile to Chialisp under `puzzles/` via `./build.sh`. Compiled bytecode is embedded in [chia-wallet-sdk](https://github.com/xch-dev/chia-wallet-sdk) for drivers.

*Note*: This page has been mostly AI-generated.

## Singleton / Action Layer

| Puzzle            | Rue                                                                                                                                          | CLSP                                                                                                                                       | Purpose                                                                                                  |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------- |
| Action layer      | [action.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/action.rue)                                          | [action.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/action.clsp)                                          | Standardized way to easily allow state to be kept/tracked, as well as running multiple actions per spend |
| Finalizer         | [finalizer.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/finalizer.rue)                                    | [finalizer.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/finalizer.clsp)                                    | Recreate singleton with new state (standard/most basic finalizer)                                        |
| Reserve finalizer | [reserve\_finalizer.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/reserve_finalizer.rue)                   | [reserve\_finalizer.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/reserve_finalizer.clsp)                   | Finalizer + reserve management (`-42` prefix)                                                            |
| Slot              | [singleton/slot.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/slot.rue)                                    | [singleton/slot.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/slot.clsp)                                    | One-time-read slot coin                                                                                  |
| Slot helpers      | [slot.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/slot.rue)                                                        | n/a                                                                                                                                        | `spend_slot`, `create_slot` utilities                                                                    |
| Precommit         | [precommit.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/precommit.rue)                                    | [precommit.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/precommit.clsp)                                    | Commit-reveal payment coin                                                                               |
| State scheduler   | [state\_scheduler.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/state_scheduler.rue)                       | [state\_scheduler.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/state_scheduler.clsp)                       | Price singleton inner puzzle during launch (changes price according to a pre-set schedule)               |
| P2 by singleton   | [p2\_delegated\_by\_singleton.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/singleton/p2_delegated_by_singleton.rue) | [p2\_delegated\_by\_singleton.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/p2_delegated_by_singleton.clsp) | A 'custody puzzle' (p2) for a singleton                                                                  |

## CAT Makers

| Puzzle              | Rue                                                                                                                                   | CLSP                                                                                                                                | Purpose                                    |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| Default CAT maker   | [default\_cat\_maker.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/default_puzzles/default_cat_maker.rue)     | [default\_cat\_maker.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/default_puzzles/default_cat_maker.clsp)     | CAT maker for standard/vanilla CATs        |
| Revocable CAT maker | [revocable\_cat\_maker.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/default_puzzles/revocable_cat_maker.rue) | [revocable\_cat\_maker.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/default_puzzles/revocable_cat_maker.clsp) | CAT maker for CATs with a revocation layer |

## CATalog Actions

| Puzzle          | Rue                                                                                                                         | CLSP                                                                                                                      | Purpose                                          |
| --------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
| Register        | [register.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/actions/catalog/register.rue)               | [register.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/actions/catalog/register.clsp)               | Insert TAIL into sorted list, mint NFT           |
| Refund          | [refund.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/actions/catalog/refund.rue)                   | [refund.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/actions/catalog/refund.clsp)                   | Return funds from (some) invalid precommit coins |
| Shared types    | [shared.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/actions/catalog/shared.rue)                   | n/a                                                                                                                       | `CatalogState`, `CatalogStateTruth`              |
| Delegated state | [delegated\_state.rue](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/actions/shared/delegated_state.rue) | [delegated\_state.clsp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/actions/shared/delegated_state.clsp) | Apply price singleton state update               |


# CLI Reference

CATalog CLI reference for slot-machine

The [slot-machine](https://github.com/Yakuhito/slot-machine) binary provides the CATalog CLI. Run it as `slot-machine catalog <subcommand>` or `cargo run -- catalog <subcommand>` from a local checkout.

## Prerequisites

* **Rust 1.95.0+**
* [**Sage wallet**](https://github.com/xch-dev/sage) running locally

See [slot-machine LICENSE.md](https://github.com/Yakuhito/slot-machine/blob/master/LICENSE.md) for licensing terms.

## Shared flags

Most transaction commands accept:

| Flag          | Default  | Description                      |
| ------------- | -------- | -------------------------------- |
| `--testnet11` | `false`  | Use testnet11 instead of mainnet |
| `--fee`       | `0.0025` | Transaction fee in XCH           |

## Commands

### `catalog initiate-launch`

Start a new CATalog deployment. Creates the registry launcher and price singleton (state scheduler + medieval vault multisig).

| Flag          | Required | Description                                                      |
| ------------- | -------- | ---------------------------------------------------------------- |
| `--pubkeys`   | yes      | Comma-separated hex BLS pubkeys for the price singleton multisig |
| `-m`          | yes      | Signature threshold (m-of-n)                                     |
| `--testnet11` | no       | Use testnet11                                                    |
| `--fee`       | no       | Launch fee in XCH                                                |

### `catalog continue-launch`

Continues/Finishes launch by deploying premine precommit coins.

| Flag                 | Required | Description                                  |
| -------------------- | -------- | -------------------------------------------- |
| `--payment-asset-id` | yes      | Payment CAT tail hash from launch initiation |
| `--cats-per-spend`   | yes      | Number of premine CATs to deploy per spend   |
| `--testnet11`        | no       | Use testnet11                                |
| `--fee`              | no       | Fee in XCH                                   |

### `catalog unroll-state-scheduler`

Commit the price schedule to the state scheduler singleton.

| Flag          | Required | Description   |
| ------------- | -------- | ------------- |
| `--testnet11` | no       | Use testnet11 |
| `--fee`       | no       | Fee in XCH    |

### `catalog verify-deployment`

Audit the built-in deployment against trusted CSVs (`catalog_premine_mainnet.csv`, `catalog_price_schedule_mainnet.csv`).

| Flag          | Required | Description                                    |
| ------------- | -------- | ---------------------------------------------- |
| `--testnet11` | no       | Verify testnet11 deployment instead of mainnet |

### `catalog register`

Register a new CAT, or refund an invalid precommit with `--refund`.

**Registration flow:**

1. CLI creates a precommit CAT coin (phase 1)
2. User waits `relative_block_height` blocks
3. CLI runs register action (phase 2)

| Flag                   | Required | Description                                                |
| ---------------------- | -------- | ---------------------------------------------------------- |
| `--tail-reveal`        | yes      | TAIL program reveal (hex CLVM)                             |
| `--ticker`             | yes      | CAT ticker (e.g. `SBX`)                                    |
| `--name`               | yes      | CAT display name                                           |
| `--image-uris`         | yes      | Comma-separated image URIs                                 |
| `--image-hash`         | yes      | Image content hash (hex)                                   |
| `--payment-asset-id`   | yes      | Payment CAT tail hash                                      |
| `--description`        | no       | On-chain description (default `""`)                        |
| `--precision`          | no       | CAT precision (default `3`)                                |
| `--metadata-uris`      | no       | Metadata URIs (default `""`)                               |
| `--metadata-hash`      | no       | Metadata hash                                              |
| `--license-uris`       | no       | License URIs (default `""`)                                |
| `--license-hash`       | no       | License hash                                               |
| `--recipient`          | no       | NFT recipient address (defaults to first Sage derivation)  |
| `--payment-cat-amount` | no       | Override payment amount (mainly for refunds)               |
| `--refund`             | no       | Run refund action instead of register                      |
| `--local`              | no       | Sync from chain into local `data.db` instead of hosted API |
| `--log`                | no       | Write spend bundle to `sb.debug`                           |
| `--testnet11`          | no       | Use testnet11                                              |
| `--fee`                | no       | Fee in XCH                                                 |

### `catalog listen`

Long-running process: syncs CATalog from chain via Coinset WebSocket into SQLite `data.db`, and serves a local neighbors API on port **3000**.

| Endpoint                        | Description                                |
| ------------------------------- | ------------------------------------------ |
| `GET /`                         | Health check                               |
| `GET /neighbors?asset_id=<hex>` | Left/right neighbor slots for registration |

Use with `catalog register --local` on testnet (testnet API currently points to `http://localhost:3000`).

| Flag          | Required | Description   |
| ------------- | -------- | ------------- |
| `--testnet11` | no       | Use testnet11 |

### `catalog sign-state-update`

Sign a registry state update (fee token or amount change) as a multisig member.

| Flag                         | Required | Description                                  |
| ---------------------------- | -------- | -------------------------------------------- |
| `--new-payment-asset-id`     | yes      | New payment CAT tail hash                    |
| `--new-payment-asset-amount` | yes      | New registration price (CAT mojos)           |
| `--my-pubkey`                | yes      | Your BLS pubkey (hex)                        |
| `--launcher-id`              | yes      | Price singleton (medieval vault) launcher id |
| `--testnet11`                | no       | Use testnet11                                |
| `--debug`                    | no       | Debug signing (prompts for secret key)       |

### `catalog broadcast-state-update`

Broadcast a signed state update with collected multisig signatures.

| Flag                         | Required | Description                    |
| ---------------------------- | -------- | ------------------------------ |
| `--new-payment-asset-id`     | yes      | New payment CAT tail hash      |
| `--new-payment-asset-amount` | yes      | New registration price         |
| `--sigs`                     | yes      | Comma-separated BLS signatures |
| `--launcher-id`              | yes      | Price singleton launcher id    |
| `--testnet11`                | no       | Use testnet11                  |
| `--fee`                      | no       | Fee in XCH                     |

## Examples

```bash
# Verify mainnet deployment
cargo r catalog verify-deployment

# Register a CAT on testnet11 (requires Sage)
cargo r catalog register \
  --testnet11 \
  --tail-reveal ff... \
  --ticker SBX \
  --name Spacebucks \
  --image-uris https://example.com/sbx.png \
  --image-hash abc123... \
  --payment-asset-id def456...

# Refund an invalid precommit
cargo r catalog register --refund --testnet11 ...

# Run local sync + API
cargo r catalog listen --testnet11
```


# CATalog

CATalog is, at a high level, a registry that allows non-expiring registrations of data hashes. Each registration creates an NFT representing the registration. By social convention, TAILs are the data being registered, and the returned NFTs are used to store and update CAT details such as name or image.

The CATalog registry's state is `(cat_maker_puzzle_hash . registration_price)`, where the first argument is the CAT maker for the payment CAT, while the second variable holds the amount of the CAT to be paid for a registration (in CAT mojos). The current on-chain values should always be read from registry state rather than assumed from documentation.

### Registry Constants

Launch-time constants are stored in [`CatalogRegistryConstants`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/primitives/action_layer/catalog_registry_info.rs):

| Field                          | Purpose                                             |
| ------------------------------ | --------------------------------------------------- |
| `launcher_id`                  | CATalog registry singleton launcher id              |
| `royalty_address`              | NFT royalty puzzle hash                             |
| `royalty_basis_points`         | NFT trade royalty (basis points)                    |
| `precommit_payout_puzzle_hash` | Where precommit funds go on successful registration |
| `relative_block_height`        | Blocks before a precommit coin is spendable         |
| `price_singleton_launcher_id`  | State scheduler / price singleton launcher id       |

### NFT Registratio

For each registration, the registry asserts an NFT was minted through:

1. **Uniqueness prelauncher** - 0-amount coin whose puzzle commits to the TAIL hash ([`uniqueness_prelauncher.rue`](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/nft/uniqueness_prelauncher.rue))
2. **Launcher coin** - standard singleton launcher
3. **Eve NFT** - asserted to be spent at launch

Uniqueness prelaunchers allow APIs and other dApps to efficiently verify that an NFT genuinely represents a given TAIL hash.

### Announcements and SDK

Generally, announcements and messages from the main registry are sent using a one-byte prefix followed by a hash of the actual message contents. To prevent collisions, the prefixes and message structures are defined in [`catalog_registry_prefix.rs`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/primitives/action_layer/catalog_registry_prefix.rs).

The high-level driver primitive is [`CatalogRegistry`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/primitives/action_layer/catalog_registry.rs).

*Written by* [*yakuhito*](https://x.com/yakuhito) *from* [*FireAcademy.io*](https://fireacademy.io/) *on Feb 11th, 2025.*


# The Register Action

The whole point of CATalog

*Note*: The register action code can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/actions/catalog/register.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/actions/catalog/register.clsp)). The SDK driver is [`CatalogRegisterAction`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/layers/action_layer/actions/catalog/register.rs).

The register action, as the name suggests, registers a new CATalog entry. This requires a precommitment coin that contains the TAIL hash, the initial NFT owner puzzle hash, as well as refund-related information (CAT maker and CAT maker solution) to be created before.

When the action is run, it:

* Asserts sorted order: `left_tail_hash < tail_hash < right_tail_hash`
* Spends the left and right neighbor slots to assert that they exist and creates three new slots so the new structure is: left neighbor <-> new slot <-> right neighbor
* Creates a uniqueness prelauncher for the CAT NFT, whose puzzle contains the TAIL hash. The prelauncher puzzle hash and parent can be used in the future to assert that a given NFT represents a particular CAT asset id (TAIL hash/CATalog entry).
* Mints the eve NFT via `secure_nft(NFT_PACK, initial_nft_owner_ph, launcher_id)` with royalty parameters from registry constants
* Consumes the precommitment coin via `SendMessage` with message `0x01` (register/payout mode)
* Asserts the CAT NFT was correctly created (i.e., it was created to the specified owner)

The action also creates a puzzle announcement. This announcement can be asserted to ensure the dApp singleton is running the right action with the right parameters. For the register action, the announcement prefix, `'r'`, is concatenated to `(sha256tree (tail_hash . initial_nft_owner_ph))`. Prefix helpers are in [`CatalogRegistryCreatedAnnouncementPrefix`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/primitives/action_layer/catalog_registry_prefix.rs).

The CLI exposes registration via `catalog register` (see [CLI Reference](/technical-manual/cli)).

*Written by* [*yakuhito*](https://x.com/yakuhito) *from* [*FireAcademy.io*](https://fireacademy.io/) *on Feb 14th, 2025.*


# The Refund Action

Something went wrong - but worry not, you can get your funds back.

*Note*: The refund action code can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/rue-puzzles/actions/catalog/refund.rue) ([Chialisp](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/actions/catalog/refund.clsp)). The SDK driver is [`CatalogRefundAction`](https://github.com/xch-dev/chia-wallet-sdk/blob/main/crates/chia-sdk-driver/src/layers/action_layer/actions/catalog/refund.rs).

Registering an entry in CATalog requires two transactions. First, a user must create a precommitment coin. Second, after waiting for `relative_block_height` blocks (typically 32 on mainnet), the user needs to interact with the registry, which checks (spends) their precommitment coin.

During the period between the two actions, it is possible that the precommitment becomes invalid. This would happen if:

* The CAT maker puzzle of the registry changes (e.g., the price singleton updated the payment CAT to a different asset, so the precommitment coin is now in the wrong CAT)
* The precommitment amount is wrong (e.g., the price singleton changed the registration price)
* The TAIL hash has been registered

When refunding, the action sends `SendMessage` with message `0` (refund mode) to return the precommit CAT to the refund puzzle hash.

If the TAIL is already registered but the precommit is otherwise invalid, the refund action also spends and recreates the existing slot with `counter + 1`.

The action also creates a puzzle announcement. This announcement can be asserted to ensure the dApp singleton is running the right action with the right parameters. For the refund action, the announcement prefix, `'$'`, is concatenated to `(sha256tree (tail_hash . initial_nft_owner_ph))`.

The CLI exposes refunds via `catalog register --refund` (same command as registration, different code path). See [CLI Reference](/technical-manual/cli).

*Written by* [*yakuhito*](https://x.com/yakuhito) *from* [*FireAcademy.io*](https://fireacademy.io/) *on Feb 14th, 2025.*


