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