quantus_cli/cli/
scheduler.rs1use crate::{chain::quantus_subxt, error::Result, log_print, log_success};
2use clap::Subcommand;
3
4#[derive(Subcommand, Debug)]
6pub enum SchedulerCommands {
7 GetLastProcessedTimestamp,
9
10 Agenda {
12 #[arg(long)]
14 range: String,
15 },
16
17 ScheduleRemark {
19 #[arg(long)]
21 after: u32,
22 #[arg(long)]
24 from: String,
25 },
26}
27
28pub async fn get_last_processed_timestamp(
30 quantus_client: &crate::chain::client::QuantusClient,
31) -> Result<Option<u64>> {
32 use quantus_subxt::api;
33
34 log_print!("🕒 Getting last processed timestamp from the scheduler");
35
36 let storage_addr = api::storage().scheduler().last_processed_timestamp();
38
39 let latest_block_hash = quantus_client.get_latest_block().await?;
41
42 let storage_at = quantus_client.client().storage().at(latest_block_hash);
43
44 let timestamp = storage_at.fetch(&storage_addr).await.map_err(|e| {
45 crate::error::QuantusError::NetworkError(format!(
46 "Failed to fetch last processed timestamp: {e:?}"
47 ))
48 })?;
49
50 Ok(timestamp)
51}
52
53async fn list_agenda_range(
54 quantus_client: &crate::chain::client::QuantusClient,
55 range: &str,
56) -> Result<()> {
57 use quantus_subxt::api;
58
59 let parts: Vec<&str> = range.split("..").collect();
61 if parts.len() != 2 {
62 return Err(crate::error::QuantusError::Generic(
63 "Invalid range format. Use --range <from>..<to>".to_string(),
64 ));
65 }
66 let start: u32 = parts[0].trim().parse().map_err(|_| {
67 crate::error::QuantusError::Generic("Invalid start block in range".to_string())
68 })?;
69 let end: u32 = parts[1].trim().parse().map_err(|_| {
70 crate::error::QuantusError::Generic("Invalid end block in range".to_string())
71 })?;
72 if start > end {
73 return Err(crate::error::QuantusError::Generic("Range start must be <= end".to_string()));
74 }
75
76 let latest_block_hash = quantus_client.get_latest_block().await?;
78 let storage_at = quantus_client.client().storage().at(latest_block_hash);
79
80 log_print!("🗓️ Scheduler::Agenda entries for blocks {}..={} (inclusive)", start, end);
81
82 for bn in start..=end {
83 let addr = api::storage().scheduler().agenda(
84 quantus_subxt::api::runtime_types::qp_scheduler::BlockNumberOrTimestamp::BlockNumber(
85 bn,
86 ),
87 );
88 match storage_at.fetch(&addr).await {
89 Ok(Some(agenda)) => {
90 log_print!("#{}: {:?}", bn, agenda);
91 },
92 Ok(None) => {
93 log_print!("#{}: <empty>", bn);
94 },
95 Err(e) => {
96 log_print!("#{}: error fetching agenda: {:?}", bn, e);
97 },
98 }
99 }
100
101 log_success!("Finished scanning Scheduler::Agenda");
102 Ok(())
103}
104
105async fn schedule_remark(
106 quantus_client: &crate::chain::client::QuantusClient,
107 after: u32,
108 from: &str,
109) -> Result<()> {
110 use quantus_subxt::api;
111
112 log_print!("🗓️ Scheduling System::remark after {} blocks", after);
113
114 let system_remark = quantus_subxt::api::runtime_types::frame_system::pallet::Call::remark {
116 remark: Vec::new(),
117 };
118 let runtime_call =
119 quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall::System(system_remark);
120
121 let when_u32: u32 = after;
123 let maybe_periodic = None;
124 let priority: u8 = 0;
125
126 let keypair = crate::wallet::load_keypair_from_wallet(from, None, None)?;
128 let schedule_tx =
129 api::tx().scheduler().schedule(when_u32, maybe_periodic, priority, runtime_call);
130 let tx_hash =
131 crate::cli::common::submit_transaction(quantus_client, &keypair, schedule_tx, None).await?;
132 log_success!("📩 Schedule extrinsic submitted: {:?}", tx_hash);
133
134 let _ =
136 crate::cli::progress_spinner::wait_for_tx_confirmation(quantus_client.client(), tx_hash)
137 .await?;
138 log_success!("✅ Schedule confirmed");
139
140 Ok(())
141}
142
143pub async fn handle_scheduler_command(command: SchedulerCommands, node_url: &str) -> Result<()> {
145 log_print!("🗓️ Scheduler");
146
147 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
148
149 match command {
150 SchedulerCommands::GetLastProcessedTimestamp => {
151 match get_last_processed_timestamp(&quantus_client).await? {
152 Some(timestamp) => {
153 log_success!("🎉 Last processed timestamp: {}", timestamp);
154 },
155 None => {
156 log_print!(
157 "🤷 No last processed timestamp found. The scheduler may not have run yet."
158 );
159 },
160 }
161 Ok(())
162 },
163 SchedulerCommands::Agenda { range } => list_agenda_range(&quantus_client, &range).await,
164 SchedulerCommands::ScheduleRemark { after, from } =>
165 schedule_remark(&quantus_client, after, &from).await,
166 }
167}