chia_sdk_driver/primitives/option/
option_underlying.rs1use chia_protocol::{Bytes32, Coin};
2use chia_puzzle_types::{
3 cat::CatArgs,
4 offer::{NotarizedPayment, Payment},
5 Memos,
6};
7use chia_puzzles::SETTLEMENT_PAYMENT_HASH;
8use chia_sdk_types::{
9 conditions::{
10 AssertBeforeSecondsAbsolute, AssertPuzzleAnnouncement, AssertSecondsAbsolute, CreateCoin,
11 },
12 payment_assertion,
13 puzzles::{
14 AugmentedConditionArgs, AugmentedConditionSolution, P2OneOfManySolution, RevocationArgs,
15 SingletonMember, SingletonMemberSolution,
16 },
17 MerkleTree, Mod,
18};
19use clvm_traits::{clvm_list, clvm_quote, match_list, ClvmEncoder, ToClvm};
20use clvm_utils::{ToTreeHash, TreeHash, TreeHasher};
21use clvmr::NodePtr;
22
23use crate::{
24 member_puzzle_hash, DriverError, InnerPuzzleSpend, Layer, MipsSpend, P2OneOfManyLayer, Spend,
25 SpendContext,
26};
27
28use super::OptionType;
29
30pub type OptionDelegatedPuzzle = (
31 u8,
32 match_list!(
33 AssertBeforeSecondsAbsolute,
34 AssertPuzzleAnnouncement,
35 CreateCoin<Bytes32>
36 ),
37);
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub struct OptionUnderlying {
41 pub launcher_id: Bytes32,
42 pub creator_puzzle_hash: Bytes32,
43 pub seconds: u64,
44 pub amount: u64,
45 pub strike_type: OptionType,
46}
47
48impl OptionUnderlying {
49 pub fn new(
50 launcher_id: Bytes32,
51 creator_puzzle_hash: Bytes32,
52 seconds: u64,
53 amount: u64,
54 strike_type: OptionType,
55 ) -> Self {
56 Self {
57 launcher_id,
58 creator_puzzle_hash,
59 seconds,
60 amount,
61 strike_type,
62 }
63 }
64
65 pub fn merkle_tree(&self) -> MerkleTree {
66 MerkleTree::new(&[self.exercise_path_hash(), self.clawback_path_hash()])
67 }
68
69 pub fn exercise_path_hash(&self) -> Bytes32 {
70 let singleton_member_hash = SingletonMember::new(self.launcher_id).curry_tree_hash();
71 member_puzzle_hash(0, Vec::new(), singleton_member_hash, true).into()
72 }
73
74 pub fn clawback_path_hash(&self) -> Bytes32 {
75 AugmentedConditionArgs::<TreeHash, TreeHash>::new(
76 AssertSecondsAbsolute::new(self.seconds).into(),
77 self.creator_puzzle_hash.into(),
78 )
79 .curry_tree_hash()
80 .into()
81 }
82
83 pub fn into_1_of_n(&self) -> P2OneOfManyLayer {
84 P2OneOfManyLayer::new(self.merkle_tree().root())
85 }
86
87 pub fn requested_payment<E>(
88 &self,
89 encoder: &mut E,
90 ) -> Result<NotarizedPayment<E::Node>, DriverError>
91 where
92 E: ClvmEncoder,
93 {
94 Ok(NotarizedPayment {
95 nonce: self.launcher_id,
96 payments: vec![Payment {
97 puzzle_hash: self.creator_puzzle_hash,
98 amount: self.strike_type.amount(),
99 memos: if self.strike_type.is_hinted() {
100 Memos::Some(vec![self.creator_puzzle_hash].to_clvm(encoder)?)
101 } else {
102 Memos::None
103 },
104 }],
105 })
106 }
107
108 pub fn delegated_puzzle(&self) -> OptionDelegatedPuzzle {
109 let puzzle_hash = match self.strike_type {
110 OptionType::Xch { .. } => SETTLEMENT_PAYMENT_HASH.into(),
111 OptionType::Cat { asset_id, .. } => {
112 CatArgs::curry_tree_hash(asset_id, SETTLEMENT_PAYMENT_HASH.into()).into()
113 }
114 OptionType::RevocableCat {
115 asset_id,
116 hidden_puzzle_hash,
117 ..
118 } => CatArgs::curry_tree_hash(
119 asset_id,
120 RevocationArgs::new(hidden_puzzle_hash, SETTLEMENT_PAYMENT_HASH.into())
121 .curry_tree_hash(),
122 )
123 .into(),
124 OptionType::Nft {
125 settlement_puzzle_hash,
126 ..
127 } => settlement_puzzle_hash,
128 };
129
130 clvm_quote!(clvm_list!(
131 AssertBeforeSecondsAbsolute::new(self.seconds),
132 payment_assertion(
133 puzzle_hash,
134 self.requested_payment(&mut TreeHasher)
135 .expect("failed to hash")
136 .tree_hash()
137 ),
138 CreateCoin::new(SETTLEMENT_PAYMENT_HASH.into(), self.amount, Memos::None)
139 ))
140 }
141
142 pub fn exercise_spend(
143 &self,
144 ctx: &mut SpendContext,
145 singleton_inner_puzzle_hash: Bytes32,
146 singleton_amount: u64,
147 ) -> Result<Spend, DriverError> {
148 let merkle_tree = self.merkle_tree();
149
150 let custody_hash: TreeHash = self.exercise_path_hash().into();
151 let merkle_proof = merkle_tree
152 .proof(custody_hash.into())
153 .ok_or(DriverError::InvalidMerkleProof)?;
154
155 let delegated_puzzle = ctx.alloc(&self.delegated_puzzle())?;
156 let delegated_spend = Spend::new(delegated_puzzle, NodePtr::NIL);
157
158 let mut mips = MipsSpend::new(delegated_spend);
159
160 let singleton_member_puzzle = ctx.curry(SingletonMember::new(self.launcher_id))?;
161 let singleton_member_solution = ctx.alloc(&SingletonMemberSolution::new(
162 singleton_inner_puzzle_hash,
163 singleton_amount,
164 ))?;
165 mips.members.insert(
166 custody_hash,
167 InnerPuzzleSpend::new(
168 0,
169 Vec::new(),
170 Spend::new(singleton_member_puzzle, singleton_member_solution),
171 ),
172 );
173
174 let spend = mips.spend(ctx, custody_hash)?;
175
176 P2OneOfManyLayer::new(merkle_tree.root()).construct_spend(
177 ctx,
178 P2OneOfManySolution::new(merkle_proof, spend.puzzle, spend.solution),
179 )
180 }
181
182 pub fn clawback_spend(
183 &self,
184 ctx: &mut SpendContext,
185 spend: Spend,
186 ) -> Result<Spend, DriverError> {
187 let merkle_tree = self.merkle_tree();
188
189 let puzzle_hash = self.clawback_path_hash();
190 let merkle_proof = merkle_tree
191 .proof(puzzle_hash)
192 .ok_or(DriverError::InvalidMerkleProof)?;
193
194 let puzzle = ctx.curry(AugmentedConditionArgs::<NodePtr, NodePtr>::new(
195 AssertSecondsAbsolute::new(self.seconds).into(),
196 spend.puzzle,
197 ))?;
198
199 let solution = ctx.alloc(&AugmentedConditionSolution::new(spend.solution))?;
200
201 P2OneOfManyLayer::new(merkle_tree.root()).construct_spend(
202 ctx,
203 P2OneOfManySolution::new(merkle_proof, puzzle, solution),
204 )
205 }
206
207 pub fn exercise_coin_spend(
208 &self,
209 ctx: &mut SpendContext,
210 coin: Coin,
211 singleton_inner_puzzle_hash: Bytes32,
212 singleton_amount: u64,
213 ) -> Result<(), DriverError> {
214 let spend = self.exercise_spend(ctx, singleton_inner_puzzle_hash, singleton_amount)?;
215 ctx.spend(coin, spend)
216 }
217
218 pub fn clawback_coin_spend(
219 &self,
220 ctx: &mut SpendContext,
221 coin: Coin,
222 spend: Spend,
223 ) -> Result<(), DriverError> {
224 let spend = self.clawback_spend(ctx, spend)?;
225 ctx.spend(coin, spend)
226 }
227}
228
229impl ToTreeHash for OptionUnderlying {
230 fn tree_hash(&self) -> TreeHash {
231 self.into_1_of_n().tree_hash()
232 }
233}