chia_sdk_driver/layers/action_layer/actions/xchandles/
extend.rs1use chia_protocol::Bytes32;
2use chia_puzzle_types::offer::{NotarizedPayment, Payment};
3use chia_puzzles::SETTLEMENT_PAYMENT_HASH;
4use chia_sdk_types::{
5 Conditions, Mod, announcement_id,
6 puzzles::{
7 DefaultCatMakerArgs, XchandlesExtendActionArgs, XchandlesExtendActionSolution,
8 XchandlesFactorPricingPuzzleArgs, XchandlesPricingSolution, XchandlesSlotValue,
9 },
10};
11use clvm_traits::{FromClvm, clvm_tuple};
12use clvm_utils::{ToTreeHash, TreeHash};
13use clvmr::NodePtr;
14
15use crate::{
16 DriverError, SingletonAction, Slot, Spend, SpendContext, XchandlesConstants, XchandlesRegistry,
17};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct XchandlesExtendAction {
21 pub launcher_id: Bytes32,
22 pub payout_puzzle_hash: Bytes32,
23}
24
25impl ToTreeHash for XchandlesExtendAction {
26 fn tree_hash(&self) -> TreeHash {
27 Self::new_args(self.launcher_id, self.payout_puzzle_hash).curry_tree_hash()
28 }
29}
30
31impl SingletonAction<XchandlesRegistry> for XchandlesExtendAction {
32 fn from_constants(constants: &XchandlesConstants) -> Self {
33 Self {
34 launcher_id: constants.launcher_id,
35 payout_puzzle_hash: constants.precommit_payout_puzzle_hash,
36 }
37 }
38}
39
40impl XchandlesExtendAction {
41 pub fn new_args(
42 launcher_id: Bytes32,
43 payout_puzzle_hash: Bytes32,
44 ) -> XchandlesExtendActionArgs {
45 XchandlesExtendActionArgs {
46 offer_mod_hash: SETTLEMENT_PAYMENT_HASH.into(),
47 payout_puzzle_hash,
48 slot_1st_curry_hash: Slot::<()>::first_curry_hash(launcher_id, 0).into(),
49 }
50 }
51
52 fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
53 ctx.curry(Self::new_args(self.launcher_id, self.payout_puzzle_hash))
54 }
55
56 pub fn spent_slot_value(
57 ctx: &mut SpendContext,
58 solution: NodePtr,
59 ) -> Result<XchandlesSlotValue, DriverError> {
60 let solution = ctx.extract::<XchandlesExtendActionSolution<
61 NodePtr,
62 (u64, (u64, (String, NodePtr))),
63 NodePtr,
64 NodePtr,
65 >>(solution)?;
66
67 let current_expiration = solution.pricing_solution.1.0;
69
70 Ok(XchandlesSlotValue::new(
71 solution.pricing_solution.1.1.0.tree_hash().into(),
72 solution.neighbors.left_value,
73 solution.neighbors.right_value,
74 current_expiration,
75 solution.rest.owner_launcher_id,
76 solution.rest.resolved_data,
77 ))
78 }
79
80 pub fn created_slot_value(
81 ctx: &mut SpendContext,
82 solution: NodePtr,
83 ) -> Result<XchandlesSlotValue, DriverError> {
84 let solution = ctx
85 .extract::<XchandlesExtendActionSolution<NodePtr, NodePtr, NodePtr, NodePtr>>(
86 solution,
87 )?;
88
89 let pricing_output = ctx.run(solution.pricing_puzzle_reveal, solution.pricing_solution)?;
90 let registration_time_delta = <(NodePtr, u64)>::from_clvm(ctx, pricing_output)?.1;
91
92 let (_, (_, (handle, _))) =
93 ctx.extract::<(NodePtr, (NodePtr, (String, NodePtr)))>(solution.pricing_solution)?;
94
95 let current_expiration = ctx
97 .extract::<(NodePtr, (u64, NodePtr))>(solution.pricing_solution)?
98 .1
99 .0;
100
101 Ok(XchandlesSlotValue::new(
102 handle.tree_hash().into(),
103 solution.neighbors.left_value,
104 solution.neighbors.right_value,
105 current_expiration + registration_time_delta,
106 solution.rest.owner_launcher_id,
107 solution.rest.resolved_data,
108 ))
109 }
110
111 #[allow(clippy::too_many_arguments)]
112 pub fn spend(
113 self,
114 ctx: &mut SpendContext,
115 registry: &mut XchandlesRegistry,
116 handle: String,
117 slot: Slot<XchandlesSlotValue>,
118 payment_asset_id: Bytes32,
119 base_handle_price: u64,
120 registration_period: u64,
121 num_periods: u64,
122 buy_time: u64,
123 ) -> Result<(Conditions, NotarizedPayment), DriverError> {
124 let spender_inner_puzzle_hash = registry.info.inner_puzzle_hash().into();
125
126 let cat_maker_puzzle_reveal = ctx.curry(DefaultCatMakerArgs::new(
128 payment_asset_id.tree_hash().into(),
129 ))?;
130 let pricing_puzzle_reveal = ctx.curry(XchandlesFactorPricingPuzzleArgs {
131 base_price: base_handle_price,
132 registration_period,
133 })?;
134
135 let slot = registry.actual_slot(slot);
136 let action_solution = ctx.alloc(&XchandlesExtendActionSolution {
137 pricing_puzzle_reveal,
138 pricing_solution: XchandlesPricingSolution {
139 buy_time,
140 current_expiration: slot.info.value.expiration,
141 handle: handle.clone(),
142 num_periods,
143 },
144 cat_maker_puzzle_reveal,
145 cat_maker_solution: (),
146 neighbors: slot.info.value.neighbors,
147 rest: slot.info.value.rest_data(),
148 })?;
149 let action_puzzle = self.construct_puzzle(ctx)?;
150
151 registry.insert_action_spend(ctx, Spend::new(action_puzzle, action_solution))?;
152
153 let renew_amount =
154 XchandlesFactorPricingPuzzleArgs::get_price(base_handle_price, &handle, num_periods);
155
156 let notarized_payment = NotarizedPayment {
157 nonce: clvm_tuple!(handle.clone(), slot.info.value.expiration)
158 .tree_hash()
159 .into(),
160 payments: vec![Payment::new(
161 registry.info.constants.precommit_payout_puzzle_hash,
162 renew_amount,
163 ctx.hint(registry.info.constants.precommit_payout_puzzle_hash)?,
164 )],
165 };
166
167 slot.spend(ctx, spender_inner_puzzle_hash)?;
169
170 let mut extend_ann = clvm_tuple!(renew_amount, handle).tree_hash().to_vec();
171 extend_ann.insert(0, b'e');
172
173 Ok((
174 Conditions::new()
175 .assert_puzzle_announcement(announcement_id(registry.coin.puzzle_hash, extend_ann)),
176 notarized_payment,
177 ))
178 }
179}