1#![allow(missing_docs)]
6
7extern crate alloc;
8
9use alloc::string::ToString;
10use alloy_primitives::{Address, Bytes, U256};
11use blueprint_std::vec::Vec;
12
13use crate::client::TangleClient;
14use crate::contracts::ITangleTypes;
15use crate::error::{Error, Result};
16
17#[derive(Debug, Clone)]
19pub struct ServiceInfo {
20 pub blueprint_id: u64,
21 pub owner: Address,
22 pub created_at: u64,
23 pub ttl: u64,
24 pub terminated_at: u64,
25 pub last_payment_at: u64,
26 pub operator_count: u32,
27 pub min_operators: u32,
28 pub max_operators: u32,
29 pub membership: MembershipModel,
30 pub pricing: PricingModel,
31 pub status: ServiceStatus,
32}
33
34#[derive(Debug, Clone)]
36pub struct BlueprintInfo {
37 pub owner: Address,
38 pub manager: Address,
39 pub created_at: u64,
40 pub operator_count: u32,
41 pub membership: MembershipModel,
42 pub pricing: PricingModel,
43 pub active: bool,
44}
45
46#[derive(Debug, Clone)]
48pub struct BlueprintConfig {
49 pub membership: MembershipModel,
50 pub pricing: PricingModel,
51 pub min_operators: u32,
52 pub max_operators: u32,
53 pub subscription_rate: U256,
54 pub subscription_interval: u64,
55 pub event_rate: U256,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum MembershipModel {
61 Fixed,
62 Dynamic,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum PricingModel {
68 PayOnce,
69 Subscription,
70 EventDriven,
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum ServiceStatus {
76 Pending,
77 Active,
78 Terminated,
79}
80
81#[derive(Debug, Clone)]
83pub struct ServiceRequestParams {
84 pub blueprint_id: u64,
86 pub operators: Vec<Address>,
88 pub operator_exposures: Option<Vec<u16>>,
90 pub permitted_callers: Vec<Address>,
92 pub config: Bytes,
94 pub ttl: u64,
96 pub payment_token: Address,
98 pub payment_amount: U256,
100 pub security_requirements: Vec<ITangleTypes::AssetSecurityRequirement>,
102}
103
104impl ServiceRequestParams {
105 pub fn new(
107 blueprint_id: u64,
108 operators: Vec<Address>,
109 permitted_callers: Vec<Address>,
110 config: Bytes,
111 ttl: u64,
112 payment_token: Address,
113 payment_amount: U256,
114 ) -> Self {
115 Self {
116 blueprint_id,
117 operators,
118 operator_exposures: None,
119 permitted_callers,
120 config,
121 ttl,
122 payment_token,
123 payment_amount,
124 security_requirements: Vec::new(),
125 }
126 }
127}
128
129#[derive(Debug, Clone)]
131pub struct ServiceRequestInfo {
132 pub request_id: u64,
134 pub blueprint_id: u64,
136 pub requester: Address,
138 pub created_at: u64,
140 pub ttl: u64,
142 pub operator_count: u32,
144 pub approval_count: u32,
146 pub payment_token: Address,
148 pub payment_amount: U256,
150 pub membership: MembershipModel,
152 pub min_operators: u32,
154 pub max_operators: u32,
156 pub rejected: bool,
158}
159
160impl ServiceRequestInfo {
161 fn from_contract(id: u64, request: ITangleTypes::ServiceRequest) -> Self {
162 Self {
163 request_id: id,
164 blueprint_id: request.blueprintId,
165 requester: request.requester,
166 created_at: request.createdAt,
167 ttl: request.ttl,
168 operator_count: request.operatorCount,
169 approval_count: request.approvalCount,
170 payment_token: request.paymentToken,
171 payment_amount: request.paymentAmount,
172 membership: ITangleTypes::MembershipModel::from_underlying(request.membership).into(),
173 min_operators: request.minOperators,
174 max_operators: request.maxOperators,
175 rejected: request.rejected,
176 }
177 }
178}
179
180impl From<ITangleTypes::MembershipModel> for MembershipModel {
181 fn from(model: ITangleTypes::MembershipModel) -> Self {
182 match model.into_underlying() {
183 0 => MembershipModel::Fixed,
184 1 => MembershipModel::Dynamic,
185 _ => MembershipModel::Fixed,
186 }
187 }
188}
189
190impl From<ITangleTypes::PricingModel> for PricingModel {
191 fn from(model: ITangleTypes::PricingModel) -> Self {
192 match model.into_underlying() {
193 0 => PricingModel::PayOnce,
194 1 => PricingModel::Subscription,
195 2 => PricingModel::EventDriven,
196 _ => PricingModel::PayOnce,
197 }
198 }
199}
200
201impl From<ITangleTypes::ServiceStatus> for ServiceStatus {
202 fn from(status: ITangleTypes::ServiceStatus) -> Self {
203 match status.into_underlying() {
204 0 => ServiceStatus::Pending,
205 1 => ServiceStatus::Active,
206 2 => ServiceStatus::Terminated,
207 _ => ServiceStatus::Pending,
208 }
209 }
210}
211
212impl TangleClient {
214 pub async fn get_service_info(&self, service_id: u64) -> Result<ServiceInfo> {
216 let result = self.get_service(service_id).await?;
217
218 Ok(ServiceInfo {
219 blueprint_id: result.blueprintId,
220 owner: result.owner,
221 created_at: result.createdAt,
222 ttl: result.ttl,
223 terminated_at: result.terminatedAt,
224 last_payment_at: result.lastPaymentAt,
225 operator_count: result.operatorCount,
226 min_operators: result.minOperators,
227 max_operators: result.maxOperators,
228 membership: ITangleTypes::MembershipModel::from_underlying(result.membership).into(),
229 pricing: ITangleTypes::PricingModel::from_underlying(result.pricing).into(),
230 status: ITangleTypes::ServiceStatus::from_underlying(result.status).into(),
231 })
232 }
233
234 pub async fn get_blueprint_info(&self, blueprint_id: u64) -> Result<BlueprintInfo> {
236 let result = self.get_blueprint(blueprint_id).await?;
237
238 Ok(BlueprintInfo {
239 owner: result.owner,
240 manager: result.manager,
241 created_at: result.createdAt,
242 operator_count: result.operatorCount,
243 membership: ITangleTypes::MembershipModel::from_underlying(result.membership).into(),
244 pricing: ITangleTypes::PricingModel::from_underlying(result.pricing).into(),
245 active: result.active,
246 })
247 }
248
249 pub async fn get_blueprint_config_info(&self, blueprint_id: u64) -> Result<BlueprintConfig> {
251 let result = self.get_blueprint_config(blueprint_id).await?;
252
253 Ok(BlueprintConfig {
254 membership: ITangleTypes::MembershipModel::from_underlying(result.membership).into(),
255 pricing: ITangleTypes::PricingModel::from_underlying(result.pricing).into(),
256 min_operators: result.minOperators,
257 max_operators: result.maxOperators,
258 subscription_rate: result.subscriptionRate,
259 subscription_interval: result.subscriptionInterval,
260 event_rate: result.eventRate,
261 })
262 }
263
264 pub async fn get_operator_services(&self, operator: Address) -> Result<Vec<u64>> {
268 let contract = self.tangle_contract();
269 let service_count: u64 = contract
270 .serviceCount()
271 .call()
272 .await
273 .map_err(|e| Error::Contract(e.to_string()))?;
274
275 let mut services = Vec::new();
276
277 for service_id in 0..service_count {
278 if self.is_service_operator(service_id, operator).await? {
279 services.push(service_id);
280 }
281 }
282
283 Ok(services)
284 }
285
286 pub async fn is_registered_for_blueprint(&self) -> Result<bool> {
288 let blueprint_id = self.config.settings.blueprint_id;
289 self.is_operator_registered(blueprint_id, self.account())
290 .await
291 }
292
293 pub async fn is_operator_active_in_restaking(&self) -> Result<bool> {
295 self.is_operator_active(self.account()).await
296 }
297
298 pub async fn get_own_stake(&self) -> Result<U256> {
300 self.get_operator_stake(self.account()).await
301 }
302
303 pub async fn list_blueprints(&self) -> Result<Vec<(u64, BlueprintInfo)>> {
305 let total = self.blueprint_count().await?;
306 let capacity = usize::try_from(total).unwrap_or(usize::MAX);
307 let mut blueprints = Vec::with_capacity(capacity);
308
309 for blueprint_id in 0..total {
310 match self.get_blueprint_info(blueprint_id).await {
311 Ok(info) => blueprints.push((blueprint_id, info)),
312 Err(err) => {
313 tracing::warn!(
314 %blueprint_id,
315 error = %err,
316 "failed to fetch blueprint info"
317 );
318 }
319 }
320 }
321
322 Ok(blueprints)
323 }
324
325 pub async fn list_services(&self) -> Result<Vec<(u64, ServiceInfo)>> {
327 let total = self.service_count().await?;
328 let capacity = usize::try_from(total).unwrap_or(usize::MAX);
329 let mut services = Vec::with_capacity(capacity);
330
331 for service_id in 0..total {
332 match self.get_service_info(service_id).await {
333 Ok(info) => services.push((service_id, info)),
334 Err(err) => {
335 tracing::warn!(
336 %service_id,
337 error = %err,
338 "failed to fetch service info"
339 );
340 }
341 }
342 }
343
344 Ok(services)
345 }
346
347 pub async fn get_service_request_info(&self, request_id: u64) -> Result<ServiceRequestInfo> {
349 let request = self.get_service_request(request_id).await?;
350 Ok(ServiceRequestInfo::from_contract(request_id, request))
351 }
352
353 pub async fn list_service_requests(&self) -> Result<Vec<ServiceRequestInfo>> {
355 let total = self.service_request_count().await?;
356 let capacity = usize::try_from(total).unwrap_or(usize::MAX);
357 let mut requests = Vec::with_capacity(capacity);
358
359 for request_id in 0..total {
360 match self.get_service_request(request_id).await {
361 Ok(info) => requests.push(ServiceRequestInfo::from_contract(request_id, info)),
362 Err(err) => {
363 tracing::warn!(
364 %request_id,
365 error = %err,
366 "failed to fetch service request"
367 );
368 }
369 }
370 }
371
372 Ok(requests)
373 }
374}
375
376#[derive(Debug, Clone)]
378pub struct OperatorSecurityCommitment {
379 pub operator: Address,
380 pub exposure_bps: u16,
381}
382
383impl TangleClient {
384 pub async fn get_service_operators_with_exposure(
386 &self,
387 service_id: u64,
388 ) -> Result<Vec<OperatorSecurityCommitment>> {
389 let operators = self.get_service_operators(service_id).await?;
390
391 Ok(operators
394 .into_iter()
395 .map(|operator| OperatorSecurityCommitment {
396 operator,
397 exposure_bps: 10000, })
399 .collect())
400 }
401}