CHIP Number | 0008 |
---|---|
Title | Splitter Puzzle |
Description | A standard puzzle for splitting a coin according to a predefined splitting schema. |
Author | Andreas Greimel (@acevail_ on Twitter) |
Editor | Dan Perry |
Comments-URI | #30 |
Status | Final |
Category | Informational |
Sub-Category | Informative |
Created | 2022-09-03 |
Dependencies | None |
This proposal aims to define a standard puzzle for splitting a coin according to a predefined splitting schema.
One of the great features of NFT1 is the built-in payment of royalties. It is very flexible, since any address and therefore any puzzle can be used as royalty recipient. Many people in the community have requested the option to split the royalty payment between different recipients. For example, a share should be sent to a charity, or multiple artists want to share the royalties automatically. Having this features available increases the value of the Chia NFT ecosystem as a whole.
This proposal does not have any backwards incompatibilities.
There have already been approaches to split royalties in the Chia ecosystem. The shortcoming of the existing approaches is a dependence on a single actor to trigger the royalty split.
The solution outlined in this propsal ensures independence from any single actor. This means that any observer of the Chia blockchain is able to trigger the royalty split, including the users wallet or an NFT marketplace.
The core is a simple Chialisp puzzle without many moving parts. It includes a payout scheme defining how the royalty should be split. The payment scheme is fixed and can not be altered by third parties. Anyone is able to trigger the royalty payout for any NFT.
An example payout scheme could look like this
Recipient Address | Share |
---|---|
xch18qt2ju2sj3k8w3290az6flkkc95fqcmcg7edl90ns0jrjav8xttsfmtqfp | 80 |
xch1p9e3l3ttl7qrrhy6zmmqmjm0v33fvrxhd494yv7at0ppd97hljns4uw464 | 20 |
The proposed Chialisp puzzle looks like this.
; This puzzle is designed to split its coin amount according to a predefined payout scheme.
(mod
(
PAYOUT_SCHEME ; The payout scheme is a list of recipient puzzle hashes and their share, e.g.
; ((0xcafef00d 80) (0xdeadbeef 20)) for an 80/20 split
my_amount ; The amount of the coin to be split.
total_shares ; The sum of all shares in the payout scheme. Passed in to reduce the complexity of the puzzle.
)
(include condition_codes.clib)
(include curry_and_treehash.clib)
(defun-inline get_puzhash (payout_scheme_item)
(f payout_scheme_item)
)
(defun-inline get_share (payout_scheme_item)
(f (r payout_scheme_item))
)
(defun-inline calculate_share (total_amount share total_shares)
(f (divmod (* total_amount share) total_shares))
)
(defun-inline get_amount (payout_scheme_item total_amount total_shares)
(calculate_share total_amount (get_share payout_scheme_item) total_shares)
)
; mutual recursive function to calculate the amount only once
(defun calculate_amount_and_split (PAYOUT_SCHEME total_amount total_shares shares_sum remaining_amount)
(if PAYOUT_SCHEME
(split_amount_and_create_coins PAYOUT_SCHEME (get_amount (f PAYOUT_SCHEME) total_amount total_shares) total_amount total_shares shares_sum remaining_amount)
(if (= total_shares shares_sum)
()
(x) ; raise if total shares input doesn't match the sum of all shares
)
)
)
; Loop through the royalty payout scheme and create coins
(defun split_amount_and_create_coins (PAYOUT_SCHEME this_amount total_amount total_shares shares_sum remaining_amount)
(c
(list
CREATE_COIN
(get_puzhash (f PAYOUT_SCHEME))
(if (r PAYOUT_SCHEME) this_amount remaining_amount)
(list (get_puzhash (f PAYOUT_SCHEME)))
)
(calculate_amount_and_split
(r PAYOUT_SCHEME)
total_amount
total_shares
(+ shares_sum (get_share (f PAYOUT_SCHEME)))
(- remaining_amount this_amount)
)
)
)
; main
(c
(list CREATE_COIN_ANNOUNCEMENT ())
(c
(list ASSERT_MY_AMOUNT my_amount)
(calculate_amount_and_split PAYOUT_SCHEME my_amount total_shares 0 my_amount)
)
)
)
The payout scheme PAYOUT_SCHEME
is curried into this puzzle, to make it immutable.
Using the above scheme as an example, it looks like this:
((0x3816a97150946c7745457f45a4fed6c16890637847b2df95f383e439758732d7 80) (0x09731fc56bff8031dc9a16f60dcb6f6462960cd76d4b5233dd5bc21697d7fca7 20)
The only solution parameter needed on spend is the actual amount of the coin and the total number of shares. The total number of shares is defined as the sum of all individual shares in the scheme.
The puzzle is very flexible and doesn't require a specific number of shares. This means the following share distributions are equivalent:
Recipient Address | Share |
---|---|
xch18qt2ju2sj3k8w3290az6flkkc95fqcmcg7edl90ns0jrjav8xttsfmtqfp | 4 |
xch1p9e3l3ttl7qrrhy6zmmqmjm0v33fvrxhd494yv7at0ppd97hljns4uw464 | 1 |
is equivalent to
Recipient Address | Share |
---|---|
xch18qt2ju2sj3k8w3290az6flkkc95fqcmcg7edl90ns0jrjav8xttsfmtqfp | 80 |
xch1p9e3l3ttl7qrrhy6zmmqmjm0v33fvrxhd494yv7at0ppd97hljns4uw464 | 20 |
is equivalent to
Recipient Address | Share |
---|---|
xch18qt2ju2sj3k8w3290az6flkkc95fqcmcg7edl90ns0jrjav8xttsfmtqfp | 8000 |
xch1p9e3l3ttl7qrrhy6zmmqmjm0v33fvrxhd494yv7at0ppd97hljns4uw464 | 2000 |
This gives the creator of the puzzle a lot of flexibility on how to split the coin.
On spend, the puzzle steps through the entries of the PAYOUT_SCHEME
and creates a new coin per entry. The amount of the new coins will be a share of the total amount, determined by the share defined in the PAYOUT_SCHEME
and the total number of shares.
If the created coins do not use up the full coin amount, the rest is added to the last recipient in the payout scheme. This difference is at most NUMBER_OF_RECIPIENTS mojos, therefore its not worth the effort to construct a more complicated mechanism. It is important that the full coin amount is being reissued, otherwise it breaks CAT coins.
Here are some example coin splits for the above schema
Coin Amount | Recipient 1 receives | Recipient 2 receives |
---|---|---|
1 mojos | 0 mojos | 1 mojos |
2 mojos | 1 mojos | 1 mojos |
100 mojos | 80 mojos | 20 mojos |
101 mojos | 80 mojos | 21 mojos |
102 mojos | 81 mojos | 21 mojos |
1000 mojos | 800 mojos | 200 mojos |
1001 mojos | 800 mojos | 201 mojos |
The unit test for the split Chialisp puzzle is located in the greimela/chialisp
GitHub repository: royalty_share/tests/test_p2_royalty_share_arbitrary_shares.py.
The Chialisp puzzle itself is located in the greimela/chialisp
GitHub repository: royalty_share/clsp/p2_royalty_share_arbitrary_shares/p2_royalty_share_arbitrary_shares_rest_to_last.clsp.
This repository is a fork of the Chialisp repository by @trgarrett, which already contains some driver code for this and other royalty puzzles. The plan is to unify all those royalty puzzles into one standard.
The Chialisp code has been covered by unit tests, but has not been audited yet by Chia Network or the broader community.
Potential risks:
- Royalty recipient can be altered
- The royalty recipients are curried into the puzzle and the only parameter in the solution is the amount. So the payout scheme can not be altered after it has been created.
- Puzzle can be bricked by wrong configuration
- Due to the dynamic share calculation, any payout scheme with positive integers as shares will work fine.
- The nature of Chialisp is that the puzzle will only execute and check the validity of the payout scheme when spending the coin. This is too late to detect any configuration errors, so the tools constructing the puzzle have to make sure that the payout scheme adheres to the defined format.
- The puzzle does not support negative shares or fractional shares and will behave in undefined ways if configured that way.
- Royalty payout can be stalled
- Since the royalty payment can be triggered by any party on a permissionless blockchain, nobody can stall or prevent this.
- Info to construct split puzzle can get lost
- As soon as one particular split occurs for the first time, the puzzle is stored on the blockchain. Also, it is in the best interest of recipients of the split to store the payout scheme in order to get paid in the future.
No additional assets.
- Added the CHIP number to the preamble as it was missing (2023/05/19)
Copyright and related rights waived via CC0.