audit_trails/client/
read_only.rs1use std::ops::Deref;
11
12#[cfg(not(target_arch = "wasm32"))]
13use iota_interaction::IotaClient;
14use iota_interaction::IotaClientTrait;
15use iota_interaction::types::base_types::{IotaAddress, ObjectID};
16use iota_interaction::types::transaction::{ProgrammableTransaction, TransactionKind};
17#[cfg(target_arch = "wasm32")]
18use iota_interaction_ts::bindings::WasmIotaClient;
19use product_common::core_client::CoreClientReadOnly;
20use product_common::network_name::NetworkName;
21use serde::de::DeserializeOwned;
22
23use super::network_id;
24use crate::core::trail::{AuditTrailHandle, AuditTrailReadOnly};
25use crate::error::Error;
26use crate::iota_interaction_adapter::IotaClientAdapter;
27use crate::package;
28
29#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
34pub struct PackageOverrides {
35 pub audit_trail: Option<ObjectID>,
37 pub tf_component: Option<ObjectID>,
39}
40
41#[derive(Clone)]
49pub struct AuditTrailClientReadOnly {
50 iota_client: IotaClientAdapter,
52 audit_trail_pkg_id: ObjectID,
54 pub(crate) tf_components_pkg_id: ObjectID,
56 network: NetworkName,
58 chain_id: String,
60}
61
62impl Deref for AuditTrailClientReadOnly {
63 type Target = IotaClientAdapter;
64 fn deref(&self) -> &Self::Target {
65 &self.iota_client
66 }
67}
68
69impl AuditTrailClientReadOnly {
70 pub const fn network(&self) -> &NetworkName {
72 &self.network
73 }
74
75 pub fn chain_id(&self) -> &str {
77 &self.chain_id
78 }
79
80 pub fn package_id(&self) -> ObjectID {
84 self.audit_trail_pkg_id
85 }
86
87 pub fn tf_components_package_id(&self) -> ObjectID {
89 self.tf_components_pkg_id
90 }
91
92 pub const fn iota_client(&self) -> &IotaClientAdapter {
94 &self.iota_client
95 }
96
97 pub fn trail<'a>(&'a self, trail_id: ObjectID) -> AuditTrailHandle<'a, Self> {
102 AuditTrailHandle::new(self, trail_id)
103 }
104
105 pub async fn new(
116 #[cfg(target_arch = "wasm32")] iota_client: WasmIotaClient,
117 #[cfg(not(target_arch = "wasm32"))] iota_client: IotaClient,
118 ) -> Result<Self, Error> {
119 let client = IotaClientAdapter::new(iota_client);
120 let network = network_id(&client).await?;
121 Self::new_internal(client, network, PackageOverrides::default()).await
122 }
123
124 async fn new_internal(
125 iota_client: IotaClientAdapter,
126 network: NetworkName,
127 package_overrides: PackageOverrides,
128 ) -> Result<Self, Error> {
129 let chain_id = network.as_ref().to_string();
130 let (network, package_ids) = package::resolve_package_ids(&network, &package_overrides).await?;
131
132 Ok(Self {
133 iota_client,
134 audit_trail_pkg_id: package_ids.audit_trail_package_id,
135 tf_components_pkg_id: package_ids.tf_components_package_id,
136 network,
137 chain_id,
138 })
139 }
140
141 pub async fn new_with_package_overrides(
154 #[cfg(target_arch = "wasm32")] iota_client: WasmIotaClient,
155 #[cfg(not(target_arch = "wasm32"))] iota_client: IotaClient,
156 package_overrides: PackageOverrides,
157 ) -> Result<Self, Error> {
158 let client = IotaClientAdapter::new(iota_client);
159 let network = network_id(&client).await?;
160 Self::new_internal(client, network, package_overrides).await
161 }
162}
163
164#[cfg_attr(not(feature = "send-sync"), async_trait::async_trait(?Send))]
165#[cfg_attr(feature = "send-sync", async_trait::async_trait)]
166impl CoreClientReadOnly for AuditTrailClientReadOnly {
167 fn package_id(&self) -> ObjectID {
168 self.audit_trail_pkg_id
169 }
170
171 fn tf_components_package_id(&self) -> Option<ObjectID> {
172 Some(self.tf_components_pkg_id)
173 }
174
175 fn network_name(&self) -> &NetworkName {
176 &self.network
177 }
178
179 fn client_adapter(&self) -> &IotaClientAdapter {
180 &self.iota_client
181 }
182}
183
184#[cfg_attr(not(feature = "send-sync"), async_trait::async_trait(?Send))]
185#[cfg_attr(feature = "send-sync", async_trait::async_trait)]
186impl AuditTrailReadOnly for AuditTrailClientReadOnly {
187 async fn execute_read_only_transaction<T: DeserializeOwned>(
192 &self,
193 tx: ProgrammableTransaction,
194 ) -> Result<T, Error> {
195 let inspection_result = self
196 .iota_client
197 .read_api()
198 .dev_inspect_transaction_block(IotaAddress::ZERO, TransactionKind::Programmable(tx), None, None, None)
199 .await
200 .map_err(|err| Error::UnexpectedApiResponse(format!("Failed to inspect transaction block: {err}")))?;
201
202 let execution_results = inspection_result
203 .results
204 .ok_or_else(|| Error::UnexpectedApiResponse("DevInspectResults missing 'results' field".to_string()))?;
205
206 let (return_value_bytes, _) = execution_results
207 .first()
208 .ok_or_else(|| Error::UnexpectedApiResponse("Execution results list is empty".to_string()))?
209 .return_values
210 .first()
211 .ok_or_else(|| Error::InvalidArgument("should have at least one return value".to_string()))?;
212
213 let deserialized_output = bcs::from_bytes::<T>(return_value_bytes)?;
214
215 Ok(deserialized_output)
216 }
217}