Skip to main content

chia_sdk_driver/layers/action_layer/actions/xchandles/
refund.rs

1use 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        // calculate announcement
135        let refund_announcement =
136            XchandlesRegistryCreatedAnnouncementPrefix::refund(precommit_coin.coin.puzzle_hash);
137
138        // spend precommit coin
139        let my_inner_puzzle_hash = registry.info.inner_puzzle_hash().into();
140        precommit_coin.spend(ctx, PrecommitSpendMode::REFUND, my_inner_puzzle_hash)?;
141
142        // spend self
143        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 there's a slot, spend it
172        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}