cargo_tangle/command/service/
request.rs1use color_eyre::Result;
2use dialoguer::console::style;
3use blueprint_clients::tangle::client::OnlineClient;
4use blueprint_crypto::sp_core::SpSr25519;
5use blueprint_crypto::tangle_pair_signer::TanglePairSigner;
6use blueprint_keystore::{Keystore, KeystoreConfig};
7use tangle_subxt::tangle_testnet_runtime::api;
8use tangle_subxt::tangle_testnet_runtime::api::runtime_types::sp_arithmetic::per_things::Percent;
9use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::types::{
10 Asset, AssetSecurityRequirement, MembershipModel,
11};
12use blueprint_core::info;
13use crate::command::jobs::helpers::{load_job_args_from_file, prompt_for_job_params};
14use crate::wait_for_in_block_success;
15use tangle_subxt::subxt::utils::AccountId32;
16use blueprint_keystore::backends::Backend;
17
18#[allow(clippy::too_many_arguments)]
43pub async fn request_service(
44 ws_rpc_url: impl AsRef<str>,
45 blueprint_id: u64,
46 min_exposure_percent: u8,
47 max_exposure_percent: u8,
48 target_operators: Vec<AccountId32>,
49 value: u128,
50 keystore_uri: String,
51 params_file: Option<String>,
52 ) -> Result<()> {
54 let client = OnlineClient::from_url(ws_rpc_url.as_ref()).await?;
55
56 let config = KeystoreConfig::new().fs_root(keystore_uri.clone());
57 let keystore = Keystore::new(config).expect("Failed to create keystore");
58 let public = keystore.first_local::<SpSr25519>().unwrap();
59 let pair = keystore.get_secret::<SpSr25519>(&public).unwrap();
60 let signer = TanglePairSigner::new(pair.0);
61
62 let blueprint_key = api::storage().services().blueprints(blueprint_id);
63 let maybe_blueprint = client
64 .storage()
65 .at_latest()
66 .await?
67 .fetch(&blueprint_key)
68 .await?;
69 let Some((_blueprint_owner, blueprint)) = maybe_blueprint else {
70 return Err(color_eyre::eyre::eyre!(
71 "Blueprint ID {} not found",
72 blueprint_id
73 ));
74 };
75
76 let min_operators = u32::try_from(target_operators.len())
77 .map_err(|_| color_eyre::eyre::eyre!("Too many operators"))?;
78 let security_requirements = vec![AssetSecurityRequirement {
79 asset: Asset::Custom(0),
80 min_exposure_percent: Percent(min_exposure_percent),
81 max_exposure_percent: Percent(max_exposure_percent),
82 }];
83
84 println!(
85 "{}",
86 style(format!(
87 "Preparing service request for blueprint ID: {}",
88 blueprint_id
89 ))
90 .cyan()
91 );
92 println!(
93 "{}",
94 style(format!(
95 "Target operators: {} (min: {})",
96 target_operators.len(),
97 min_operators
98 ))
99 .dim()
100 );
101 println!(
102 "{}",
103 style(format!(
104 "Exposure range: {}% - {}%",
105 min_exposure_percent, max_exposure_percent
106 ))
107 .dim()
108 );
109
110 let request_args = if blueprint.request_params.0.is_empty() {
112 Vec::new()
114 } else if let Some(file_path) = params_file {
115 info!(
116 "Request params definition: {:?}",
117 blueprint.request_params.0
118 );
119
120 load_job_args_from_file(&file_path, &blueprint.request_params.0)?
122 } else {
123 info!(
124 "Request params definition: {:?}",
125 blueprint.request_params.0
126 );
127 prompt_for_job_params(&blueprint.request_params.0)?
129 };
130
131 let call = api::tx().services().request(
132 None,
133 blueprint_id,
134 Vec::new(),
135 target_operators,
136 request_args,
137 security_requirements,
138 1000,
139 Asset::Custom(0),
140 value,
141 MembershipModel::Fixed { min_operators },
142 );
143
144 println!("{}", style("Submitting Service Request...").cyan());
145 let res = client
146 .tx()
147 .sign_and_submit_then_watch_default(&call, &signer)
148 .await?;
149 let events = wait_for_in_block_success(res).await;
150
151 let service_request_event = events
152 .find_first::<api::services::events::ServiceRequested>()
153 .map_err(|e| color_eyre::eyre::eyre!("Service request failed: {e}"))?
154 .ok_or_else(|| color_eyre::eyre::eyre!("Service request event not found"))?;
155
156 println!(
157 "{}",
158 style("Service Request submitted successfully").green()
159 );
160
161 println!(
162 "{}",
163 style(format!(
164 "Service Request ID: {}",
165 service_request_event.request_id
166 ))
167 .green()
168 .bold()
169 );
170
171 Ok(())
172}