iota_json_rpc/
transaction_builder_api.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::sync::Arc;
6
7use async_trait::async_trait;
8use fastcrypto::encoding::Base64;
9use iota_core::authority::AuthorityState;
10use iota_json::IotaJsonValue;
11use iota_json_rpc_api::{TransactionBuilderOpenRpc, TransactionBuilderServer, internal_error};
12use iota_json_rpc_types::{
13    IotaObjectDataFilter, IotaObjectDataOptions, IotaObjectResponse,
14    IotaTransactionBlockBuilderMode, IotaTypeTag, RPCTransactionRequestParams,
15    TransactionBlockBytes,
16};
17use iota_open_rpc::Module;
18use iota_transaction_builder::{DataReader, TransactionBuilder};
19use iota_types::{
20    base_types::{IotaAddress, ObjectID, ObjectInfo},
21    iota_serde::BigInt,
22};
23use jsonrpsee::{RpcModule, core::RpcResult};
24use move_core_types::language_storage::StructTag;
25
26use crate::{IotaRpcModule, authority_state::StateRead};
27
28pub struct TransactionBuilderApi(TransactionBuilder);
29
30impl TransactionBuilderApi {
31    pub fn new(state: Arc<AuthorityState>) -> Self {
32        let reader = Arc::new(AuthorityStateDataReader::new(state));
33        Self(TransactionBuilder::new(reader))
34    }
35
36    pub fn new_with_data_reader(data_reader: Arc<dyn DataReader + Sync + Send>) -> Self {
37        Self(TransactionBuilder::new(data_reader))
38    }
39}
40
41pub struct AuthorityStateDataReader(Arc<dyn StateRead>);
42
43impl AuthorityStateDataReader {
44    pub fn new(state: Arc<AuthorityState>) -> Self {
45        Self(state)
46    }
47}
48
49#[async_trait]
50impl DataReader for AuthorityStateDataReader {
51    async fn get_owned_objects(
52        &self,
53        address: IotaAddress,
54        object_type: StructTag,
55    ) -> Result<Vec<ObjectInfo>, anyhow::Error> {
56        Ok(self
57            .0
58            // DataReader is used internally, don't need a limit
59            .get_owner_objects(
60                address,
61                None,
62                Some(IotaObjectDataFilter::StructType(object_type)),
63            )?)
64    }
65
66    async fn get_object_with_options(
67        &self,
68        object_id: ObjectID,
69        options: IotaObjectDataOptions,
70    ) -> Result<IotaObjectResponse, anyhow::Error> {
71        let result = self.0.get_object_read(&object_id)?;
72        Ok((result, options).try_into()?)
73    }
74
75    async fn get_reference_gas_price(&self) -> Result<u64, anyhow::Error> {
76        let epoch_store = self.0.load_epoch_store_one_call_per_task();
77        Ok(epoch_store.reference_gas_price())
78    }
79}
80
81#[async_trait]
82impl TransactionBuilderServer for TransactionBuilderApi {
83    async fn transfer_object(
84        &self,
85        signer: IotaAddress,
86        object_id: ObjectID,
87        gas: Option<ObjectID>,
88        gas_budget: BigInt<u64>,
89        recipient: IotaAddress,
90    ) -> RpcResult<TransactionBlockBytes> {
91        let data = self
92            .0
93            .transfer_object(signer, object_id, gas, *gas_budget, recipient)
94            .await
95            .map_err(internal_error)?;
96        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
97    }
98
99    async fn transfer_iota(
100        &self,
101        signer: IotaAddress,
102        iota_object_id: ObjectID,
103        gas_budget: BigInt<u64>,
104        recipient: IotaAddress,
105        amount: Option<BigInt<u64>>,
106    ) -> RpcResult<TransactionBlockBytes> {
107        let data = self
108            .0
109            .transfer_iota(
110                signer,
111                iota_object_id,
112                *gas_budget,
113                recipient,
114                amount.map(|a| *a),
115            )
116            .await
117            .map_err(internal_error)?;
118        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
119    }
120
121    async fn pay(
122        &self,
123        signer: IotaAddress,
124        input_coins: Vec<ObjectID>,
125        recipients: Vec<IotaAddress>,
126        amounts: Vec<BigInt<u64>>,
127        gas: Option<ObjectID>,
128        gas_budget: BigInt<u64>,
129    ) -> RpcResult<TransactionBlockBytes> {
130        let data = self
131            .0
132            .pay(
133                signer,
134                input_coins,
135                recipients,
136                amounts.into_iter().map(|a| *a).collect(),
137                gas,
138                *gas_budget,
139            )
140            .await
141            .map_err(internal_error)?;
142        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
143    }
144
145    async fn pay_iota(
146        &self,
147        signer: IotaAddress,
148        input_coins: Vec<ObjectID>,
149        recipients: Vec<IotaAddress>,
150        amounts: Vec<BigInt<u64>>,
151        gas_budget: BigInt<u64>,
152    ) -> RpcResult<TransactionBlockBytes> {
153        let data = self
154            .0
155            .pay_iota(
156                signer,
157                input_coins,
158                recipients,
159                amounts.into_iter().map(|a| *a).collect(),
160                *gas_budget,
161            )
162            .await
163            .map_err(internal_error)?;
164        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
165    }
166
167    async fn pay_all_iota(
168        &self,
169        signer: IotaAddress,
170        input_coins: Vec<ObjectID>,
171        recipient: IotaAddress,
172        gas_budget: BigInt<u64>,
173    ) -> RpcResult<TransactionBlockBytes> {
174        let data = self
175            .0
176            .pay_all_iota(signer, input_coins, recipient, *gas_budget)
177            .await
178            .map_err(internal_error)?;
179        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
180    }
181
182    async fn publish(
183        &self,
184        sender: IotaAddress,
185        compiled_modules: Vec<Base64>,
186        dependencies: Vec<ObjectID>,
187        gas: Option<ObjectID>,
188        gas_budget: BigInt<u64>,
189    ) -> RpcResult<TransactionBlockBytes> {
190        let compiled_modules = compiled_modules
191            .into_iter()
192            .map(|data| data.to_vec().map_err(|e| anyhow::anyhow!(e)))
193            .collect::<Result<Vec<_>, _>>()
194            .map_err(internal_error)?;
195        let data = self
196            .0
197            .publish(sender, compiled_modules, dependencies, gas, *gas_budget)
198            .await
199            .map_err(internal_error)?;
200        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
201    }
202
203    async fn split_coin(
204        &self,
205        signer: IotaAddress,
206        coin_object_id: ObjectID,
207        split_amounts: Vec<BigInt<u64>>,
208        gas: Option<ObjectID>,
209        gas_budget: BigInt<u64>,
210    ) -> RpcResult<TransactionBlockBytes> {
211        let split_amounts = split_amounts.into_iter().map(|a| *a).collect();
212        let data = self
213            .0
214            .split_coin(signer, coin_object_id, split_amounts, gas, *gas_budget)
215            .await
216            .map_err(internal_error)?;
217        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
218    }
219
220    async fn split_coin_equal(
221        &self,
222        signer: IotaAddress,
223        coin_object_id: ObjectID,
224        split_count: BigInt<u64>,
225        gas: Option<ObjectID>,
226        gas_budget: BigInt<u64>,
227    ) -> RpcResult<TransactionBlockBytes> {
228        let data = self
229            .0
230            .split_coin_equal(signer, coin_object_id, *split_count, gas, *gas_budget)
231            .await
232            .map_err(internal_error)?;
233        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
234    }
235
236    async fn merge_coin(
237        &self,
238        signer: IotaAddress,
239        primary_coin: ObjectID,
240        coin_to_merge: ObjectID,
241        gas: Option<ObjectID>,
242        gas_budget: BigInt<u64>,
243    ) -> RpcResult<TransactionBlockBytes> {
244        let data = self
245            .0
246            .merge_coins(signer, primary_coin, coin_to_merge, gas, *gas_budget)
247            .await
248            .map_err(internal_error)?;
249        Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
250    }
251
252    async fn move_call(
253        &self,
254        signer: IotaAddress,
255        package_object_id: ObjectID,
256        module: String,
257        function: String,
258        type_arguments: Vec<IotaTypeTag>,
259        rpc_arguments: Vec<IotaJsonValue>,
260        gas: Option<ObjectID>,
261        gas_budget: BigInt<u64>,
262        _txn_builder_mode: Option<IotaTransactionBlockBuilderMode>,
263    ) -> RpcResult<TransactionBlockBytes> {
264        Ok(TransactionBlockBytes::from_data(
265            self.0
266                .move_call(
267                    signer,
268                    package_object_id,
269                    &module,
270                    &function,
271                    type_arguments,
272                    rpc_arguments,
273                    gas,
274                    *gas_budget,
275                    None,
276                )
277                .await
278                .map_err(internal_error)?,
279        )
280        .map_err(internal_error)?)
281    }
282
283    async fn batch_transaction(
284        &self,
285        signer: IotaAddress,
286        params: Vec<RPCTransactionRequestParams>,
287        gas: Option<ObjectID>,
288        gas_budget: BigInt<u64>,
289        _txn_builder_mode: Option<IotaTransactionBlockBuilderMode>,
290    ) -> RpcResult<TransactionBlockBytes> {
291        Ok(TransactionBlockBytes::from_data(
292            self.0
293                .batch_transaction(signer, params, gas, *gas_budget)
294                .await
295                .map_err(internal_error)?,
296        )
297        .map_err(internal_error)?)
298    }
299
300    async fn request_add_stake(
301        &self,
302        signer: IotaAddress,
303        coins: Vec<ObjectID>,
304        amount: Option<BigInt<u64>>,
305        validator: IotaAddress,
306        gas: Option<ObjectID>,
307        gas_budget: BigInt<u64>,
308    ) -> RpcResult<TransactionBlockBytes> {
309        let amount = amount.map(|a| *a);
310        Ok(TransactionBlockBytes::from_data(
311            self.0
312                .request_add_stake(signer, coins, amount, validator, gas, *gas_budget)
313                .await
314                .map_err(internal_error)?,
315        )
316        .map_err(internal_error)?)
317    }
318
319    async fn request_withdraw_stake(
320        &self,
321        signer: IotaAddress,
322        staked_iota: ObjectID,
323        gas: Option<ObjectID>,
324        gas_budget: BigInt<u64>,
325    ) -> RpcResult<TransactionBlockBytes> {
326        Ok(TransactionBlockBytes::from_data(
327            self.0
328                .request_withdraw_stake(signer, staked_iota, gas, *gas_budget)
329                .await
330                .map_err(internal_error)?,
331        )
332        .map_err(internal_error)?)
333    }
334
335    async fn request_add_timelocked_stake(
336        &self,
337        signer: IotaAddress,
338        locked_balance: ObjectID,
339        validator: IotaAddress,
340        gas: ObjectID,
341        gas_budget: BigInt<u64>,
342    ) -> RpcResult<TransactionBlockBytes> {
343        Ok(TransactionBlockBytes::from_data(
344            self.0
345                .request_add_timelocked_stake(signer, locked_balance, validator, gas, *gas_budget)
346                .await
347                .map_err(internal_error)?,
348        )
349        .map_err(internal_error)?)
350    }
351
352    async fn request_withdraw_timelocked_stake(
353        &self,
354        signer: IotaAddress,
355        timelocked_staked_iota: ObjectID,
356        gas: ObjectID,
357        gas_budget: BigInt<u64>,
358    ) -> RpcResult<TransactionBlockBytes> {
359        Ok(TransactionBlockBytes::from_data(
360            self.0
361                .request_withdraw_timelocked_stake(signer, timelocked_staked_iota, gas, *gas_budget)
362                .await
363                .map_err(internal_error)?,
364        )
365        .map_err(internal_error)?)
366    }
367}
368
369impl IotaRpcModule for TransactionBuilderApi {
370    fn rpc(self) -> RpcModule<Self> {
371        self.into_rpc()
372    }
373
374    fn rpc_doc_module() -> Module {
375        TransactionBuilderOpenRpc::module_doc()
376    }
377}