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    announcement_id,
5    puzzles::{
6        DefaultCatMakerArgs, PrecommitSpendMode, XchandlesRefundActionArgs,
7        XchandlesRefundActionSolution, XchandlesSlotValue,
8    },
9    Conditions, Mod,
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};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct XchandlesRefundAction {
22    pub launcher_id: Bytes32,
23    pub relative_block_height: u32,
24    pub payout_puzzle_hash: Bytes32,
25}
26
27impl ToTreeHash for XchandlesRefundAction {
28    fn tree_hash(&self) -> TreeHash {
29        Self::new_args(
30            self.launcher_id,
31            self.relative_block_height,
32            self.payout_puzzle_hash,
33        )
34        .curry_tree_hash()
35    }
36}
37
38impl SingletonAction<XchandlesRegistry> for XchandlesRefundAction {
39    fn from_constants(constants: &XchandlesConstants) -> Self {
40        Self {
41            launcher_id: constants.launcher_id,
42            relative_block_height: constants.relative_block_height,
43            payout_puzzle_hash: constants.precommit_payout_puzzle_hash,
44        }
45    }
46}
47
48impl XchandlesRefundAction {
49    pub fn new_args(
50        launcher_id: Bytes32,
51        relative_block_height: u32,
52        payout_puzzle_hash: Bytes32,
53    ) -> XchandlesRefundActionArgs {
54        XchandlesRefundActionArgs {
55            precommit_1st_curry_hash: PrecommitLayer::<()>::first_curry_hash(
56                SingletonStruct::new(launcher_id).tree_hash().into(),
57                relative_block_height,
58                payout_puzzle_hash,
59            )
60            .into(),
61            slot_1st_curry_hash: Slot::<()>::first_curry_hash(launcher_id, 0).into(),
62        }
63    }
64
65    fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
66        ctx.curry(Self::new_args(
67            self.launcher_id,
68            self.relative_block_height,
69            self.payout_puzzle_hash,
70        ))
71    }
72
73    pub fn spent_slot_value(
74        ctx: &SpendContext,
75        solution: NodePtr,
76    ) -> Result<Option<XchandlesSlotValue>, DriverError> {
77        let solution =
78            XchandlesRefundActionSolution::<NodePtr, NodePtr, NodePtr, NodePtr, NodePtr>::from_clvm(
79                ctx, solution,
80            )?;
81
82        Ok(solution.slot_value)
83    }
84
85    pub fn created_slot_value(
86        spent_slot_value: Option<XchandlesSlotValue>,
87    ) -> Option<XchandlesSlotValue> {
88        spent_slot_value // nothing changed; just oracle
89    }
90
91    pub fn spend(
92        self,
93        ctx: &mut SpendContext,
94        registry: &mut XchandlesRegistry,
95        precommit_coin: &PrecommitCoin<XchandlesPrecommitValue>,
96        precommited_pricing_puzzle_reveal: NodePtr,
97        precommited_pricing_puzzle_solution: NodePtr,
98        slot: Option<Slot<XchandlesSlotValue>>,
99    ) -> Result<Conditions, DriverError> {
100        // calculate announcement
101        let mut refund_announcement = precommit_coin.coin.puzzle_hash.to_vec();
102        refund_announcement.insert(0, b'$');
103
104        // spend precommit coin
105        let my_inner_puzzle_hash = registry.info.inner_puzzle_hash().into();
106        precommit_coin.spend(ctx, PrecommitSpendMode::REFUND, my_inner_puzzle_hash)?;
107
108        // spend self
109        let slot = slot.map(|s| registry.actual_slot(s));
110        let cat_maker_args = DefaultCatMakerArgs::new(precommit_coin.asset_id.tree_hash().into());
111        let action_solution = XchandlesRefundActionSolution {
112            precommited_cat_maker_reveal: ctx.curry(cat_maker_args)?,
113            precommited_cat_maker_hash: cat_maker_args.curry_tree_hash().into(),
114            precommited_cat_maker_solution: (),
115            precommited_pricing_puzzle_reveal,
116            precommited_pricing_puzzle_hash: ctx
117                .tree_hash(precommited_pricing_puzzle_reveal)
118                .into(),
119            precommited_pricing_puzzle_solution,
120            handle: precommit_coin.value.handle.clone(),
121            secret: precommit_coin.value.secret,
122            precommited_owner_launcher_id: precommit_coin.value.owner_launcher_id,
123            precommited_resolved_data: precommit_coin.value.resolved_data.clone(),
124            refund_puzzle_hash_hash: precommit_coin.refund_puzzle_hash.tree_hash().into(),
125            precommit_amount: precommit_coin.coin.amount,
126            slot_value: slot.as_ref().map(|slot| slot.info.value.clone()),
127        }
128        .to_clvm(ctx)?;
129        let action_puzzle = self.construct_puzzle(ctx)?;
130
131        registry.insert_action_spend(ctx, Spend::new(action_puzzle, action_solution))?;
132
133        // if there's a slot, spend it
134        if let Some(slot) = slot {
135            slot.spend(ctx, my_inner_puzzle_hash)?;
136        }
137
138        Ok(
139            Conditions::new().assert_puzzle_announcement(announcement_id(
140                registry.coin.puzzle_hash,
141                refund_announcement,
142            )),
143        )
144    }
145}