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