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