penumbra_sdk_auction/component/action_handler/dutch/
schedule.rs1use crate::auction::dutch::actions::schedule::MAX_AUCTION_AMOUNT_RESERVES;
2use crate::auction::dutch::DutchAuctionDescription;
3use crate::component::AuctionStoreRead;
4use anyhow::{ensure, Result};
5use async_trait::async_trait;
6use cnidarium::StateWrite;
7use cnidarium_component::ActionHandler;
8use penumbra_sdk_num::Amount;
9use penumbra_sdk_sct::component::clock::EpochRead;
10
11use crate::auction::dutch::ActionDutchAuctionSchedule;
12use crate::component::DutchAuctionManager;
13
14#[async_trait]
15impl ActionHandler for ActionDutchAuctionSchedule {
16 type CheckStatelessContext = ();
17 async fn check_stateless(&self, _context: ()) -> Result<()> {
18 let DutchAuctionDescription {
19 input,
20 output_id,
21 max_output,
22 min_output,
23 start_height,
24 end_height,
25 step_count,
26 nonce: _,
27 } = self.description;
28
29 ensure!(
31 input.amount > Amount::zero(),
32 "input amount MUST be positive (got zero)"
33 );
34
35 ensure!(step_count > 0, "step count MUST be positive (got zero)");
37
38 ensure!(
40 input.amount <= MAX_AUCTION_AMOUNT_RESERVES.into(),
41 "input amount MUST be less than 52 bits wide"
42 );
43
44 ensure!(
46 input.asset_id != output_id,
47 "input id MUST be different from output id"
48 );
49
50 ensure!(
52 max_output > min_output,
53 "max_output MUST be greater than min_output"
54 );
55
56 ensure!(max_output > 0u128.into(), "max output MUST be positive");
58
59 ensure!(
61 max_output <= MAX_AUCTION_AMOUNT_RESERVES.into(),
62 "max output amount MUST be less than 52 bits wide"
63 );
64
65 ensure!(min_output > 0u128.into(), "min output MUST be positive");
67
68 ensure!(
70 min_output <= MAX_AUCTION_AMOUNT_RESERVES.into(),
71 "min output amount MUST be less than 52 bits wide"
72 );
73
74 ensure!(
76 start_height < end_height,
77 "the start height MUST be strictly less than the end height (got: start={} >= end={})",
78 start_height,
79 end_height
80 );
81
82 ensure!(
85 step_count >= 2,
86 "step count MUST be at least two (got: {step_count})"
87 );
88
89 ensure!(
91 step_count <= 255,
92 "the dutch auction step count MUST be less than 255 (got: {step_count})",
93 );
94
95 let block_window = end_height.checked_sub(start_height).ok_or_else(|| {
97 anyhow::anyhow!(
98 "underflow ({end_height} < {start_height}) - the validation rules are incoherent!"
99 )
100 })?;
101 ensure!(
102 (block_window % step_count) == 0,
103 "the block window ({block_window}) MUST be a multiple of the step count ({step_count})"
104 );
105
106 Ok(())
107 }
108
109 async fn check_and_execute<S: StateWrite>(&self, mut state: S) -> Result<()> {
110 let schedule = self;
111
112 let current_height = state.get_block_height().await?;
114 let start_height = schedule.description.start_height;
115 ensure!(
116 start_height > current_height,
117 "dutch auction MUST start in the future (start={}, current={})",
118 start_height,
119 current_height
120 );
121
122 let id = schedule.description.id();
124 ensure!(
125 !state.auction_id_exists(id).await,
126 "the supplied auction id is already known to the chain (id={id})"
127 );
128
129 state.schedule_auction(schedule.description.clone()).await?;
130 Ok(())
131 }
132}