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