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