1use 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 .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}