sui_json_rpc/
transaction_builder_api.rs

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