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