chia_sdk_driver/layers/action_layer/actions/catalog/
refund.rs1use chia_protocol::Bytes32;
2use chia_puzzle_types::singleton::SingletonStruct;
3use chia_sdk_types::{
4 Conditions, Mod, announcement_id,
5 puzzles::{
6 CatalogRefundActionArgs, CatalogRefundActionSolution, CatalogSlotValue,
7 DefaultCatMakerArgs, PrecommitSpendMode, SlotNeigborsInfo,
8 },
9};
10use clvm_traits::{FromClvm, ToClvm, clvm_tuple};
11use clvm_utils::{ToTreeHash, TreeHash};
12use clvmr::NodePtr;
13
14use crate::{
15 CatalogPrecommitValue, CatalogRegistry, CatalogRegistryConstants, DriverError, PrecommitCoin,
16 PrecommitLayer, SingletonAction, Slot, Spend, SpendContext,
17};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct CatalogRefundAction {
21 pub launcher_id: Bytes32,
22 pub relative_block_height: u32,
23 pub payout_puzzle_hash: Bytes32,
24}
25
26impl ToTreeHash for CatalogRefundAction {
27 fn tree_hash(&self) -> TreeHash {
28 Self::new_args(
29 self.launcher_id,
30 self.relative_block_height,
31 self.payout_puzzle_hash,
32 )
33 .curry_tree_hash()
34 }
35}
36
37impl SingletonAction<CatalogRegistry> for CatalogRefundAction {
38 fn from_constants(constants: &CatalogRegistryConstants) -> Self {
39 Self {
40 launcher_id: constants.launcher_id,
41 relative_block_height: constants.relative_block_height,
42 payout_puzzle_hash: constants.precommit_payout_puzzle_hash,
43 }
44 }
45}
46
47impl CatalogRefundAction {
48 pub fn new_args(
49 launcher_id: Bytes32,
50 relative_block_height: u32,
51 payout_puzzle_hash: Bytes32,
52 ) -> CatalogRefundActionArgs {
53 CatalogRefundActionArgs {
54 precommit_1st_curry_hash: PrecommitLayer::<()>::first_curry_hash(
55 SingletonStruct::new(launcher_id).tree_hash().into(),
56 relative_block_height,
57 payout_puzzle_hash,
58 )
59 .into(),
60 slot_1st_curry_hash: Slot::<CatalogSlotValue>::first_curry_hash(launcher_id, 0).into(),
61 }
62 }
63
64 pub fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
65 ctx.curry(Self::new_args(
66 self.launcher_id,
67 self.relative_block_height,
68 self.payout_puzzle_hash,
69 ))
70 }
71
72 pub fn spent_slot_value(
73 &self,
74 ctx: &SpendContext,
75 solution: NodePtr,
76 ) -> Result<Option<CatalogSlotValue>, DriverError> {
77 let params = CatalogRefundActionSolution::<NodePtr, ()>::from_clvm(ctx, solution)?;
78
79 Ok(params.neighbors.map(|neighbors| CatalogSlotValue {
80 asset_id: params.tail_hash,
81 neighbors,
82 }))
83 }
84
85 pub fn created_slot_value(
86 &self,
87 ctx: &SpendContext,
88 solution: NodePtr,
89 ) -> Result<Option<CatalogSlotValue>, DriverError> {
90 self.spent_slot_value(ctx, solution)
91 }
92
93 pub fn spend(
94 self,
95 ctx: &mut SpendContext,
96 catalog: &mut CatalogRegistry,
97 tail_hash: Bytes32,
98 neighbors: Option<SlotNeigborsInfo>,
99 precommit_coin: &PrecommitCoin<CatalogPrecommitValue>,
100 slot: Option<Slot<CatalogSlotValue>>,
101 ) -> Result<Conditions, DriverError> {
102 let mut refund_announcement =
104 clvm_tuple!(tail_hash, precommit_coin.value.initial_inner_puzzle_hash)
105 .tree_hash()
106 .to_vec();
107 refund_announcement.insert(0, b'$');
108
109 let secure_conditions = Conditions::new().assert_puzzle_announcement(announcement_id(
110 catalog.coin.puzzle_hash,
111 refund_announcement,
112 ));
113
114 let spender_inner_puzzle_hash = catalog.info.inner_puzzle_hash().into();
116 let initial_inner_puzzle_hash = precommit_coin.value.initial_inner_puzzle_hash;
117 precommit_coin.spend(ctx, PrecommitSpendMode::REFUND, spender_inner_puzzle_hash)?;
118
119 if let Some(slot) = slot {
121 let slot = catalog.actual_slot(slot);
122 slot.spend(ctx, spender_inner_puzzle_hash)?;
123 }
124
125 let cat_maker_args = DefaultCatMakerArgs::new(precommit_coin.asset_id.tree_hash().into());
127 let action_solution = CatalogRefundActionSolution {
128 precommited_cat_maker_reveal: ctx.curry(cat_maker_args)?,
129 precommited_cat_maker_hash: cat_maker_args.curry_tree_hash().into(),
130 precommited_cat_maker_solution: (),
131 tail_hash,
132 initial_nft_owner_ph: initial_inner_puzzle_hash,
133 refund_puzzle_hash_hash: precommit_coin.refund_puzzle_hash.tree_hash().into(),
134 precommit_amount: precommit_coin.coin.amount,
135 neighbors,
136 };
137 let action_solution = action_solution.to_clvm(ctx)?;
138 let action_puzzle = self.construct_puzzle(ctx)?;
139
140 catalog.insert_action_spend(ctx, Spend::new(action_puzzle, action_solution))?;
141 Ok(secure_conditions)
142 }
143}