Skip to main content

CATs

Chia Asset Tokens are fungible tokens that are issued on the Chia blockchain. The CAT puzzle ensures that the supply of a specific CAT never changes unless the rules of issuance specific to that CAT are followed. These are enforced using a separate Chialisp program called the Token and Asset Issuance Limitations (TAIL).

Aside from the TAIL, there is also an inner puzzle that the CAT wraps around. The inner puzzle controls the ownership of the specific coin, and when the coin is spent, the new puzzle is wrapped in the CAT again. Typically, you wrap the standard transaction so that you can send CATs to Chia wallet addresses.

note

The first version, known as CAT1, reached its end of life at block 2,311,760 on July 26th, 2022.

It has been replaced with the current CAT2 standard because of a vulnerability discovered during a security audit.

To learn more about the terminology used on this page, check out the blog entry explaining CAT naming conventions.

Additionally, fungible tokens can be split apart and merged together, whereas non-fungible tokens are indivisible.

Code Examples

chia-blockchain

The official Chia wallet has a reference implementation for the following in Python:

chia-rs

The wallet code used by the MonsterSprouts example game has the following reference methods:

chia-wallet-lib

The Chia wallet library NPM package has the following reference implementation:

CAT Code

This is the source code of the CAT, which can also be found in the chia-blockchain repository in the puzzle cat_v2.clvm:

Expand CAT2 puzzle
cat_v2.clvm
; Coins locked with this puzzle are spendable cats.
;
; Choose a list of n inputs (n>=1), I_1, ... I_n with amounts A_1, ... A_n.
;
; We put them in a ring, so "previous" and "next" have intuitive k-1 and k+1 semantics,
; wrapping so {n} and 0 are the same, ie. all indices are mod n.
;
; Each coin creates 0 or more coins with total output value O_k.
; Let D_k = the "debt" O_k - A_k contribution of coin I_k, ie. how much debt this input accumulates.
; Some coins may spend more than they contribute and some may spend less, ie. D_k need
; not be zero. That's okay. It's enough for the total of all D_k in the ring to be 0.
;
; A coin can calculate its own D_k since it can verify A_k (it's hashed into the coin id)
; and it can sum up `CREATE_COIN` conditions for O_k.
;
; Defines a "subtotal of debts" S_k for each coin as follows:
;
; S_1 = 0
; S_k = S_{k-1} + D_{k-1}
;
; Here's the main trick that shows the ring sums to 0.
; You can prove by induction that S_{k+1} = D_1 + D_2 + ... + D_k.
; But it's a ring, so S_{n+1} is also S_1, which is 0. So D_1 + D_2 + ... + D_k = 0.
; So the total debts must be 0, ie. no coins are created or destroyed.
;
; Each coin's solution includes I_{k-1}, I_k, and I_{k+1} along with proofs that I_{k}, and I_{k+1} are CATs of the same type.
; Each coin's solution includes S_{k-1}. It calculates D_k = O_k - A_k, and then S_k = S_{k-1} + D_{k-1}
;
; Announcements are used to ensure that each S_k follows the pattern is valid.
; Announcements automatically commit to their own coin id.
; Coin I_k creates an announcement that further commits to I_{k-1} and S_{k-1}.
;
; Coin I_k gets a proof that I_{k+1} is a cat, so it knows it must also create an announcement
; when spent. It checks that I_{k+1} creates an announcement committing to I_k and S_k.
;
; So S_{k+1} is correct iff S_k is correct.
;
; Coins also receive proofs that their neighbours are CATs, ensuring the announcements aren't forgeries.
; Inner puzzles and the CAT layer prepend `CREATE_COIN_ANNOUNCEMENT` with different prefixes to avoid forgeries.
; Ring announcements use 0xcb, and inner puzzles are given 0xca
;
; In summary, I_k generates a coin_announcement Y_k ("Y" for "yell") as follows:
;
; Y_k: hash of I_k (automatically), I_{k-1}, S_k
;
; Each coin creates an assert_coin_announcement to ensure that the next coin's announcement is as expected:
; Y_{k+1} : hash of I_{k+1}, I_k, S_{k+1}
;
; TLDR:
; I_k : coins
; A_k : amount coin k contributes
; O_k : amount coin k spend
; D_k : difference/delta that coin k incurs (A - O)
; S_k : subtotal of debts D_1 + D_2 ... + D_k
; Y_k : announcements created by coin k commiting to I_{k-1}, I_k, S_k
;
; All conditions go through a "transformer" that looks for CREATE_COIN conditions
; generated by the inner solution, and wraps the puzzle hash ensuring the output is a cat.
;
; Three output conditions are prepended to the list of conditions for each I_k:
; (ASSERT_MY_ID I_k) to ensure that the passed in value for I_k is correct
; (CREATE_COIN_ANNOUNCEMENT I_{k-1} S_k) to create this coin's announcement
; (ASSERT_COIN_ANNOUNCEMENT hashed_announcement(Y_{k+1})) to ensure the next coin really is next and
; the relative values of S_k and S_{k+1} are correct
;
; This is all we need to do to ensure cats exactly balance in the inputs and outputs.
;
; Proof:
; Consider n, k, I_k values, O_k values, S_k and A_k as above.
; For the (CREATE_COIN_ANNOUNCEMENT Y_{k+1}) (created by the next coin)
; and (ASSERT_COIN_ANNOUNCEMENT hashed(Y_{k+1})) to match,
; we see that I_k can ensure that is has the correct value for S_{k+1}.
;
; By induction, we see that S_{m+1} = sum(i, 1, m) [O_i - A_i] = sum(i, 1, m) O_i - sum(i, 1, m) A_i
; So S_{n+1} = sum(i, 1, n) O_i - sum(i, 1, n) A_i. But S_{n+1} is actually S_1 = 0,
; so thus sum(i, 1, n) O_i = sum (i, 1, n) A_i, ie. output total equals input total.

;; GLOSSARY:
;; MOD_HASH: this code's sha256 tree hash
;; TAIL_PROGRAM_HASH: the program that determines if a coin can mint new cats, burn cats, and check if its lineage is valid if its parent is not a CAT
;; INNER_PUZZLE: an independent puzzle protecting the coins. Solutions to this puzzle are expected to generate `AGG_SIG` conditions and possibly `CREATE_COIN` conditions.
;; ---- items above are curried into the puzzle hash ----
;; inner_puzzle_solution: the solution to the inner puzzle
;; prev_coin_id: the id for the previous coin
;; tail_program_reveal: reveal of TAIL_PROGRAM_HASH required to run the program if desired
;; tail_solution: optional solution passed into tail_program
;; lineage_proof: optional proof that our coin's parent is a CAT
;; this_coin_info: (parent_id puzzle_hash amount)
;; next_coin_proof: (parent_id inner_puzzle_hash amount)
;; prev_subtotal: the subtotal between prev-coin and this-coin
;; extra_delta: an amount that is added to our delta and checked by the TAIL program
;;

(mod (
MOD_HASH ;; curried into puzzle
TAIL_PROGRAM_HASH ;; curried into puzzle
INNER_PUZZLE ;; curried into puzzle
inner_puzzle_solution ;; if invalid, INNER_PUZZLE will fail
lineage_proof ;; This is the parent's coin info, used to check if the parent was a CAT. Optional if using tail_program.
prev_coin_id ;; used in this coin's announcement, prev_coin ASSERT_COIN_ANNOUNCEMENT will fail if wrong
this_coin_info ;; verified with ASSERT_MY_COIN_ID
next_coin_proof ;; used to generate ASSERT_COIN_ANNOUNCEMENT
prev_subtotal ;; included in announcement, prev_coin ASSERT_COIN_ANNOUNCEMENT will fail if wrong
extra_delta ;; this is the "legal discrepancy" between your real delta and what you're announcing your delta is
)

;;;;; start library code

(include condition_codes.clvm)
(include curry-and-treehash.clinc)
(include cat_truths.clib)
(include utility_macros.clib)

(defconstant RING_MORPH_BYTE 0xcb)


; take two lists and merge them into one
(defun merge_list (list_a list_b)
(if list_a
(c (f list_a) (merge_list (r list_a) list_b))
list_b
)
)

; cat_mod_struct = (MOD_HASH MOD_HASH_hash GENESIS_COIN_CHECKER GENESIS_COIN_CHECKER_hash)

(defun-inline mod_hash_from_cat_mod_struct (cat_mod_struct) (f cat_mod_struct))
(defun-inline mod_hash_hash_from_cat_mod_struct (cat_mod_struct) (f (r cat_mod_struct)))
(defun-inline tail_program_hash_from_cat_mod_struct (cat_mod_struct) (f (r (r cat_mod_struct))))

;;;;; end library code

;; return the puzzle hash for a cat with the given `GENESIS_COIN_CHECKER_hash` & `INNER_PUZZLE`
(defun-inline cat_puzzle_hash (cat_mod_struct inner_puzzle_hash)
(puzzle-hash-of-curried-function (mod_hash_from_cat_mod_struct cat_mod_struct)
inner_puzzle_hash
(sha256 ONE (tail_program_hash_from_cat_mod_struct cat_mod_struct))
(mod_hash_hash_from_cat_mod_struct cat_mod_struct)
)
)

;; assert `CREATE_COIN_ANNOUNCEMENT` doesn't contain the RING_MORPH_BYTE bytes so it cannot be used to cheat the coin ring

(defun-inline morph_condition (condition cat_mod_struct)
(if (= (f condition) CREATE_COIN)
(c CREATE_COIN
(c (cat_puzzle_hash cat_mod_struct (f (r condition)))
(r (r condition)))
)
(if (= (f condition) CREATE_COIN_ANNOUNCEMENT)
(assert (not (and
(= 33 (strlen (f (r condition))))
(= (substr (f (r condition)) 0 ONE) RING_MORPH_BYTE) ; lazy eval
))
; then
condition
)
condition
)
)
)

;; given a coin's parent, inner_puzzle and amount, and the cat_mod_struct, calculate the id of the coin
(defun-inline coin_id_for_proof (coin cat_mod_struct)
(calculate_coin_id (f coin) (cat_puzzle_hash cat_mod_struct (f (r coin))) (f (r (r coin))))
)

;; utility to fetch coin amount from coin
(defun-inline input_amount_for_coin (coin)
(f (r (r coin)))
)

;; calculate the hash of an announcement
;; we add 0xcb so ring announcements exist in a different namespace to announcements from inner_puzzles
(defun-inline calculate_annoucement_id (this_coin_id this_subtotal next_coin_id cat_mod_struct)
(sha256 next_coin_id RING_MORPH_BYTE (sha256tree (list this_coin_id this_subtotal)))
)

;; create the `ASSERT_COIN_ANNOUNCEMENT` condition that ensures the next coin's announcement is correct
(defun-inline create_assert_next_announcement_condition (this_coin_id this_subtotal next_coin_id cat_mod_struct)
(list ASSERT_COIN_ANNOUNCEMENT
(calculate_annoucement_id this_coin_id
this_subtotal
next_coin_id
cat_mod_struct
)
)
)

;; here we commit to I_{k-1} and S_k
;; we add 0xcb so ring announcements exist in a different namespace to announcements from inner_puzzles
(defun-inline create_announcement_condition (prev_coin_id prev_subtotal)
(list CREATE_COIN_ANNOUNCEMENT
(concat RING_MORPH_BYTE (sha256tree (list prev_coin_id prev_subtotal)))
)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; this function takes a condition and returns an integer indicating
;; the value of all output coins created with CREATE_COIN. If it's not
;; a CREATE_COIN condition, it returns 0.

(defun-inline output_value_for_condition (condition)
(if (= (f condition) CREATE_COIN)
(f (r (r condition)))
0
)
)

;; add two conditions to the list of morphed conditions:
;; CREATE_COIN_ANNOUNCEMENT for my announcement
;; ASSERT_COIN_ANNOUNCEMENT for the next coin's announcement
(defun-inline generate_final_output_conditions
(
prev_subtotal
this_subtotal
morphed_conditions
prev_coin_id
this_coin_id
next_coin_id
cat_mod_struct
)
(c (create_announcement_condition prev_coin_id prev_subtotal)
(c (create_assert_next_announcement_condition this_coin_id this_subtotal next_coin_id cat_mod_struct)
morphed_conditions)
)
)


;; This next section of code loops through all of the conditions to do three things:
;; 1) Look for a "magic" value of -113 and, if one exists, filter it, and take note of the tail reveal and solution
;; 2) Morph any CREATE_COIN or CREATE_COIN_ANNOUNCEMENT conditions
;; 3) Sum the total output amount of all of the CREATE_COINs that are output by the inner puzzle
;;
;; After everything return a struct in the format (morphed_conditions . (output_sum . tail_reveal_and_solution))
;; If multiple magic conditions are specified, the later one will take precedence

(defun-inline condition_tail_reveal (condition) (f (r (r (r condition)))))
(defun-inline condition_tail_solution (condition) (f (r (r (r (r condition))))))

(defun cons_onto_first_and_add_to_second (morphed_condition output_value struct)
(c (c morphed_condition (f struct)) (c (+ output_value (f (r struct))) (r (r struct))))
)

(defun find_and_strip_tail_info (inner_conditions cat_mod_struct tail_reveal_and_solution)
(if inner_conditions
(if (= (output_value_for_condition (f inner_conditions)) -113) ; Checks this is a CREATE_COIN of value -113
(find_and_strip_tail_info
(r inner_conditions)
cat_mod_struct
(c (condition_tail_reveal (f inner_conditions)) (condition_tail_solution (f inner_conditions)))
)
(cons_onto_first_and_add_to_second
(morph_condition (f inner_conditions) cat_mod_struct)
(output_value_for_condition (f inner_conditions))
(find_and_strip_tail_info
(r inner_conditions)
cat_mod_struct
tail_reveal_and_solution
)
)
)
(c () (c 0 tail_reveal_and_solution))
)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;; lineage checking

;; return true iff parent of `this_coin_info` is provably a cat
;; A 'lineage proof' consists of (parent_parent_id parent_INNER_puzzle_hash parent_amount)
;; We use this information to construct a coin who's puzzle has been wrapped in this MOD and verify that,
;; once wrapped, it matches our parent coin's ID.
(defun-inline is_parent_cat (
cat_mod_struct
parent_id
lineage_proof
)
(= parent_id
(calculate_coin_id (f lineage_proof)
(cat_puzzle_hash cat_mod_struct (f (r lineage_proof)))
(f (r (r lineage_proof)))
)
)
)

(defun check_lineage_or_run_tail_program
(
this_coin_info
tail_reveal_and_solution
parent_is_cat ; flag which says whether or not the parent CAT check ran and passed
lineage_proof
Truths
extra_delta
inner_conditions
)
(if tail_reveal_and_solution
(assert (= (sha256tree (f tail_reveal_and_solution)) (cat_tail_program_hash_truth Truths))
(merge_list
(a (f tail_reveal_and_solution)
(list
Truths
parent_is_cat
lineage_proof ; Lineage proof is only guaranteed to be true if parent_is_cat
extra_delta
inner_conditions
(r tail_reveal_and_solution)
)
)
inner_conditions
)
)
(assert parent_is_cat (not extra_delta)
inner_conditions
)
)
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun stager_two (
Truths
(inner_conditions . (output_sum . tail_reveal_and_solution))
lineage_proof
prev_coin_id
this_coin_info
next_coin_id
prev_subtotal
extra_delta
)
(check_lineage_or_run_tail_program
this_coin_info
tail_reveal_and_solution
(if lineage_proof (is_parent_cat (cat_struct_truth Truths) (my_parent_cat_truth Truths) lineage_proof) ())
lineage_proof
Truths
extra_delta
(generate_final_output_conditions
prev_subtotal
; the expression on the next line calculates `this_subtotal` by adding the delta to `prev_subtotal`
(+ prev_subtotal (- (input_amount_for_coin this_coin_info) output_sum) extra_delta)
inner_conditions
prev_coin_id
(my_id_cat_truth Truths)
next_coin_id
(cat_struct_truth Truths)
)
)
)

; CAT TRUTHS struct is: ; CAT Truths is: ((Inner puzzle hash . (MOD hash . (MOD hash hash . TAIL hash))) . (my_id . (my_parent_info my_puzhash my_amount)))
; create truths - this_coin_info verified true because we calculated my ID from it!
; lineage proof is verified later by cat parent check or tail_program

(defun stager (
cat_mod_struct
inner_conditions
lineage_proof
inner_puzzle_hash
my_id
prev_coin_id
this_coin_info
next_coin_proof
prev_subtotal
extra_delta
)
(c (list ASSERT_MY_COIN_ID my_id) (stager_two
(cat_truth_data_to_truth_struct
inner_puzzle_hash
cat_mod_struct
my_id
this_coin_info
)
(find_and_strip_tail_info inner_conditions cat_mod_struct ())
lineage_proof
prev_coin_id
this_coin_info
(coin_id_for_proof next_coin_proof cat_mod_struct)
prev_subtotal
extra_delta
))
)

(stager
;; calculate cat_mod_struct, inner_puzzle_hash, coin_id
(list MOD_HASH (sha256 ONE MOD_HASH) TAIL_PROGRAM_HASH)
(a INNER_PUZZLE inner_puzzle_solution)
lineage_proof
(sha256tree INNER_PUZZLE)
(calculate_coin_id (f this_coin_info) (f (r this_coin_info)) (f (r (r this_coin_info))))
prev_coin_id ; ID
this_coin_info ; (parent_id puzzle_hash amount)
next_coin_proof ; (parent_id innerpuzhash amount)
prev_subtotal
extra_delta
)
)

Additionally, there are a few standard TAIL puzzles.

Single-Issuance TAIL

The single-issuance TAIL prevents melting and requires the parent to be a specific coin. This is currently the default way to issue CATs, since it ensures the supply will never increase.

This is the source code, which can also be found in the chia-blockchain repository in the puzzle genesis_by_coin_id.clvm:

Expand Genesis By Coin ID puzzle
genesis_by_coin_id.clvm
; This is a TAIL for use with cat.clvm.
;
; This checker allows new CATs to be created if they have a particular coin id as parent
;
; The genesis_id is curried in, making this lineage_check program unique and giving the CAT it's uniqueness
(mod (
GENESIS_ID
Truths
parent_is_cat
lineage_proof
delta
inner_conditions
_
)

(include cat_truths.clib)

(if delta
(x)
(if (= (my_parent_cat_truth Truths) GENESIS_ID)
()
(x)
)
)

)

Multi-Issuance TAIL

The multi-issuance TAIL allows any action to be taken, providing the original issuance key is used. Each spend that makes supply changes must be signed separately using this same key.

This is the source code, which can also be found in the chia-blockchain repository in the puzzle everything_with_signature.clvm:

Expand Everything With Signature puzzle
everything_with_signature.clvm
; This is a "limitations_program" for use with cat.clvm.
(mod (
PUBKEY
Truths
parent_is_cat
lineage_proof
delta
inner_conditions
_
)

(include condition_codes.clvm)

(list (list AGG_SIG_ME PUBKEY delta)) ; Careful with a delta of zero, the bytecode is 80 not 00
)

This TAIL provides a balance between security and flexibility. It's similar to the previous TAIL, but the signature can be reused for the puzzle it's signed with, allowing the TAIL to change over time. The creator can publish the signature, allowing any owner to run the TAIL on their CAT, but any permissions granted can never be revoked.

This is the source code, which can also be found in the chia-blockchain repository in the puzzle delegated_tail.clvm:

Expand Delegated TAIL puzzle
delegated_tail.clvm
; This is a "limitations_program" for use with cat.clvm.
(mod (
PUBKEY
Truths
parent_is_cat
lineage_proof
delta
inner_conditions
(
delegated_puzzle
delegated_solution
)
)

(include condition_codes.clvm)

(defun sha256tree1 (TREE)
(if (l TREE)
(sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE)))
(sha256 1 TREE)))

(c (list AGG_SIG_UNSAFE PUBKEY (sha256tree1 delegated_puzzle))
(a delegated_puzzle (c Truths (c parent_is_cat (c lineage_proof (c delta (c inner_conditions delegated_solution))))))
)
)

Usage

CATs are designed in a way that makes them unusable as regular XCH in spends. However, it is usually possible to melt them back into XCH later. Tokens are often used as a form of credits, such as casino chips or store credit.

TAILs can be used to accommodate issuance requirements, such as:

UsageDescription
StablecoinsCreator can issue new tokens which are backed by attested funds.
Limited SupplyCreator can run a one-time issuance, and no more can ever be issued.
Asset RedemptionCreator can allow owners to melt into XCH by following specific rules.

In all of these cases, the TAIL program is run when a coin is spent to check if the issuance is valid.

Design Decisions

Created coins become CATs with the same TAIL

When the inner puzzle returns a CREATE_COIN condition, it will wrap its puzzle hash with the CAT and curry in the same TAIL as before. This prevents the CAT from being implicitly melted and the value used for other things when spending it.

Unless TAIL is revealed, spends must net zero

In order to prevent issuing new CATs or melting existing CATs without the TAIL permitting it, any spend that doesn't reveal the TAIL program must output the same amount as the input coins.

CATs must match their parent or use the TAIL

Another way to prevent CATs from being issued without permission, is that they validate their lineage by asserting the parent is a CAT of the same TAIL. This is done by using the ASSERT_MY_ID condition to verify the information passed in the spend.

CATs enforce prefixes for coin announcements

In order to ensure that the CATs can communicate with each other without interference from an inner puzzle, they must prepend a 0xcb prefix to coin announcements that are created within the CAT layer.

TAILs are given a list of pre-calculated truths

Many TAILs require information that has already been calculated in the CAT layer, so it is bundled together as a pre-validated collection of Truths. These truths are then passed into the TAIL as the first non-curried parameter in the solution. For more info, see the Truth Table.

CATs have the option to use hinting

Hinting is a way to signal the CAT's type to a Chia wallet. The hint is typically an inner puzzle hash. For more info, see our explanation of hinting in the FAQ.

Spend Accounting

Each CAT coin has a truthful calculation of the difference between its amount and its output amount, called its delta.

The CAT coins also are given the sum of every other coin's deltas, which must be zero. In order to enforce this zero-delta rule, each CAT coin is assigned a next and previous neighbor, which ultimately forms a ring.

The coins use coin announcements to communicate with their neighbors. For this use case, the announcements must contain two pieces of information:

  • The ID of the coin that created this coin. This is already implicitly contained in the coin announcement.
  • The intended recipient's Coin ID. The announcement's message must contain this information in order to prevent attacks where coins can receive messages that weren't intended for them.

To form the ring, every coin outputs the following conditions:

(
(CREATE_COIN_ANNOUNCEMENT (concat previous_coin_ID sum_of_deltas_before_me))
(ASSERT_COIN_ANNOUNCEMENT (sha256 next_coin_id my_coin_id sum_of_deltas_including_me))
)

The announcement assertions take the hash of the announcement creator's ID and message.

In order to create next_coin_id, the next coin's inner puzzle is wrapped with the current coin's CAT information. This guarantees that next_coin_id is a CAT of the same type as the current coin.

Because both coins follow the same CAT mod puzzle, they must comply with the same set of truths. This, in turn, guarantees that the whole ring is telling the truth. As long as the ring is connected, the total delta must be zero.

Extra Delta

There are two exceptions to the aforementioned zero-delta rule:

  • Issuing coins (creating CATs from XCH)
  • Melting coins (turning CATs back into XCH)

To account for these cases, the TAIL program may approve a misreporting of a CAT coin's Delta by a specific amount, called the extra delta. This is one of the parameters passed to the TAIL program.

There are a few rules to ensure that extra coins are not issued or melted:

  • If the extra delta is anything other than 0, the TAIL program is forced to run. It must evaluate whether to permit the extra delta, or fail with an (x) call.
  • If the extra delta value in the solution does not cause the TAIL program to fail, then it is automatically added to the reported delta, which is used in the announcement ring.
  • If the TAIL program is not revealed and the extra delta is not 0, then the spend will fail.

TAIL Program

The TAIL program is powerful and flexible. It has control over, and access to, many things. This gives a programmer a lot of control, but also a great deal of responsibility.

danger

If the TAIL is programmed incorrectly, the tokens may be issued by attackers, rendering the asset worthless.

A TAIL should follow all of the conventional rules of security that any Chialisp program responsible for locking up money should follow.

Several parameters must be passed to a TAIL's solution:

ParameterDescription
TruthsThese are bundled together, as explained in the truths section.
parent_is_catWhether the parent CAT has been validated as the same type.
lineage_proof (optional)Proof that the parent is a CAT of the same type as this CAT.
deltaThe extra delta value, as explained here.
inner_conditionsThe conditions returned by the inner puzzle (see below).
tail_solution (optional)A list of opaque parameters passed to the TAIL.

Although the TAIL is powerful, it is not necessarily run every time the coin is spent. The TAIL is run if a magic condition is created in the inner puzzle. This magic condition is required to prevent people who can spend the TAIL from intercepting the spend and changing it against the spender's will. The magic condition that triggers the TAIL to be run must look like this:

(51 0 -113 <TAIL puzzle> <TAIL solution>), where:

  • 51 is the condition code for CREATE_COIN.
  • 0 can be replaced with anything, but this is the smallest possible value. Although the CREATE_COIN condition usually has a puzzle hash, it is ignored here.
  • -113 is the magic amount, which signals to the CAT layer that the TAIL must be run. A negative number was chosen because negative coins are usually invalid, but otherwise it is an arbitrary amount.

The TAIL should check diligently for and authorize or reject the following things:

  • Is the extra delta issuing or melting any coins?
  • Is this coin's parent not a CAT of the same type?

Limitations of the TAIL

In Ethereum, a token's issuer might have the ability to freeze or confiscate funds without the owner's permission. This is not possible in Chia.

In Chia, an issuer creates a TAIL, which lives inside all CATs of the same type, including those that have already been distributed. However, the issuer does not have the power to spend coins they do not own. A TAIL can only run as the last step in a CAT's spend, and the owner of the CAT (not the issuer) is responsible for providing its solution. This means that only the owner can run the TAIL. Therefore, the CAT's owner is the only one with the ability to complete the spend.

This decision adds some decentralization for users. It also adds some complexity. The importance of creating a well-constructed TAIL cannot be emphasized enough. Once a CAT has been distributed, it is no longer possible to change the TAIL across the entire token supply. The TAIL is locked into the same set of rules forever. To change the TAIL would be tantamount to replacing physical cash in circulation. An exchange for new tokens would have to be offered, and the old token eventually deprecated, even if some people still carry it.

It also means that if the set of rules is compromised, people may be able to issue or melt CATs maliciously. There’s no easy way to patch the TAIL, other than through the process above, and therefore it is best avoided.

Valuation

Some decisions regarding the granularity and denomination of CATs versus XCH:

  • Most Chia wallets choose to display their value in XCH. However, this is a purely cosmetic choice because Chia's blockchain only knows about mojos. One XCH is equal to one trillion (1,000,000,000,000) mojos.
  • In a similar vein, Chia Network has made the design decision to map 1 CAT to 1,000 XCH mojos. This ratio will be the same for all CATs.
  • Theoretically, it would be possible to set the CAT:mojo ratio to something other than 1:1000 for a specific CAT, but we strongly recommend against doing this. The official Chia wallet will not support CATs with a ratio other than 1:1000. Additionally, if you created your own wallet with support for different ratios, users of this wallet would almost certainly be confused and accidentally spend too much or too little money, by multiple orders of magnitude. Please don't attempt this.
  • The melt value of a single token is 1,000 mojos. This remains true regardless of the token's face value or its circulating supply.
  • A token's face value is based on supply and demand, rather than being matched to the melt value.

By analogy, on multiple occasions the US Treasury has floated the idea of issuing a $1 trillion coin made from platinum. Leaving aside the practical implications of doing this, the magnitude of the difference between this hypothetical coin's face value and melt value would be similar to that of CATs and XCH. The coin would be worth $1 trillion dollars, but if someone melted it and sold the platinum, they'd only receive a minuscule fraction of that amount.

On the other end of the spectrum, consider the US penny. Its base metals (97.5% zinc and 2.5% copper) are worth around two cents. So in theory, if you could melt a penny into zinc and copper while minimizing your costs, you could sell these metals for a sizable profit.

Reasons for Melting

It's important to keep in mind that a CAT's TAIL (and nothing else) decides the rules for melting, if it allows melting at all. For example, our single-issuance TAIL only works with a specific coin, so it does not allow melting. CATs that use this TAIL can never be melted, no matter how small their face value.

Our delegated TAIL leaves it entirely up to the CAT's creator how and when melting can happen. There are many possible reasons you may want to melt a CAT, other than the obvious example of its face value being less than the melt value.

Beyond our pre-packaged examples, TAILs with a wide range of functionality are also possible. To illustrate some of this functionality, let's consider three potential reasons for melting, along with who is permitted to melt the tokens.

Removal From Circulation

For certain categories of CATs, such as stablecoins and redemption tokens, melting will be allowed only by the creator, and only if they own the tokens. In the case of a stablecoin, the creator may need to remove some tokens from circulation if backing funds are reduced. For redemption tokens, the owner may exchange a token with the creator for something of value. The token no longer has any face value, so its creator will remove it from circulation.

Value Exchange

Some CATs might allow their owners to melt tokens in order to gain something else of value, for example NFTs or other CATs. In fact, an entire marketplace could emerge from this concept.

Here are some possible examples of this:

  • The creator of a set of NFTs also creates a small issuance of golden tickets that can be used to pick out any individual NFT before the set is made publicly available.
  • A celebrity issues some tokens that can be exchanged for something of non-monetary value, such as a meeting with said celebrity.
  • The holder of a CAT must submit a proof of melt in order to enter a contest.

Ephemeral Tokens

A CAT could be created as a limited-time offer or as a game of musical chairs. In these cases, tokens would be melted against the owner's will. This could be done either at random or as a deliberate type of slashing.

Truth Table

The following is a list of truths that are passed into the TAIL:

TruthDescription
My IDThe ID of the coin.
My Parent's IDThe ID of the coin's parent.
My Full Puzzle HashThe puzzle hash contained inside the coin.
My AmountThe amount of the coin in mojos.
My Inner Puzzle HashThe puzzle hash of the inner puzzle inside the CAT layer.
My Lineage Proof (optional)Proof that the CAT's parent has the same TAIL.
My TAIL Solution (optional)A list of parameters passed into the TAIL program.
My Coin InfoThe parent, puzzle hash, and amount.
CAT Mod HashThe hash of the CAT before anything is curried.
CAT Mod Hash HashThe hash of the CAT mod hash.
CAT TAIL Program HashThe hash of the TAIL program that was curried into the CAT.

Conclusion

CATs are a great way to create tokens that represent divisible assets or currencies on top of the Chia blockchain. They can be used to represent stablecoins, tickets, store credit, and many other things.