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::{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 .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}