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 let operator_count = self.get_blueprint_operator_count(blueprint_id).await?;
240
241 Ok(BlueprintInfo {
242 owner: result.owner,
243 manager: result.manager,
244 created_at: result.createdAt,
245 operator_count,
246 membership: ITangleTypes::MembershipModel::from_underlying(result.membership).into(),
247 pricing: ITangleTypes::PricingModel::from_underlying(result.pricing).into(),
248 active: result.active,
249 })
250 }
251
252 pub async fn get_blueprint_config_info(&self, blueprint_id: u64) -> Result<BlueprintConfig> {
254 let result = self.get_blueprint_config(blueprint_id).await?;
255
256 Ok(BlueprintConfig {
257 membership: ITangleTypes::MembershipModel::from_underlying(result.membership).into(),
258 pricing: ITangleTypes::PricingModel::from_underlying(result.pricing).into(),
259 min_operators: result.minOperators,
260 max_operators: result.maxOperators,
261 subscription_rate: result.subscriptionRate,
262 subscription_interval: result.subscriptionInterval,
263 event_rate: result.eventRate,
264 })
265 }
266
267 pub async fn get_operator_services(&self, operator: Address) -> Result<Vec<u64>> {
271 let contract = self.tangle_contract();
272 let service_count: u64 = contract
273 .serviceCount()
274 .call()
275 .await
276 .map_err(|e| Error::Contract(e.to_string()))?;
277
278 let mut services = Vec::new();
279
280 for service_id in 0..service_count {
281 if self.is_service_operator(service_id, operator).await? {
282 services.push(service_id);
283 }
284 }
285
286 Ok(services)
287 }
288
289 pub async fn is_registered_for_blueprint(&self) -> Result<bool> {
291 let blueprint_id = self.config.settings.blueprint_id;
292 self.is_operator_registered(blueprint_id, self.account())
293 .await
294 }
295
296 pub async fn is_operator_active_in_restaking(&self) -> Result<bool> {
298 self.is_operator_active(self.account()).await
299 }
300
301 pub async fn get_own_stake(&self) -> Result<U256> {
303 self.get_operator_stake(self.account()).await
304 }
305
306 pub async fn list_blueprints(&self) -> Result<Vec<(u64, BlueprintInfo)>> {
308 let total = self.blueprint_count().await?;
309 let capacity = usize::try_from(total).unwrap_or(usize::MAX);
310 let mut blueprints = Vec::with_capacity(capacity);
311
312 for blueprint_id in 0..total {
313 match self.get_blueprint_info(blueprint_id).await {
314 Ok(info) => blueprints.push((blueprint_id, info)),
315 Err(err) => {
316 tracing::warn!(
317 %blueprint_id,
318 error = %err,
319 "failed to fetch blueprint info"
320 );
321 }
322 }
323 }
324
325 Ok(blueprints)
326 }
327
328 pub async fn list_services(&self) -> Result<Vec<(u64, ServiceInfo)>> {
330 let total = self.service_count().await?;
331 let capacity = usize::try_from(total).unwrap_or(usize::MAX);
332 let mut services = Vec::with_capacity(capacity);
333
334 for service_id in 0..total {
335 match self.get_service_info(service_id).await {
336 Ok(info) => services.push((service_id, info)),
337 Err(err) => {
338 tracing::warn!(
339 %service_id,
340 error = %err,
341 "failed to fetch service info"
342 );
343 }
344 }
345 }
346
347 Ok(services)
348 }
349
350 pub async fn get_service_request_info(&self, request_id: u64) -> Result<ServiceRequestInfo> {
352 let request = self.get_service_request(request_id).await?;
353 Ok(ServiceRequestInfo::from_contract(request_id, request))
354 }
355
356 pub async fn list_service_requests(&self) -> Result<Vec<ServiceRequestInfo>> {
358 let total = self.service_request_count().await?;
359 let capacity = usize::try_from(total).unwrap_or(usize::MAX);
360 let mut requests = Vec::with_capacity(capacity);
361
362 for request_id in 0..total {
363 match self.get_service_request(request_id).await {
364 Ok(info) => requests.push(ServiceRequestInfo::from_contract(request_id, info)),
365 Err(err) => {
366 tracing::warn!(
367 %request_id,
368 error = %err,
369 "failed to fetch service request"
370 );
371 }
372 }
373 }
374
375 Ok(requests)
376 }
377}
378
379#[derive(Debug, Clone)]
381pub struct OperatorSecurityCommitment {
382 pub operator: Address,
383 pub exposure_bps: u16,
384}
385
386impl TangleClient {
387 pub async fn get_service_operators_with_exposure(
389 &self,
390 service_id: u64,
391 ) -> Result<Vec<OperatorSecurityCommitment>> {
392 let operators = self.get_service_operators(service_id).await?;
393
394 Ok(operators
397 .into_iter()
398 .map(|operator| OperatorSecurityCommitment {
399 operator,
400 exposure_bps: 10000, })
402 .collect())
403 }
404}