iota_rest_api/client/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5pub mod sdk;
6
7use iota_sdk2::types::EpochId;
8use iota_types::{
9    TypeTag,
10    base_types::{IotaAddress, ObjectID, SequenceNumber},
11    crypto::AuthorityStrongQuorumSignInfo,
12    effects::{TransactionEffects, TransactionEvents},
13    full_checkpoint_content::CheckpointData,
14    messages_checkpoint::{CertifiedCheckpointSummary, CheckpointSequenceNumber},
15    object::Object,
16    transaction::Transaction,
17};
18pub use reqwest;
19use sdk::Result;
20
21use self::sdk::Response;
22use crate::transactions::ExecuteTransactionQueryParameters;
23
24#[derive(Clone)]
25pub struct Client {
26    inner: sdk::Client,
27}
28
29impl Client {
30    pub fn new<S: AsRef<str>>(base_url: S) -> Self {
31        Self {
32            inner: sdk::Client::new(base_url.as_ref()).unwrap(),
33        }
34    }
35
36    pub fn inner(&self) -> &sdk::Client {
37        &self.inner
38    }
39
40    pub async fn get_latest_checkpoint(&self) -> Result<CertifiedCheckpointSummary> {
41        self.inner
42            .get_latest_checkpoint()
43            .await
44            .map(Response::into_inner)
45            .and_then(|checkpoint| checkpoint.try_into().map_err(Into::into))
46    }
47
48    pub async fn get_full_checkpoint(
49        &self,
50        checkpoint_sequence_number: CheckpointSequenceNumber,
51    ) -> Result<CheckpointData> {
52        let url = self
53            .inner
54            .url()
55            .join(&format!("checkpoints/{checkpoint_sequence_number}/full"))?;
56
57        let response = self
58            .inner
59            .client()
60            .get(url)
61            .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS)
62            .send()
63            .await?;
64
65        self.inner.bcs(response).await.map(Response::into_inner)
66    }
67
68    pub async fn get_checkpoint_summary(
69        &self,
70        checkpoint_sequence_number: CheckpointSequenceNumber,
71    ) -> Result<CertifiedCheckpointSummary> {
72        self.inner
73            .get_checkpoint(checkpoint_sequence_number)
74            .await
75            .map(Response::into_inner)
76            .and_then(|checkpoint| {
77                iota_sdk2::types::SignedCheckpointSummary {
78                    checkpoint: checkpoint.checkpoint,
79                    signature: checkpoint.signature,
80                }
81                .try_into()
82                .map_err(Into::into)
83            })
84    }
85
86    pub async fn get_object(&self, object_id: ObjectID) -> Result<Object> {
87        self.inner
88            .get_object(object_id.into())
89            .await
90            .map(Response::into_inner)
91            .and_then(|object| object.try_into().map_err(Into::into))
92    }
93
94    pub async fn get_object_with_version(
95        &self,
96        object_id: ObjectID,
97        version: SequenceNumber,
98    ) -> Result<Object> {
99        self.inner
100            .get_object_with_version(object_id.into(), version.into())
101            .await
102            .map(Response::into_inner)
103            .and_then(|object| object.try_into().map_err(Into::into))
104    }
105
106    pub async fn execute_transaction(
107        &self,
108        parameters: &ExecuteTransactionQueryParameters,
109        transaction: &Transaction,
110    ) -> Result<TransactionExecutionResponse> {
111        #[derive(serde::Serialize)]
112        struct SignedTransaction<'a> {
113            transaction: &'a iota_types::transaction::TransactionData,
114            signatures: &'a [iota_types::signature::GenericSignature],
115        }
116
117        let url = self.inner.url().join("transactions")?;
118        let body = bcs::to_bytes(&SignedTransaction {
119            transaction: &transaction.inner().intent_message.value,
120            signatures: &transaction.inner().tx_signatures,
121        })?;
122
123        let response = self
124            .inner
125            .client()
126            .post(url)
127            .query(parameters)
128            .header(reqwest::header::ACCEPT, crate::APPLICATION_BCS)
129            .header(reqwest::header::CONTENT_TYPE, crate::APPLICATION_BCS)
130            .body(body)
131            .send()
132            .await?;
133
134        self.inner.bcs(response).await.map(Response::into_inner)
135    }
136
137    pub async fn get_epoch_last_checkpoint(
138        &self,
139        epoch: EpochId,
140    ) -> Result<CertifiedCheckpointSummary> {
141        self.inner
142            .get_epoch_last_checkpoint(epoch)
143            .await
144            .map(Response::into_inner)
145            .and_then(|checkpoint| checkpoint.try_into().map_err(Into::into))
146    }
147}
148
149#[derive(Debug, serde::Serialize, serde::Deserialize)]
150pub struct TransactionExecutionResponse {
151    pub effects: TransactionEffects,
152
153    pub finality: EffectsFinality,
154    pub events: Option<TransactionEvents>,
155    pub balance_changes: Option<Vec<BalanceChange>>,
156    pub input_objects: Option<Vec<Object>>,
157    pub output_objects: Option<Vec<Object>>,
158}
159
160#[derive(Debug, serde::Serialize, serde::Deserialize)]
161pub enum EffectsFinality {
162    Certified {
163        signature: AuthorityStrongQuorumSignInfo,
164    },
165    Checkpointed {
166        checkpoint: CheckpointSequenceNumber,
167    },
168}
169
170#[derive(PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
171pub struct BalanceChange {
172    /// Owner of the balance change
173    pub address: IotaAddress,
174    /// Type of the Coin
175    pub coin_type: TypeTag,
176    /// The amount indicate the balance value changes,
177    /// negative amount means spending coin value and positive means receiving
178    /// coin value.
179    pub amount: i128,
180}