1use 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},
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 cursor: Option<ObjectID>,
56 limit: Option<usize>,
57 options: IotaObjectDataOptions,
58 ) -> Result<iota_json_rpc_types::ObjectsPage, anyhow::Error> {
59 let limit = limit.unwrap_or(50);
60 let mut result = self
61 .0
62 .get_owner_objects_with_limit(
63 address,
64 cursor,
65 limit + 1,
66 Some(IotaObjectDataFilter::StructType(object_type)),
67 )?
68 .into_iter()
69 .map(|info| {
70 let read = self.0.get_object_read(&info.object_id)?;
71 IotaObjectResponse::try_from_object_read_and_options(read, &options)
72 })
73 .collect::<Result<Vec<_>, _>>()?;
74
75 let next_cursor = if result.len() > limit {
76 result.pop().unwrap().object_id().ok()
78 } else {
79 None
80 };
81
82 Ok(iota_json_rpc_types::ObjectsPage {
83 data: result,
84 next_cursor,
85 has_next_page: next_cursor.is_some(),
86 })
87 }
88
89 async fn get_object_with_options(
90 &self,
91 object_id: ObjectID,
92 options: IotaObjectDataOptions,
93 ) -> Result<IotaObjectResponse, anyhow::Error> {
94 let result = self.0.get_object_read(&object_id)?;
95 IotaObjectResponse::try_from_object_read_and_options(result, &options)
96 }
97
98 async fn get_reference_gas_price(&self) -> Result<u64, anyhow::Error> {
99 let epoch_store = self.0.load_epoch_store_one_call_per_task();
100 Ok(epoch_store.reference_gas_price())
101 }
102}
103
104#[async_trait]
105impl TransactionBuilderServer for TransactionBuilderApi {
106 async fn transfer_object(
107 &self,
108 signer: IotaAddress,
109 object_id: ObjectID,
110 gas: Option<ObjectID>,
111 gas_budget: BigInt<u64>,
112 recipient: IotaAddress,
113 ) -> RpcResult<TransactionBlockBytes> {
114 let data = self
115 .0
116 .transfer_object(signer, object_id, gas, *gas_budget, recipient)
117 .await
118 .map_err(internal_error)?;
119 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
120 }
121
122 async fn transfer_iota(
123 &self,
124 signer: IotaAddress,
125 iota_object_id: ObjectID,
126 gas_budget: BigInt<u64>,
127 recipient: IotaAddress,
128 amount: Option<BigInt<u64>>,
129 ) -> RpcResult<TransactionBlockBytes> {
130 let data = self
131 .0
132 .transfer_iota(
133 signer,
134 iota_object_id,
135 *gas_budget,
136 recipient,
137 amount.map(|a| *a),
138 )
139 .await
140 .map_err(internal_error)?;
141 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
142 }
143
144 async fn pay(
145 &self,
146 signer: IotaAddress,
147 input_coins: Vec<ObjectID>,
148 recipients: Vec<IotaAddress>,
149 amounts: Vec<BigInt<u64>>,
150 gas: Option<ObjectID>,
151 gas_budget: BigInt<u64>,
152 ) -> RpcResult<TransactionBlockBytes> {
153 let data = self
154 .0
155 .pay(
156 signer,
157 input_coins,
158 recipients,
159 amounts.into_iter().map(|a| *a).collect(),
160 gas,
161 *gas_budget,
162 )
163 .await
164 .map_err(internal_error)?;
165 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
166 }
167
168 async fn pay_iota(
169 &self,
170 signer: IotaAddress,
171 input_coins: Vec<ObjectID>,
172 recipients: Vec<IotaAddress>,
173 amounts: Vec<BigInt<u64>>,
174 gas_budget: BigInt<u64>,
175 ) -> RpcResult<TransactionBlockBytes> {
176 let data = self
177 .0
178 .pay_iota(
179 signer,
180 input_coins,
181 recipients,
182 amounts.into_iter().map(|a| *a).collect(),
183 *gas_budget,
184 )
185 .await
186 .map_err(internal_error)?;
187 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
188 }
189
190 async fn pay_all_iota(
191 &self,
192 signer: IotaAddress,
193 input_coins: Vec<ObjectID>,
194 recipient: IotaAddress,
195 gas_budget: BigInt<u64>,
196 ) -> RpcResult<TransactionBlockBytes> {
197 let data = self
198 .0
199 .pay_all_iota(signer, input_coins, recipient, *gas_budget)
200 .await
201 .map_err(internal_error)?;
202 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
203 }
204
205 async fn publish(
206 &self,
207 sender: IotaAddress,
208 compiled_modules: Vec<Base64>,
209 dependencies: Vec<ObjectID>,
210 gas: Option<ObjectID>,
211 gas_budget: BigInt<u64>,
212 ) -> RpcResult<TransactionBlockBytes> {
213 let compiled_modules = compiled_modules
214 .into_iter()
215 .map(|data| data.to_vec().map_err(|e| anyhow::anyhow!(e)))
216 .collect::<Result<Vec<_>, _>>()
217 .map_err(internal_error)?;
218 let data = self
219 .0
220 .publish(sender, compiled_modules, dependencies, gas, *gas_budget)
221 .await
222 .map_err(internal_error)?;
223 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
224 }
225
226 async fn split_coin(
227 &self,
228 signer: IotaAddress,
229 coin_object_id: ObjectID,
230 split_amounts: Vec<BigInt<u64>>,
231 gas: Option<ObjectID>,
232 gas_budget: BigInt<u64>,
233 ) -> RpcResult<TransactionBlockBytes> {
234 let split_amounts = split_amounts.into_iter().map(|a| *a).collect();
235 let data = self
236 .0
237 .split_coin(signer, coin_object_id, split_amounts, gas, *gas_budget)
238 .await
239 .map_err(internal_error)?;
240 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
241 }
242
243 async fn split_coin_equal(
244 &self,
245 signer: IotaAddress,
246 coin_object_id: ObjectID,
247 split_count: BigInt<u64>,
248 gas: Option<ObjectID>,
249 gas_budget: BigInt<u64>,
250 ) -> RpcResult<TransactionBlockBytes> {
251 let data = self
252 .0
253 .split_coin_equal(signer, coin_object_id, *split_count, gas, *gas_budget)
254 .await
255 .map_err(internal_error)?;
256 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
257 }
258
259 async fn merge_coin(
260 &self,
261 signer: IotaAddress,
262 primary_coin: ObjectID,
263 coin_to_merge: ObjectID,
264 gas: Option<ObjectID>,
265 gas_budget: BigInt<u64>,
266 ) -> RpcResult<TransactionBlockBytes> {
267 let data = self
268 .0
269 .merge_coins(signer, primary_coin, coin_to_merge, gas, *gas_budget)
270 .await
271 .map_err(internal_error)?;
272 Ok(TransactionBlockBytes::from_data(data).map_err(internal_error)?)
273 }
274
275 async fn move_call(
276 &self,
277 signer: IotaAddress,
278 package_object_id: ObjectID,
279 module: String,
280 function: String,
281 type_arguments: Vec<IotaTypeTag>,
282 rpc_arguments: Vec<IotaJsonValue>,
283 gas: Option<ObjectID>,
284 gas_budget: BigInt<u64>,
285 _txn_builder_mode: Option<IotaTransactionBlockBuilderMode>,
286 ) -> RpcResult<TransactionBlockBytes> {
287 Ok(TransactionBlockBytes::from_data(
288 self.0
289 .move_call(
290 signer,
291 package_object_id,
292 &module,
293 &function,
294 type_arguments,
295 rpc_arguments,
296 gas,
297 *gas_budget,
298 None,
299 )
300 .await
301 .map_err(internal_error)?,
302 )
303 .map_err(internal_error)?)
304 }
305
306 async fn batch_transaction(
307 &self,
308 signer: IotaAddress,
309 params: Vec<RPCTransactionRequestParams>,
310 gas: Option<ObjectID>,
311 gas_budget: BigInt<u64>,
312 _txn_builder_mode: Option<IotaTransactionBlockBuilderMode>,
313 ) -> RpcResult<TransactionBlockBytes> {
314 Ok(TransactionBlockBytes::from_data(
315 self.0
316 .batch_transaction(signer, params, gas, *gas_budget)
317 .await
318 .map_err(internal_error)?,
319 )
320 .map_err(internal_error)?)
321 }
322
323 async fn request_add_stake(
324 &self,
325 signer: IotaAddress,
326 coins: Vec<ObjectID>,
327 amount: Option<BigInt<u64>>,
328 validator: IotaAddress,
329 gas: Option<ObjectID>,
330 gas_budget: BigInt<u64>,
331 ) -> RpcResult<TransactionBlockBytes> {
332 let amount = amount.map(|a| *a);
333 Ok(TransactionBlockBytes::from_data(
334 self.0
335 .request_add_stake(signer, coins, amount, validator, gas, *gas_budget)
336 .await
337 .map_err(internal_error)?,
338 )
339 .map_err(internal_error)?)
340 }
341
342 async fn request_withdraw_stake(
343 &self,
344 signer: IotaAddress,
345 staked_iota: ObjectID,
346 gas: Option<ObjectID>,
347 gas_budget: BigInt<u64>,
348 ) -> RpcResult<TransactionBlockBytes> {
349 Ok(TransactionBlockBytes::from_data(
350 self.0
351 .request_withdraw_stake(signer, staked_iota, gas, *gas_budget)
352 .await
353 .map_err(internal_error)?,
354 )
355 .map_err(internal_error)?)
356 }
357
358 async fn request_add_timelocked_stake(
359 &self,
360 signer: IotaAddress,
361 locked_balance: ObjectID,
362 validator: IotaAddress,
363 gas: ObjectID,
364 gas_budget: BigInt<u64>,
365 ) -> RpcResult<TransactionBlockBytes> {
366 Ok(TransactionBlockBytes::from_data(
367 self.0
368 .request_add_timelocked_stake(signer, locked_balance, validator, gas, *gas_budget)
369 .await
370 .map_err(internal_error)?,
371 )
372 .map_err(internal_error)?)
373 }
374
375 async fn request_withdraw_timelocked_stake(
376 &self,
377 signer: IotaAddress,
378 timelocked_staked_iota: ObjectID,
379 gas: ObjectID,
380 gas_budget: BigInt<u64>,
381 ) -> RpcResult<TransactionBlockBytes> {
382 Ok(TransactionBlockBytes::from_data(
383 self.0
384 .request_withdraw_timelocked_stake(signer, timelocked_staked_iota, gas, *gas_budget)
385 .await
386 .map_err(internal_error)?,
387 )
388 .map_err(internal_error)?)
389 }
390}
391
392impl IotaRpcModule for TransactionBuilderApi {
393 fn rpc(self) -> RpcModule<Self> {
394 self.into_rpc()
395 }
396
397 fn rpc_doc_module() -> Module {
398 TransactionBuilderOpenRpc::module_doc()
399 }
400}