chia_sdk_driver/layers/action_layer/actions/xchandles/
refund.rs1use chia_protocol::Bytes32;
2use chia_puzzle_types::singleton::SingletonStruct;
3use chia_sdk_types::{
4 Conditions, Mod, announcement_id,
5 puzzles::{
6 DefaultCatMakerArgs, PrecommitSpendMode, PuzzleHashPuzzleAndSolution,
7 XchandlesHandleSlotValue, XchandlesOtherPrecommitData, XchandlesPricingSolution,
8 XchandlesRefundActionArgs, XchandlesRefundActionSolution, XchandlesSlotNonce,
9 },
10};
11use clvm_traits::{FromClvm, ToClvm};
12use clvm_utils::{ToTreeHash, TreeHash};
13use clvmr::NodePtr;
14
15use crate::{
16 DriverError, PrecommitCoin, PrecommitLayer, SingletonAction, Slot, Spend, SpendContext,
17 XchandlesConstants, XchandlesPrecommitValue, XchandlesRegistry,
18 XchandlesRegistryCreatedAnnouncementPrefix,
19};
20
21use super::{XchandlesPrecommitValueLog, XchandlesRefundActionLog, run_pricing_output};
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct XchandlesRefundAction {
25 pub launcher_id: Bytes32,
26 pub relative_block_height: u32,
27 pub payout_puzzle_hash: Bytes32,
28}
29
30impl ToTreeHash for XchandlesRefundAction {
31 fn tree_hash(&self) -> TreeHash {
32 Self::new_args(
33 self.launcher_id,
34 self.relative_block_height,
35 self.payout_puzzle_hash,
36 )
37 .curry_tree_hash()
38 }
39}
40
41impl SingletonAction<XchandlesRegistry> for XchandlesRefundAction {
42 fn from_constants(constants: &XchandlesConstants) -> Self {
43 Self {
44 launcher_id: constants.launcher_id,
45 relative_block_height: constants.relative_block_height,
46 payout_puzzle_hash: constants.precommit_payout_puzzle_hash,
47 }
48 }
49}
50
51impl XchandlesRefundAction {
52 pub fn new_args(
53 launcher_id: Bytes32,
54 relative_block_height: u32,
55 payout_puzzle_hash: Bytes32,
56 ) -> XchandlesRefundActionArgs {
57 XchandlesRefundActionArgs {
58 precommit_1st_curry_hash: PrecommitLayer::<()>::first_curry_hash(
59 SingletonStruct::new(launcher_id).tree_hash().into(),
60 relative_block_height,
61 payout_puzzle_hash,
62 )
63 .into(),
64 handle_slot_1st_curry_hash: Slot::<()>::first_curry_hash(
65 launcher_id,
66 XchandlesSlotNonce::HANDLE.to_u64(),
67 )
68 .into(),
69 }
70 }
71
72 fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
73 ctx.curry(Self::new_args(
74 self.launcher_id,
75 self.relative_block_height,
76 self.payout_puzzle_hash,
77 ))
78 }
79
80 pub fn get_log(
81 ctx: &mut SpendContext,
82 solution: NodePtr,
83 ) -> Result<XchandlesRefundActionLog, DriverError> {
84 let solution =
85 XchandlesRefundActionSolution::<NodePtr, (), NodePtr, NodePtr, Bytes32>::from_clvm(
86 ctx, solution,
87 )?;
88
89 let pricing_solution = ctx.extract::<XchandlesPricingSolution>(
90 solution.precommited_pricing_puzzle_and_solution.solution,
91 )?;
92
93 let (precommitted_total_price, precommitted_registered_time) = run_pricing_output(
94 ctx,
95 solution.precommited_pricing_puzzle_and_solution.puzzle,
96 solution.precommited_pricing_puzzle_and_solution.solution,
97 )?;
98
99 let handle = solution.handle.clone();
100 let precommit_value = XchandlesPrecommitValueLog::new(
101 solution.precommited_cat_maker_and_solution.puzzle_hash,
102 solution.precommited_pricing_puzzle_and_solution.puzzle_hash,
103 pricing_solution,
104 handle,
105 solution.other_precommit_data.refund_and_secret.secret,
106 solution.other_precommit_data.launcher_ids.owner_launcher_id,
107 solution
108 .other_precommit_data
109 .launcher_ids
110 .resolved_launcher_id,
111 );
112
113 let spent_slot = solution.slot_value;
114 let created_slot = spent_slot.map(|slot| slot.with_counter(slot.counter + 1));
115
116 Ok(XchandlesRefundActionLog {
117 spent_slot,
118 created_slot,
119 precommit_value,
120 precommitted_total_price,
121 precommitted_registered_time,
122 })
123 }
124
125 pub fn spend(
126 self,
127 ctx: &mut SpendContext,
128 registry: &mut XchandlesRegistry,
129 precommit_coin: &PrecommitCoin<XchandlesPrecommitValue>,
130 precommited_pricing_puzzle_reveal: NodePtr,
131 precommited_pricing_puzzle_solution: NodePtr,
132 slot: Option<Slot<XchandlesHandleSlotValue>>,
133 ) -> Result<Conditions, DriverError> {
134 let refund_announcement =
136 XchandlesRegistryCreatedAnnouncementPrefix::refund(precommit_coin.coin.puzzle_hash);
137
138 let my_inner_puzzle_hash = registry.info.inner_puzzle_hash().into();
140 precommit_coin.spend(ctx, PrecommitSpendMode::REFUND, my_inner_puzzle_hash)?;
141
142 let slot = slot.map(|s| registry.actual_handle_slot(s));
144 let cat_maker_args = DefaultCatMakerArgs::new(precommit_coin.asset_id.tree_hash().into());
145 let action_solution = XchandlesRefundActionSolution {
146 precommited_pricing_puzzle_and_solution: PuzzleHashPuzzleAndSolution::new(
147 ctx.tree_hash(precommited_pricing_puzzle_reveal).into(),
148 precommited_pricing_puzzle_reveal,
149 precommited_pricing_puzzle_solution,
150 ),
151 precommited_cat_maker_and_solution: PuzzleHashPuzzleAndSolution::new(
152 cat_maker_args.curry_tree_hash().into(),
153 ctx.curry(cat_maker_args)?,
154 (),
155 ),
156 handle: precommit_coin.value.handle.clone(),
157 precommit_amount: precommit_coin.coin.amount,
158 slot_value: slot.as_ref().map(|slot| slot.info.value),
159 other_precommit_data: XchandlesOtherPrecommitData::new(
160 precommit_coin.value.owner_launcher_id,
161 precommit_coin.value.resolved_launcher_id,
162 precommit_coin.refund_puzzle_hash.tree_hash().into(),
163 precommit_coin.value.secret,
164 ),
165 }
166 .to_clvm(ctx)?;
167 let action_puzzle = self.construct_puzzle(ctx)?;
168
169 registry.insert_action_spend(ctx, Spend::new(action_puzzle, action_solution))?;
170
171 if let Some(slot) = slot {
173 slot.spend(ctx, my_inner_puzzle_hash)?;
174 }
175
176 Ok(
177 Conditions::new().assert_puzzle_announcement(announcement_id(
178 registry.coin.puzzle_hash,
179 refund_announcement,
180 )),
181 )
182 }
183}