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