Skip to content

Commit 26d5d4c

Browse files
CHIP-0045: Option Contracts (#152)
* Add options * Revocable CATs are their own type * NFT and more security considerations * Add validations section * Add explanation of options * Add permalinks to the implementation * Open CHIP-45 as a Draft * Update comments link * Move CHIP-45 to Review status * Clarify that NFTs as underlying need royalties, and NFTs should not be the strike price * Move CHIP-45 to Last Call * Move CHIP-45 to Final status --------- Co-authored-by: danieljperry <d.perry@chia.net>
1 parent 84bfb25 commit 26d5d4c

File tree

1 file changed

+246
-0
lines changed

1 file changed

+246
-0
lines changed

CHIPs/chip-0045.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
| CHIP Number | 0045 |
2+
| :------------ | :------------------------------------------------------------------------- |
3+
| Title | Option Contracts |
4+
| Description | Put and call options on the Chia blockchain. |
5+
| Author | [Brandon Haggstrom](https://github.com/Rigidity) |
6+
| Editor | [Dan Perry](https://github.com/danieljperry) |
7+
| Comments-URI | [CHIPs repo, PR #152](https://github.com/Chia-Network/chips/pull/152) |
8+
| Status | Final |
9+
| Category | Standards Track |
10+
| Sub-Category | Primitive |
11+
| Created | 2025-03-05 |
12+
| Requires | [0020](https://github.com/Chia-Network/chips/blob/main/CHIPs/chip-0020.md) |
13+
| Replaces | None |
14+
| Superseded-By | None |
15+
16+
## Abstract
17+
18+
American style put and call options for assets (including securities) can be peer-to-peer minted, traded, and exercised on the Chia blockchain, without intermediaries. This makes use of Chia's powerful coin set model and many existing primitives (including offer files) built on top of it.
19+
20+
## Motivation
21+
22+
With more assets coming to the Chia blockchain, including securities such as the certificates issued by Permuto, it's desirable to not only trade them, but also create an options market around these assets. By composing existing primitives, it shouldn't be too difficult to integrate this into the existing ecosystem.
23+
24+
Here's an example flow for how option contracts can be used:
25+
26+
1. Alice wants to make an option contract with an underlying 10,000 SBX, a strike price of 2 XCH, and an expiration of 3 months later.
27+
2. Alice makes an offer file wherein she mints the option contract, locks up the 10,000 SBX inside of it, and requests a 0.5 XCH premium.
28+
3. Bob buys the option for 0.5 XCH and hangs onto it for 2 months.
29+
4. Bob decides to exercise the option, so his wallet constructs an offer file to do so.
30+
5. He takes the offer, by melting the option contract, unlocking the underlying 10,000 SBX, and paying the strike price of 2 XCH.
31+
6. Alice gets 2 XCH and Bob gets 10,000 SBX.
32+
33+
If Bob never wanted to exercise the option, he could have let it expire. Then, Alice owns the 10,000 SBX again and Bob only had to pay 0.5 XCH.
34+
35+
## Backwards Compatibility
36+
37+
Even though option contract singletons are non-fungible, they don't follow the NFT1 standard. This is for a multitude of reasons:
38+
39+
1. NFTs typically include metadata that are undesirable for on-chain options, including images, off-chain JSON files, and licenses. If excluded, there could be subtle incompatibilities with existing display services and wallets.
40+
2. Option contracts do not need royalties to be paid when traded. Once the premium is paid, it's fully owned by the buyer and up to them what to do with it.
41+
3. NFTs cannot be melted, so using a custom singleton puzzle allows options to be melted instead of burned when they have been exercised.
42+
4. Options don't need the ability to be assigned to DID profiles. In fact, if NFTs were used for options, changing the DID owner or metadata would affect the puzzle and therefore prevent them from being exercised (due to burning the NFT being a requirement). In some cases, this could permanently and unintentionally brick the option.
43+
5. It would be relatively trivial to create a fake NFT that looks like an option contract, but doesn't in fact have the correct details (or any coin locked up in it at all). While display services and wallets could help differentiate between the two, backwards compatibility here would actually lead to misleading buyers. Therefore, this puzzle intentionally makes it so websites and apps have to opt-in to displaying them.
44+
45+
## Rationale
46+
47+
The rationale around the backwards incompatibility with the NFT1 standard is described above. However, there are other aspects that were considered but decided against.
48+
49+
It would have been possible to hard code the underlying coin spend paths as a list of conditions rather than a p2_1_of_n with a p2_singleton and clawback path inside. Although it's conceptually simpler, the p2_1_of_n allows revealing only the spend path you actually use, which can save on cost. The minter also automatically has full custody to spend the coin however they choose after it expires, rather than needing an intermediate spend to send it to their address first.
50+
51+
The metadata is stored once in the singleton launcher, so that intermediate transfers of the singleton don't incur additional cost by repeating the curried metadata in each coin spend. However, it's stored on-chain so that display services and wallets don't need to call centralized APIs or access off-chain information to validate the option and construct the offer file for exercising it.
52+
53+
## Specification
54+
55+
There are several moving parts to this standard, and it's important to get the implementation right. It combines many existing primitives rather than using only new Chialisp.
56+
57+
### Option Contract
58+
59+
The option contract itself is represented as a singleton with a custom inner puzzle (the Chialisp source code for this puzzle is attached). It's an asset that can be owned by a puzzle hash, transferred elsewhere, and traded with offer files.
60+
61+
When the option singleton is minted, the following metadata structure is included in the `key_value_list` parameter of the singleton launcher solution:
62+
63+
```
64+
(expiration_seconds strike_type)
65+
```
66+
67+
Where `strike_type` can be one of the following:
68+
69+
1. XCH `(0 amount)`
70+
2. CAT `(1 asset_id amount)`
71+
3. Revocable CAT `(2 asset_id hidden_puzzle_hash amount)`
72+
73+
Note that these values are proper nil-terminated lists, in order to leave room for extensions to this standard in the future.
74+
75+
### Underlying Coin
76+
77+
The underlying asset is locked in a coin with a p2_1_of_n puzzle. These puzzle members must be included in the merkle tree:
78+
79+
1. The exercise p2_singleton puzzle, controlled by the option contract singleton. For the purposes of standardization, the puzzle used by the reference implementation always follows the Meta Inner Puzzle Spec (MIPS), although this isn't strictly required. Specifically, a singleton member with no restrictions and a nonce of 0. Note, the p2_singleton puzzle itself is not wrapped in any conditions, since this is enforced by the option contract with the precommitted delegated puzzle hash.
80+
2. The clawback puzzle, which is the minter's puzzle hash wrapped in augmented_condition to enforce the `ASSERT_SECONDS_ABSOLUTE` timelock. Essentially, the coin can be arbitrarily spent by the minter after it expires (custody automatically changes hands without any actions being taken).
81+
82+
Memos (including the hint) are optional for the underlying coin, because the coin id is directly curried into the option contract singleton's inner puzzle.
83+
84+
### Delegated Puzzle
85+
86+
The delegated puzzle hash used to exercise the underlying coin is precommitted in the option singleton puzzle. It should be reproducible solely from the `strike_type` metadata parameter.
87+
88+
It's a simple quoted list of these conditions:
89+
90+
1. `(ASSERT_BEFORE_SECONDS_ABSOLUTE expiration_timestamp)`
91+
2. `(ASSERT_PUZZLE_ANNOUNCEMENT payment_announcement_id)`
92+
3. `(CREATE_COIN SETTLEMENT_PAYMENT_HASH amount)`
93+
94+
Note that the created coin does not have a memo. This is because it can instead be added to the final coin created when the taker of the offer sends the coin to themselves.
95+
96+
The announcement id consists of the following, hashed together:
97+
98+
1. The requested settlement puzzle hash. For CATs, this is optionally wrapped in the revocation layer, and then wrapped in the CAT layer.
99+
2. The notarized payment tree hash. The nonce is the launcher id of the option contract singleton, and the payment is made to the `minter_puzzle_hash` with a memo if it's a CAT.
100+
101+
If the underlying asset is an NFT, add the NFT transfer condition to reveal the strike price as the trade prices list. NFTs are not supported as the strike price in this version of the standard.
102+
103+
### Exercise Spend
104+
105+
The option contract will be exercised if two conditions are output by the inner puzzle at the same time (in any order):
106+
107+
1. `(CREATE_COIN () -113)`
108+
2. `(SEND_MESSAGE 23 underlying_delegated_puzzle_hash underlying_coin_id)`
109+
110+
The `CREATE_COIN` with `-113` amount is the standard singleton melt condition, which means the singleton will not be recreated after exercising it.
111+
112+
The `SEND_MESSAGE` authorizes the p2_singleton puzzle of the underlying coin to be spent, but it must adhere to the specific delegated puzzle that has been precommitted (according to the section above).
113+
114+
The underlying coin also needs to be manually spent to unlock the funds to the offer settlement puzzle. If either of the coins aren't spent, the other won't be valid (due to the message). The p2_singleton is one path of the p2_1_of_n puzzle used by the underlying coin, and it can be spent using MIPS.
115+
116+
The settlement coin must be claimed by the taker of the offer, and its announcement can be asserted by the conditions output by either the singleton inner puzzle or the coin used to pay the requested payment.
117+
118+
And finally, the requested payment must be made to fulfill the taker's side of the bargain. This allows an atomic swap with an offer file, even though there's a bit more complicated machinery going on under the hood than usual.
119+
120+
### Clawback Spend
121+
122+
The clawback spend path is the most straightforward. The sender's spend needs to be wrapped with the `ASSERT_SECONDS_ABSOLUTE` condition, by using the augmented_condition puzzle.
123+
124+
Other than that, this can be used directly in the p2_1_of_n spend to do whatever the sender wants with the coin after expiration.
125+
126+
### Validations
127+
128+
Display services and wallets must extensively verify the authenticity of the option contract. Just having a singleton with the correct inner puzzle doesn't necessarily mean it's a valid option.
129+
130+
Here is a list of things that will need to be fetched:
131+
132+
1. The singleton and option contract puzzle arguments
133+
2. The launcher coin's `key_value_lists` solution parameter
134+
3. The underlying coin (and possibly its parent or other related context) that matches the coin id in the option contract puzzle
135+
136+
And these are the validations that must be performed, in no particular order:
137+
138+
- The underlying coin exists, is unspent, and matches the claimed coin id
139+
- The metadata is valid and the expiration timestamp hasn't been reached yet
140+
- For revocable CAT strike assets, the correct hidden_puzzle_hash is used
141+
- The underlying coin's puzzle hash matches the expected p2_1_of_n puzzle hash
142+
- The precommitted delegated puzzle hash matches the expected one
143+
144+
### Option Contract Puzzle
145+
146+
This is intended to be used as an inner puzzle to singleton_top_layer_v1_1.
147+
148+
```lisp
149+
(mod (
150+
MOD_HASH
151+
UNDERLYING_COIN_ID
152+
UNDERLYING_DELEGATED_PUZZLE_HASH
153+
INNER_PUZZLE
154+
inner_solution
155+
)
156+
157+
(include curry.clib)
158+
(include utility_macros.clib)
159+
160+
(defconstant CREATE_COIN 51)
161+
(defconstant SEND_MESSAGE 66)
162+
163+
(defun-inline wrap_puzzle_hash (MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH inner_puzzle_hash)
164+
(curry_hashes MOD_HASH
165+
(sha256 1 MOD_HASH)
166+
(sha256 1 UNDERLYING_COIN_ID)
167+
(sha256 1 UNDERLYING_DELEGATED_PUZZLE_HASH)
168+
inner_puzzle_hash
169+
)
170+
)
171+
172+
(defun-inline wrap_create_coin (MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH (inner_puzzle_hash . rest))
173+
(c CREATE_COIN (c (wrap_puzzle_hash MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH inner_puzzle_hash) rest))
174+
)
175+
176+
; Wraps CREATE_COIN conditions in the option contract layer
177+
(defun morph_conditions (MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH conditions melted exercised)
178+
(if conditions
179+
(if (= (f (f conditions)) CREATE_COIN)
180+
(if (= (f (r (r (f conditions)))) -113)
181+
; Allow melting but make sure it's tracked for later
182+
(c
183+
(f conditions)
184+
(morph_conditions MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH (r conditions) 1 exercised)
185+
)
186+
; Wrap the created coin in the option contract layer
187+
(c
188+
(wrap_create_coin MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH (r (f conditions)))
189+
(morph_conditions MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH (r conditions) melted exercised)
190+
)
191+
)
192+
(if (and (= (f (f conditions)) SEND_MESSAGE) (all
193+
(= (f (r (f conditions))) 23) ; Puzzle hash => coin id
194+
(= (f (r (r (r (f conditions))))) UNDERLYING_COIN_ID) ; Received by underlying coin id
195+
))
196+
(assert (= (f (r (r (f conditions)))) UNDERLYING_DELEGATED_PUZZLE_HASH)
197+
; Make note that the option has been exercised
198+
(c
199+
(f conditions)
200+
(morph_conditions MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH (r conditions) melted 1)
201+
)
202+
)
203+
(c
204+
(f conditions)
205+
(morph_conditions MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH (r conditions) melted exercised)
206+
)
207+
)
208+
)
209+
(assert (= melted exercised) ; If melted, must also be exercised and vice versa
210+
()
211+
)
212+
)
213+
)
214+
215+
(morph_conditions MOD_HASH UNDERLYING_COIN_ID UNDERLYING_DELEGATED_PUZZLE_HASH (a INNER_PUZZLE inner_solution) () ())
216+
)
217+
```
218+
219+
## Test Cases
220+
221+
In the [wallet sdk implementation](https://github.com/xch-dev/chia-wallet-sdk/blob/4d7d666d2e7e9f4713b5b76af11ff073d7eda152/crates/chia-sdk-driver/src/primitives/option/option_contract.rs), there are tests for all of the following:
222+
223+
1. Minting
224+
2. Transferring
225+
3. Exercising (including ensuring it fails after expiration and without paying)
226+
4. Clawback (including ensuring it fails before expiration)
227+
5. XCH, CATs, Revocable CATs, and NFTs
228+
6. Ensuring melting fails without exercising and vice versa
229+
230+
## Reference Implementation
231+
232+
There is a comprehensive [option contract implementation](https://github.com/xch-dev/chia-wallet-sdk/tree/4d7d666d2e7e9f4713b5b76af11ff073d7eda152/crates/chia-sdk-driver/src/primitives/option) in the chia-wallet-sdk repo.
233+
234+
## Security
235+
236+
It's important to verify the authenticity of an option contract before displaying it, since although a singleton may adhere to the correct puzzle, the metadata and underlying coin might not line up as expected. This could mislead people into buying something that should be guaranteed but isn't.
237+
238+
The amount of the underlying coin or strike amount must be non-zero, due to a restriction in the settlement payments puzzle. Otherwise, the option cannot be exercised.
239+
240+
## Additional Assets
241+
242+
None
243+
244+
## Copyright
245+
246+
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

0 commit comments

Comments
 (0)