iota_json_rpc_api/transaction_builder.rs
1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use fastcrypto::encoding::Base64;
6use iota_json::IotaJsonValue;
7use iota_json_rpc_types::{
8 IotaTransactionBlockBuilderMode, IotaTypeTag, RPCTransactionRequestParams,
9 TransactionBlockBytes,
10 iota_primitives::{
11 Base64 as Base64Schema, IotaAddress as IotaAddressSchema, ObjectID as ObjectIDSchema,
12 TypeTag as TypeTagSchema,
13 },
14};
15use iota_open_rpc_macros::open_rpc;
16use iota_types::{
17 base_types::{IotaAddress, ObjectID},
18 iota_serde::BigInt,
19};
20use jsonrpsee::{core::RpcResult, proc_macros::rpc};
21
22/// Provides methods for constructing transactions such as transferring objects,
23/// sending coins, performing Move calls, or managing stakes.
24#[open_rpc(namespace = "unsafe", tag = "Transaction Builder API")]
25#[rpc(server, client, namespace = "unsafe")]
26pub trait TransactionBuilder {
27 /// Create an unsigned transaction to transfer an object from one address to
28 /// another. The object's type must allow public transfers
29 #[rustfmt::skip]
30 #[method(name = "transferObject")]
31 async fn transfer_object(
32 &self,
33 /// the transaction signer's IOTA address
34 #[schemars(with = "IotaAddressSchema")]
35 signer: IotaAddress,
36 /// the ID of the object to be transferred
37 #[schemars(with = "ObjectIDSchema")]
38 object_id: ObjectID,
39 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
40 #[schemars(with = "Option<ObjectIDSchema>")]
41 gas: Option<ObjectID>,
42 /// the gas budget, the transaction will fail if the gas cost exceed the budget
43 #[schemars(with = "String")]
44 gas_budget: BigInt<u64>,
45 /// the recipient's IOTA address
46 #[schemars(with = "IotaAddressSchema")]
47 recipient: IotaAddress,
48 ) -> RpcResult<TransactionBlockBytes>;
49
50 /// Create an unsigned transaction to send IOTA coin object to an IOTA address.
51 /// The IOTA object is also used as the gas object.
52 #[rustfmt::skip]
53 #[method(name = "transferIota")]
54 async fn transfer_iota(
55 &self,
56 /// the transaction signer's IOTA address
57 #[schemars(with = "IotaAddressSchema")]
58 signer: IotaAddress,
59 /// the IOTA coin object to be used in this transaction
60 #[schemars(with = "ObjectIDSchema")]
61 iota_object_id: ObjectID,
62 /// the gas budget, the transaction will fail if the gas cost exceed the budget
63 #[schemars(with = "String")]
64 gas_budget: BigInt<u64>,
65 /// the recipient's IOTA address
66 #[schemars(with = "IotaAddressSchema")]
67 recipient: IotaAddress,
68 /// the amount to be split out and transferred
69 #[schemars(with = "Option<String>")]
70 amount: Option<BigInt<u64>>,
71 ) -> RpcResult<TransactionBlockBytes>;
72
73 /// Send `Coin<T>` to a list of addresses, where `T` can be any coin type,
74 /// following a list of amounts, The object specified in the `gas` field
75 /// will be used to pay the gas fee for the transaction. The gas object can
76 /// not appear in `input_coins`. If the gas object is not specified, the RPC
77 /// server will auto-select one.
78 #[rustfmt::skip]
79 #[method(name = "pay")]
80 async fn pay(
81 &self,
82 /// the transaction signer's IOTA address
83 #[schemars(with = "IotaAddressSchema")]
84 signer: IotaAddress,
85 /// the IOTA coins to be used in this transaction
86 #[schemars(with = "Vec<ObjectIDSchema>")]
87 input_coins: Vec<ObjectID>,
88 /// the recipients' addresses, the length of this vector must be the same as amounts.
89 #[schemars(with = "Vec<IotaAddressSchema>")]
90 recipients: Vec<IotaAddress>,
91 /// the amounts to be transferred to recipients, following the same order
92 #[schemars(with = "Vec<String>")]
93 amounts: Vec<BigInt<u64>>,
94 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
95 #[schemars(with = "Option<ObjectIDSchema>")]
96 gas: Option<ObjectID>,
97 /// the gas budget, the transaction will fail if the gas cost exceed the budget
98 #[schemars(with = "String")]
99 gas_budget: BigInt<u64>,
100 ) -> RpcResult<TransactionBlockBytes>;
101
102 /// Send IOTA coins to a list of addresses, following a list of amounts.
103 /// This is for IOTA coin only and does not require a separate gas coin object.
104 /// Specifically, what pay_iota does are:
105 /// 1. debit each input_coin to create new coin following the order of
106 /// amounts and assign it to the corresponding recipient.
107 /// 2. accumulate all residual IOTA from input coins left and deposit all IOTA
108 /// to the first
109 /// input coin, then use the first input coin as the gas coin object.
110 /// 3. the balance of the first input coin after tx is sum(input_coins) -
111 /// sum(amounts) - actual_gas_cost
112 /// 4. all other input coints other than the first one are deleted.
113 #[rustfmt::skip]
114 #[method(name = "payIota")]
115 async fn pay_iota(
116 &self,
117 /// the transaction signer's IOTA address
118 #[schemars(with = "IotaAddressSchema")]
119 signer: IotaAddress,
120 /// the IOTA coins to be used in this transaction, including the coin for gas payment.
121 #[schemars(with = "Vec<ObjectIDSchema>")]
122 input_coins: Vec<ObjectID>,
123 /// the recipients' addresses, the length of this vector must be the same as amounts.
124 #[schemars(with = "Vec<IotaAddressSchema>")]
125 recipients: Vec<IotaAddress>,
126 /// the amounts to be transferred to recipients, following the same order
127 #[schemars(with = "Vec<String>")]
128 amounts: Vec<BigInt<u64>>,
129 /// the gas budget, the transaction will fail if the gas cost exceed the budget
130 #[schemars(with = "String")]
131 gas_budget: BigInt<u64>,
132 ) -> RpcResult<TransactionBlockBytes>;
133
134 /// Send all IOTA coins to one recipient.
135 /// This is for IOTA coin only and does not require a separate gas coin object.
136 /// Specifically, what pay_all_iota does are:
137 /// 1. accumulate all IOTA from input coins and deposit all IOTA to the first
138 /// input coin
139 /// 2. transfer the updated first coin to the recipient and also use this first
140 /// coin as gas coin object.
141 /// 3. the balance of the first input coin after tx is sum(input_coins) -
142 /// actual_gas_cost.
143 /// 4. all other input coins other than the first are deleted.
144 #[rustfmt::skip]
145 #[method(name = "payAllIota")]
146 async fn pay_all_iota(
147 &self,
148 /// the transaction signer's IOTA address
149 #[schemars(with = "IotaAddressSchema")]
150 signer: IotaAddress,
151 /// the IOTA coins to be used in this transaction, including the coin for gas payment.
152 #[schemars(with = "Vec<ObjectIDSchema>")]
153 input_coins: Vec<ObjectID>,
154 /// the recipient address,
155 #[schemars(with = "IotaAddressSchema")]
156 recipient: IotaAddress,
157 /// the gas budget, the transaction will fail if the gas cost exceed the budget
158 #[schemars(with = "String")]
159 gas_budget: BigInt<u64>,
160 ) -> RpcResult<TransactionBlockBytes>;
161
162 /// Create an unsigned transaction to execute a Move call on the network, by
163 /// calling the specified function in the module of a given package.
164 #[rustfmt::skip]
165 #[method(name = "moveCall")]
166 async fn move_call(
167 &self,
168 /// the transaction signer's IOTA address
169 #[schemars(with = "IotaAddressSchema")]
170 signer: IotaAddress,
171 /// the Move package ID, e.g. `0x2`
172 #[schemars(with = "ObjectIDSchema")]
173 package_object_id: ObjectID,
174 /// the Move module name, e.g. `pay`
175 module: String,
176 /// the move function name, e.g. `split`
177 function: String,
178 /// the type arguments of the Move function
179 #[schemars(with = "Vec<TypeTagSchema>")]
180 type_arguments: Vec<IotaTypeTag>,
181 /// the arguments to be passed into the Move function, in [IotaJson](https://docs.iota.org/developer/references/iota-api) format
182 arguments: Vec<IotaJsonValue>,
183 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
184 #[schemars(with = "Option<ObjectIDSchema>")]
185 gas: Option<ObjectID>,
186 /// the gas budget, the transaction will fail if the gas cost exceed the budget
187 #[schemars(with = "String")]
188 gas_budget: BigInt<u64>,
189 /// Whether this is a Normal transaction or a Dev Inspect Transaction. Default to be `IotaTransactionBlockBuilderMode::Commit` when it's None.
190 execution_mode: Option<IotaTransactionBlockBuilderMode>,
191 ) -> RpcResult<TransactionBlockBytes>;
192
193 /// Create an unsigned transaction to publish a Move package.
194 #[rustfmt::skip]
195 #[method(name = "publish")]
196 async fn publish(
197 &self,
198 /// the transaction signer's IOTA address
199 #[schemars(with = "IotaAddressSchema")]
200 sender: IotaAddress,
201 /// the compiled bytes of a Move package
202 #[schemars(with = "Vec<Base64Schema>")]
203 compiled_modules: Vec<Base64>,
204 /// a list of transitive dependency addresses that this set of modules depends on.
205 #[schemars(with = "Vec<ObjectIDSchema>")]
206 dependencies: Vec<ObjectID>,
207 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
208 #[schemars(with = "Option<ObjectIDSchema>")]
209 gas: Option<ObjectID>,
210 /// the gas budget, the transaction will fail if the gas cost exceed the budget
211 #[schemars(with = "String")]
212 gas_budget: BigInt<u64>,
213 ) -> RpcResult<TransactionBlockBytes>;
214
215 /// Create an unsigned transaction to split a coin object into multiple
216 /// coins.
217 #[rustfmt::skip]
218 #[method(name = "splitCoin")]
219 async fn split_coin(
220 &self,
221 /// the transaction signer's IOTA address
222 #[schemars(with = "IotaAddressSchema")]
223 signer: IotaAddress,
224 /// the coin object to be spilt
225 #[schemars(with = "ObjectIDSchema")]
226 coin_object_id: ObjectID,
227 /// the amounts to split out from the coin
228 #[schemars(with = "Vec<String>")]
229 split_amounts: Vec<BigInt<u64>>,
230 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
231 #[schemars(with = "Option<ObjectIDSchema>")]
232 gas: Option<ObjectID>,
233 /// the gas budget, the transaction will fail if the gas cost exceed the budget
234 #[schemars(with = "String")]
235 gas_budget: BigInt<u64>,
236 ) -> RpcResult<TransactionBlockBytes>;
237
238 /// Create an unsigned transaction to split a coin object into multiple
239 /// equal-size coins.
240 #[rustfmt::skip]
241 #[method(name = "splitCoinEqual")]
242 async fn split_coin_equal(
243 &self,
244 /// the transaction signer's IOTA address
245 #[schemars(with = "IotaAddressSchema")]
246 signer: IotaAddress,
247 /// the coin object to be spilt
248 #[schemars(with = "ObjectIDSchema")]
249 coin_object_id: ObjectID,
250 /// the number of coins to split into
251 #[schemars(with = "String")]
252 split_count: BigInt<u64>,
253 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
254 #[schemars(with = "Option<ObjectIDSchema>")]
255 gas: Option<ObjectID>,
256 /// the gas budget, the transaction will fail if the gas cost exceed the budget
257 #[schemars(with = "String")]
258 gas_budget: BigInt<u64>,
259 ) -> RpcResult<TransactionBlockBytes>;
260
261 /// Create an unsigned transaction to merge multiple coins into one coin.
262 #[rustfmt::skip]
263 #[method(name = "mergeCoins")]
264 async fn merge_coin(
265 &self,
266 /// the transaction signer's IOTA address
267 #[schemars(with = "IotaAddressSchema")]
268 signer: IotaAddress,
269 /// the coin object to merge into, this coin will remain after the transaction
270 #[schemars(with = "ObjectIDSchema")]
271 primary_coin: ObjectID,
272 /// the coin object to be merged, this coin will be destroyed, the balance will be added to `primary_coin`
273 #[schemars(with = "ObjectIDSchema")]
274 coin_to_merge: ObjectID,
275 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
276 #[schemars(with = "Option<ObjectIDSchema>")]
277 gas: Option<ObjectID>,
278 /// the gas budget, the transaction will fail if the gas cost exceed the budget
279 #[schemars(with = "String")]
280 gas_budget: BigInt<u64>,
281 ) -> RpcResult<TransactionBlockBytes>;
282
283 /// Create an unsigned batched transaction.
284 #[rustfmt::skip]
285 #[method(name = "batchTransaction")]
286 async fn batch_transaction(
287 &self,
288 /// the transaction signer's IOTA address
289 #[schemars(with = "IotaAddressSchema")]
290 signer: IotaAddress,
291 /// list of transaction request parameters
292 single_transaction_params: Vec<RPCTransactionRequestParams>,
293 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
294 #[schemars(with = "Option<ObjectIDSchema>")]
295 gas: Option<ObjectID>,
296 /// the gas budget, the transaction will fail if the gas cost exceed the budget
297 #[schemars(with = "String")]
298 gas_budget: BigInt<u64>,
299 /// Whether this is a regular transaction or a Dev Inspect Transaction
300 txn_builder_mode: Option<IotaTransactionBlockBuilderMode>,
301 ) -> RpcResult<TransactionBlockBytes>;
302
303 /// Add stake to a validator's staking pool using multiple coins and amount.
304 #[rustfmt::skip]
305 #[method(name = "requestAddStake")]
306 async fn request_add_stake(
307 &self,
308 /// the transaction signer's IOTA address
309 #[schemars(with = "IotaAddressSchema")]
310 signer: IotaAddress,
311 /// Coin<IOTA> object to stake
312 #[schemars(with = "Vec<ObjectIDSchema>")]
313 coins: Vec<ObjectID>,
314 /// stake amount
315 #[schemars(with = "Option<String>")]
316 amount: Option<BigInt<u64>>,
317 /// the validator's IOTA address
318 #[schemars(with = "IotaAddressSchema")]
319 validator: IotaAddress,
320 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
321 #[schemars(with = "Option<ObjectIDSchema>")]
322 gas: Option<ObjectID>,
323 /// the gas budget, the transaction will fail if the gas cost exceed the budget
324 #[schemars(with = "String")]
325 gas_budget: BigInt<u64>,
326 ) -> RpcResult<TransactionBlockBytes>;
327
328 /// Withdraw stake from a validator's staking pool.
329 #[rustfmt::skip]
330 #[method(name = "requestWithdrawStake")]
331 async fn request_withdraw_stake(
332 &self,
333 /// the transaction signer's IOTA address
334 #[schemars(with = "IotaAddressSchema")]
335 signer: IotaAddress,
336 /// StakedIota object ID
337 #[schemars(with = "ObjectIDSchema")]
338 staked_iota: ObjectID,
339 /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
340 #[schemars(with = "Option<ObjectIDSchema>")]
341 gas: Option<ObjectID>,
342 /// the gas budget, the transaction will fail if the gas cost exceed the budget
343 #[schemars(with = "String")]
344 gas_budget: BigInt<u64>,
345 ) -> RpcResult<TransactionBlockBytes>;
346
347 /// Add timelocked stake to a validator's staking pool using multiple balances
348 /// and amount.
349 #[rustfmt::skip]
350 #[method(name = "requestAddTimelockedStake")]
351 async fn request_add_timelocked_stake(
352 &self,
353 /// the transaction signer's IOTA address
354 #[schemars(with = "IotaAddressSchema")]
355 signer: IotaAddress,
356 /// TimeLock<Balance<IOTA>> object to stake
357 #[schemars(with = "ObjectIDSchema")]
358 locked_balance: ObjectID,
359 /// the validator's IOTA address
360 #[schemars(with = "IotaAddressSchema")]
361 validator: IotaAddress,
362 /// gas object to be used in this transaction
363 #[schemars(with = "ObjectIDSchema")]
364 gas: ObjectID,
365 /// the gas budget, the transaction will fail if the gas cost exceed the budget
366 #[schemars(with = "String")]
367 gas_budget: BigInt<u64>,
368 ) -> RpcResult<TransactionBlockBytes>;
369
370 /// Withdraw timelocked stake from a validator's staking pool.
371 #[rustfmt::skip]
372 #[method(name = "requestWithdrawTimelockedStake")]
373 async fn request_withdraw_timelocked_stake(
374 &self,
375 /// the transaction signer's IOTA address
376 #[schemars(with = "IotaAddressSchema")]
377 signer: IotaAddress,
378 /// TimelockedStakedIota object ID
379 #[schemars(with = "ObjectIDSchema")]
380 timelocked_staked_iota: ObjectID,
381 /// gas object to be used in this transaction
382 #[schemars(with = "ObjectIDSchema")]
383 gas: ObjectID,
384 /// the gas budget, the transaction will fail if the gas cost exceed the budget
385 #[schemars(with = "String")]
386 gas_budget: BigInt<u64>,
387 ) -> RpcResult<TransactionBlockBytes>;
388}