Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions CHIPs/chip-0050.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
CHIP Number | 0050
:-------------|:----
Title | Action Layer and Slots
Description | A singleton inner layer that handles the core needs of complex dApps: efficient multi-action calls in a single spend and persistent state.
Author | [yakuhito](https://github.com/Yakuhito)
Editor | [Dan Perry](https://github.com/danieljperry)
Comments-URI | [CHIPs repo, PR #165](https://github.com/Chia-Network/chips/pull/165)
Status | Review
Category | Informational
Sub-Category | Chialisp
Created | 2025-07-22
Requires | -
Replaces | -
Superseded-By | -

## Abstract
This CHIP proposes a singleton inner layer that handles the common needs of Chia decentralized applications. The combination of puzzles allows singletons to efficiently support more than one ‘action’ (i.e., functionality), much like Solidity contracts have multiple methods that can be called. Moreover, multiple actions can be efficiently called within the same spend, drastically reducing total spend bundle cost. Each action call has access to spend-specific context via the ephemeral state (e.g., am I the first action to run?), as well as long-term context via the persistent/normal dApp state (e.g., what’s the current registration price for an item?). Finalizers are customizable components that can be defined to control the logic the singleton runs at the end of every spend. The default finalizer simply recreates the singleton with a new state, while the reserve finalizer also manages a CAT or XCH reserve owned by the dApp. This proposal also includes slots, another form of persistent storage that leverages the coinset model.

## Motivation
A set of core properties (behavior) can be observed when analyzing most blockchain-powered decentralized applications. Namely:
* **Multiple capabilities**: A dApp may have multiple distinct functionalities. Users interacting with it may run one or more of these functionalities. For example, a user may add/remove liquidity from a TibetSwap pair or swap one asset for the other.
* **Multiple interactions from many parties**: As a shared resource, the dApp is expected to support multiple interactions from different parties, sometimes in the same block. A dApp should do this efficiently (i.e., in a way that minimizes transaction cost as much as practically possible), and, ideally, in a way that works well with the mempool’s current Replace-by-Fee rules.
* **State**: Most dApps have state variables, which are persistent across spends. These variables may be simple values (integers, strings, etc.), lists of values, or even mappings.
* **Reserves**: Some dApps may also manage user funds. For example, a TibetSwap pair manages the funds of liquidity providers, which it uses to allow traders to swap one asset for the other.

The action layer and slots were designed as a ‘framework’ that takes care of these needs with minimal code changes across apps. This way, Chialisp developers can focus on designing and optimizing an app’s defining logic without having to worry about the aspects described above.


## Backwards Compatibility
This proposal includes a new set of puzzles intended as a dApp framework. The main goal is to greatly simplify dApp development, but their use is completely optional. Both already-deployed and future apps may opt not to use the puzzles described in this CHIP without them being considered as going against the CHIP.

## Rationale
To support multiple actions, a pattern similar to the p2_1_of_n puzzle was employed. Namely, all puzzles allowed to run are put into a Merkle tree whose corresponding root is kept in the action layer. When an action is run, its puzzle, along with the corresponding Merkle proof, are revealed. For subsequent action runs in the same spends, the previous puzzle reveal is referenced, eliminating the need to store and hash the same puzzle reveal multiple times.

Each action is given two states as truths, as well as a user-provided solution. The ephemeral state starts out as `()` for each new singleton spend, giving actions a way to access spend-specific context (e.g., whether the action is the first to be run in the spend, which is relevant if the action uses relative conditions). The second state is persistent - it is preserved across spends and always constitutes the output of the previous action (except for the state given to the first action in the eve singleton spend). Actions return new states to be passed to the next action (or discarded - like the ephemeral state is for the last action run in a spend), as well as a list of output conditions which will be processed and (by default) passed to the singleton layer.

The action layer’s customizable component is called a finalizer, which includes any logic that needs to run at the end of a spend. The default finalizer is a minimal puzzle that only re-creates the singleton coin with a new state. The reserve finalizer also allows the singleton to manage one reserve whose amount is kept in the persistent state (usually as the first value). Actions in singletons leveraging the reserve finalizer can make the reserve output a given condition by prepending a special value (-42) to it.

In some cases, storing values in the persistent state is not appropriate. For example, lists or mappings may grow to have a large number of entries, each of which is only rarely accessed. Storing such data structures in the state would be unoptimal, as the state is passed through all actions that run and hashed every spend. Slots were created to address this problem by offering a more expensive way of storing data that is not needed during most spends. A singleton may create slots - 0-amount coins holding some data - at any time. The special puzzle allows only the owner singleton to later spend the coin to retrieve the data. Slots can also be used to enforce uniqueness of keys across a list of key-value pairs, as described [here](https://blog.fireacademy.io/p/uniqueness-on-chain) and [here](https://blog.fireacademy.io/p/solving-the-problem-of-uniqueness).

## Specification
The action layer can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/action.clsp). The default finalizer can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/finalizer.clsp), and the reserve finalizer can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/reserve_finalizer.clsp). The slot puzzle can be found [here](https://github.com/Yakuhito/slot-machine/blob/master/puzzles/singleton/slot.clsp). More documentation about the action layer and slots can be found [here](https://docs.catalog.cat/technical-manual/action-layer) and [here](https://docs.catalog.cat/technical-manual/slots).


## Test Cases
A series of tests verifying on-chain apps powered by the action layer can be found in chia-wallet-sdk.

## Reference Implementation
The reference implementation can be found [here](https://github.com/Yakuhito/slot-machine). It includes puzzles for three apps that use the action layer: CATalog, XCHandles, and the Reward Distributor. Apps are in varying states of completion and will have their own subsequent CHIPs. A CLI tool for viewing and interacting with the apps through Coinset.org and the Sage RPC is also included in the repository.

Drivers for the action layer and associated apps, as well as tests, have been integrated into chia-wallet-sdk.


## Security
Puzzle security risk is the main concern when it comes to this standard. Multiple members of the Chia community have reviewed the standardized components of the action layer (‘action.clsp’, the default and reserve finalizers, and the slot puzzle). Chialisp developers implementing actions or custom finalizers should perform full audits on new apps, as the action layer is not intended to act as a security sandbox.

## Copyright
Copyright and rights to this CHIP waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
70 changes: 70 additions & 0 deletions CHIPs/chip-0051.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
CHIP Number | 0051
:-------------|:----
Title | Reward Distributor
Description | Puzzles that enable continuous on-chain reward distribution to a changing set of recipients.
Author | [yakuhito](https://github.com/Yakuhito), [Michael Taylor](https://github.com/MichaelTaylor3D)
Editor | [Dan Perry](https://github.com/danieljperry)
Comments-URI | [CHIPs repo, PR #165](https://github.com/Chia-Network/chips/pull/165)
Status | Review
Category | Informational
Sub-Category | Chialisp
Created | 2025-07-22
Requires | CHIP-0050 (Action Layer and Slots), CHIP-0035 (DataLayer Delegation Capabilities)
Replaces | -
Superseded-By | -

## Abstract
The reward distributor is an on-chain application responsible for continuously distributing incentives to a dynamic list of recipients. It is inspired by the [mechanism](https://uploads-ssl.webflow.com/5ad71ffeb79acc67c8bcdaba/5ad8d1193a40977462982470_scalable-reward-distribution-paper.pdf) frequently used on other chains for liquidity farms. The singleton uses a list of active recipients and their weights to distribute rewards each second. Anyone can commit additional funds to be distributed in the current or future epochs. Commitments for the latter can be partially clawed back (i.e., they’re subject to withdrawal penalties) before the start of the epoch designated during deposit. Control over the list of reward recipients depends on which mode the distributor operates in. In the managed mode, an external party can directly add and remove entries from the list. In the collection NFT mode, the list is built as users stake and unstake NFTs minted by a particular DID, with each NFT having the same weight. In the curated NFT mode, a list of NFTs and their respective weights is tracked via a [CHIP-0035](https://github.com/Chia-Network/chips/blob/main/CHIPs/chip-0035.md) DataLayer store. In the CAT mode, CATs with a particular asset id can be staked to earn a share of the rewards proportional to the staked CAT's amount. **We are making this CHIP to explain the reward distributor’s capabilities with the hope that other projects with similar requirements will be able to use our implementation.**

## Motivation
The reward distributor was first envisioned as a direct result of DIG Network’s need for an on-chain solution that minimizes trust. The DIG reward distribution scheme has three main shareholders:
* **Reward sponsors** provide the funds used to incentivize stores. They want to see that previous funds have been distributed correctly in the past, and ideally get part of their commitments back if the system doesn’t work as expected.
* **Managers (validators)** are responsible for checking which stores are eligible for rewards at a given time, as well as maintaining a publicly accessible list of the stores currently being rewarded. They earn a small portion of the total funds in exchange for allowing the system to distribute the rest correctly.
* **Mirror operators** expect rewards for meeting the eligibility criteria (hosting data and serving it) and a way to ensure those rewards are adequately paid.

The system described above has a centralized component. Namely, a manager is a trusted oracle that publishes off-chain data (a list of entries - mirrors - eligible for rewards) and keeps it up-to-date. This CHIP proposes an on-chain solution to minimize the trust required between the three principal shareholders. It also proposes alternative modes of operation for the reward distributor, where the manager is replaced by code that adds entries to the payout list when users deposit NFTs or CATs and removes them when users wish to unstake their assets.

## Backwards Compatibility
This proposal does not introduce any breaking changes.

## Rationale
As mentioned in [DIP-0002](https://github.com/DIG-Network/DIPS/blob/main/DIPs/dip-0002.md), the following requirements were identified after a series of public and private discussions:
* **XCH and CAT payouts**: Each reward distributor pays incentives in either XCH, the native currency of the Chia blockchain, or in a specific Chia Asset Token (CAT).
* **Continuous rewards/incentives**: An entry’s payout should accurately reflect its presence (and weight) in the reward list in a given epoch. While a transaction is not made every second, reward recipients should have confidence that a deterministic amount of rewards is set aside for them every second. The accrued amount can only be paid out to them, even if the manager starts misbehaving in the future.
* **Anyone can commit incentives for future epochs**: Anyone should be able to sponsor future epochs by committing funds. Commitments can be clawed back to account for the possibility that a manager might go rogue or offline. This clawback incurs a penalty, making manager misconduct costly to all parties: the manager loses reputation and future rewards, while the sponsor loses a part of their commitment.
* **Manager controls the list of active entries**: The manager is the only entity that can add or remove entries from the active entries list. Active entries continuously earn a part of the epoch's total payout. The distribution is weighted according to each entry’s share, which is set by the manager. Alternatively, if the reward distributor operates in the NFT mode, anyone can stake any NFT minted by a predefined DID to add an entry to the list. The user is able to get back their NFT at any time by unstaking it, which also removes their entry from the rewards list.
* **Fee proportional to total payout amount**: The manager provides an essential service and has to cover network fees for transactions that update the reward recipient list. As such, it makes sense to incentivize the manager by reserving a fixed percentage of each epoch’s total rewards. More generally, reward distributors can be set up to send a fee proportional to distributed incentives to a payout puzzle hash specified at launch.
* **Payouts can be manually triggered**: Reward recipients are automatically paid out when they are removed from the active entries list. Payouts can also be manually triggered after the accrued rewards exceed a predefined threshold (minimum amount).

## Specification
The reward distributor is a singleton that uses the action layer and slots, as presented in CHIP-0050. It contains the following actions:
* **sync**: Distributes rewards for the elapsed time since the last sync operation, which occurred in the same epoch.
* **new_epoch**: Transitions to a new epoch at the end of the current one, also transferring the (manager’s) fee.
* **commit_incentives** & **withdraw_incentives** : Used to commit incentives for a future epoch (i.e., sponsor it) and to withdraw a previous commitment. The latter operation carries an associated penalty.
* **add_rewards**: Adds incentives to the current epoch. The amount cannot be clawed back.
* **add_entry** & **remove_entry**: Used by the manager to modify the active entry list if the distributor is in the ‘managed’ mode
* **stake** & **unstake**: Used by users to lock and unlock assets to add or remove an entry in the active rewards list if the distributor is in a non-manager mode. The logic verifying assets should be staked/unstaked is defined in locking/unlocking puzzles, detailed below.
* **inititate_payout**: Initiates a payout of accrued rewards. The payout amount needs to exceed a minimum threshold defined at distributor launch. Based on configuration set at launch, an approval in the form of a message from the recipient puzzle hash may or may not be required for every payout.
* **refresh**: In the curated NFT mode, this action can be called by anyone to update an NFT's weight to the current one set in the DataLayer store (if there is a mismatch). This action allows projects (curators) to have a dynamic list of weights. This action is optional for the curated NFT mode - distributors containing it are referred to as 'refreshable.' Non-refreshable distributors do not allow the update of an NFT's staked weight unless the owner voluntarily unstakes and stakes it again.

The reward distributor’s type is static and set at launch. A managed NFT/curated NFT/CAT reward distributor will not contain the add_entry/remove_entry actions, while a managed reward distributor will not contain the stake/unstake actions. Only curated NFT reward distributors may contain the 'refresh' action. More detailed documentation for the DIG reward distributor can be found [here](https://docs.xchandles.com/techincal-manual/dig-reward-distributor).

In non-managed modes of the reward distributor, the stake and unstake puzzles manage common functionality (such as slot assertion and optional consolidation for the same puzzle hash, as well as payouts) while delegating the calculation of additional/removed shares and specific locking conditions to locking and unlocking puzzles. For example, a collection NFT reward distributor and a CAT reward distributor will have the same stake/unstake base puzzles. The former's locking puzzle will be set to the 'nfts_from_dl' variation of the locking puzzle and the NFT unlocking puzzle, while the latter will use the CAT locking and unlocking puzzles.

## Test Cases
A series of tests covering one or more cases for all actions of the reward distributor are available in chia-wallet-sdk.

## Reference Implementation
The puzzles for the reward distributor can be found [here](https://github.com/DIG-Network/reward-distributor-clsp/tree/main/rue-puzzles/actions/reward-distributor) ([Chialisp](https://github.com/DIG-Network/reward-distributor-clsp/tree/main/puzzles/actions/reward_distributor)). Drivers and tests for both kinds of reward distributors have been integrated into chia-wallet-sdk. A CLI for deploying and interacting with reward distributors through Coinset.org the Sage RPC is available [here](https://github.com/Yakuhito/slot-machine).

## Security
Because the reward distributor controls committed funds until they’re paid out, puzzle risk is a significant concern, especially in the context of an on-chain app with high complexity. Multiple members of the Chia community have reviewed the puzzles included in this CHIP.

It should also be noted that, in the managed operating mode, managers are responsible for keeping the active entry list of a reward distributor accurate. Since every modification requires an on-chain transaction, the software managers run should have protections against 'griefing' attacks. Entries may continuously switch their status (e.g., mirrors could go online, then offline, then repeat the process many times) in an attempt to make the manager pay significant network fees over many update transactions.

## Copyright
Copyright and rights to this CHIP waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).