Skip to main content

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
106    ///    to the first
107    /// input coin, then use the first input coin as the gas coin object.
108    /// 3. the balance of the first input coin after tx is sum(input_coins) -
109    ///    sum(amounts) - actual_gas_cost
110    /// 4. all other input coints other than the first one are deleted.
111    #[rustfmt::skip]
112    #[method(name = "payIota")]
113    async fn pay_iota(
114        &self,
115        /// the transaction signer's IOTA address
116        #[schemars(with = "AddressSchema")]
117        signer: Address,
118        /// the IOTA coins to be used in this transaction, including the coin for gas payment.
119        #[schemars(with = "Vec<ObjectIdSchema>")]
120        input_coins: Vec<ObjectId>,
121        /// the recipients' addresses, the length of this vector must be the same as amounts.
122        #[schemars(with = "Vec<AddressSchema>")]
123        recipients: Vec<Address>,
124        /// the amounts to be transferred to recipients, following the same order
125        #[schemars(with = "Vec<String>")]
126        amounts: Vec<BigInt<u64>>,
127        /// the gas budget, the transaction will fail if the gas cost exceed the budget
128        #[schemars(with = "String")]
129        gas_budget: BigInt<u64>,
130    ) -> RpcResult<TransactionBlockBytes>;
131
132    /// Send all IOTA coins to one recipient.
133    /// This is for IOTA coin only and does not require a separate gas coin object.
134    /// Specifically, what pay_all_iota does are:
135    /// 1. accumulate all IOTA from input coins and deposit all IOTA to the first
136    ///    input coin
137    /// 2. transfer the updated first coin to the recipient and also use this first
138    ///    coin as gas coin object.
139    /// 3. the balance of the first input coin after tx is sum(input_coins) -
140    ///    actual_gas_cost.
141    /// 4. all other input coins other than the first are deleted.
142    #[rustfmt::skip]
143    #[method(name = "payAllIota")]
144    async fn pay_all_iota(
145        &self,
146        /// the transaction signer's IOTA address
147        #[schemars(with = "AddressSchema")]
148        signer: Address,
149        /// the IOTA coins to be used in this transaction, including the coin for gas payment.
150        #[schemars(with = "Vec<ObjectIdSchema>")]
151        input_coins: Vec<ObjectId>,
152        /// the recipient address,
153        #[schemars(with = "AddressSchema")]
154        recipient: Address,
155        /// the gas budget, the transaction will fail if the gas cost exceed the budget
156        #[schemars(with = "String")]
157        gas_budget: BigInt<u64>,
158    ) -> RpcResult<TransactionBlockBytes>;
159
160    /// Create an unsigned transaction to execute a Move call on the network, by
161    /// calling the specified function in the module of a given package.
162    #[rustfmt::skip]
163    #[method(name = "moveCall")]
164    async fn move_call(
165        &self,
166        /// the transaction signer's IOTA address
167        #[schemars(with = "AddressSchema")]
168        signer: Address,
169        /// the Move package ID, e.g. `0x2`
170        #[schemars(with = "ObjectIdSchema")]
171        package_object_id: ObjectId,
172        /// the Move module name, e.g. `pay`
173        module: String,
174        /// the move function name, e.g. `split`
175        function: String,
176        /// the type arguments of the Move function
177        #[schemars(with = "Vec<TypeTagSchema>")]
178        type_arguments: Vec<IotaTypeTag>,
179        /// the arguments to be passed into the Move function, in [IotaJson](https://docs.iota.org/developer/references/iota-api) format
180        arguments: Vec<IotaJsonValue>,
181        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
182        #[schemars(with = "Option<ObjectIdSchema>")]
183        gas: Option<ObjectId>,
184        /// the gas budget, the transaction will fail if the gas cost exceed the budget
185        #[schemars(with = "String")]
186        gas_budget: BigInt<u64>,
187        /// Whether this is a Normal transaction or a Dev Inspect Transaction. Default to be `IotaTransactionBlockBuilderMode::Commit` when it's None.
188        execution_mode: Option<IotaTransactionBlockBuilderMode>,
189    ) -> RpcResult<TransactionBlockBytes>;
190
191    /// Create an unsigned transaction to publish a Move package.
192    #[rustfmt::skip]
193    #[method(name = "publish")]
194    async fn publish(
195        &self,
196        /// the transaction signer's IOTA address
197        #[schemars(with = "AddressSchema")]
198        sender: Address,
199        /// the compiled bytes of a Move package
200        #[schemars(with = "Vec<Base64Schema>")]
201        compiled_modules: Vec<Base64>,
202        /// a list of transitive dependency addresses that this set of modules depends on.
203        #[schemars(with = "Vec<ObjectIdSchema>")]
204        dependencies: Vec<ObjectId>,
205        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
206        #[schemars(with = "Option<ObjectIdSchema>")]
207        gas: Option<ObjectId>,
208        /// the gas budget, the transaction will fail if the gas cost exceed the budget
209        #[schemars(with = "String")]
210        gas_budget: BigInt<u64>,
211    ) -> RpcResult<TransactionBlockBytes>;
212
213    /// Create an unsigned transaction to split a coin object into multiple
214    /// coins.
215    #[rustfmt::skip]
216    #[method(name = "splitCoin")]
217    async fn split_coin(
218        &self,
219        /// the transaction signer's IOTA address
220        #[schemars(with = "AddressSchema")]
221        signer: Address,
222        /// the coin object to be spilt
223        #[schemars(with = "ObjectIdSchema")]
224        coin_object_id: ObjectId,
225        /// the amounts to split out from the coin
226        #[schemars(with = "Vec<String>")]
227        split_amounts: Vec<BigInt<u64>>,
228        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
229        #[schemars(with = "Option<ObjectIdSchema>")]
230        gas: Option<ObjectId>,
231        /// the gas budget, the transaction will fail if the gas cost exceed the budget
232        #[schemars(with = "String")]
233        gas_budget: BigInt<u64>,
234    ) -> RpcResult<TransactionBlockBytes>;
235
236    /// Create an unsigned transaction to split a coin object into multiple
237    /// equal-size coins.
238    #[rustfmt::skip]
239    #[method(name = "splitCoinEqual")]
240    async fn split_coin_equal(
241        &self,
242        /// the transaction signer's IOTA address
243        #[schemars(with = "AddressSchema")]
244        signer: Address,
245        /// the coin object to be spilt
246        #[schemars(with = "ObjectIdSchema")]
247        coin_object_id: ObjectId,
248        /// the number of coins to split into
249        #[schemars(with = "String")]
250        split_count: BigInt<u64>,
251        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
252        #[schemars(with = "Option<ObjectIdSchema>")]
253        gas: Option<ObjectId>,
254        /// the gas budget, the transaction will fail if the gas cost exceed the budget
255        #[schemars(with = "String")]
256        gas_budget: BigInt<u64>,
257    ) -> RpcResult<TransactionBlockBytes>;
258
259    /// Create an unsigned transaction to merge multiple coins into one coin.
260    #[rustfmt::skip]
261    #[method(name = "mergeCoins")]
262    async fn merge_coin(
263        &self,
264        /// the transaction signer's IOTA address
265        #[schemars(with = "AddressSchema")]
266        signer: Address,
267        /// the coin object to merge into, this coin will remain after the transaction
268        #[schemars(with = "ObjectIdSchema")]
269        primary_coin: ObjectId,
270        /// the coin object to be merged, this coin will be destroyed, the balance will be added to `primary_coin`
271        #[schemars(with = "ObjectIdSchema")]
272        coin_to_merge: ObjectId,
273        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
274        #[schemars(with = "Option<ObjectIdSchema>")]
275        gas: Option<ObjectId>,
276        /// the gas budget, the transaction will fail if the gas cost exceed the budget
277        #[schemars(with = "String")]
278        gas_budget: BigInt<u64>,
279    ) -> RpcResult<TransactionBlockBytes>;
280
281    /// Create an unsigned batched transaction.
282    #[rustfmt::skip]
283    #[method(name = "batchTransaction")]
284    async fn batch_transaction(
285        &self,
286        /// the transaction signer's IOTA address
287        #[schemars(with = "AddressSchema")]
288        signer: Address,
289        /// list of transaction request parameters
290        single_transaction_params: Vec<RPCTransactionRequestParams>,
291        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
292        #[schemars(with = "Option<ObjectIdSchema>")]
293        gas: Option<ObjectId>,
294        /// the gas budget, the transaction will fail if the gas cost exceed the budget
295        #[schemars(with = "String")]
296        gas_budget: BigInt<u64>,
297        /// Whether this is a regular transaction or a Dev Inspect Transaction
298        txn_builder_mode: Option<IotaTransactionBlockBuilderMode>,
299    ) -> RpcResult<TransactionBlockBytes>;
300
301    /// Add stake to a validator's staking pool using multiple coins and amount.
302    #[rustfmt::skip]
303    #[method(name = "requestAddStake")]
304    async fn request_add_stake(
305        &self,
306        /// the transaction signer's IOTA address
307        #[schemars(with = "AddressSchema")]
308        signer: Address,
309        /// Coin<IOTA> object to stake
310        #[schemars(with = "Vec<ObjectIdSchema>")]
311        coins: Vec<ObjectId>,
312        /// stake amount
313        #[schemars(with = "Option<String>")]
314        amount: Option<BigInt<u64>>,
315        /// the validator's IOTA address
316        #[schemars(with = "AddressSchema")]
317        validator: Address,
318        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
319        #[schemars(with = "Option<ObjectIdSchema>")]
320        gas: Option<ObjectId>,
321        /// the gas budget, the transaction will fail if the gas cost exceed the budget
322        #[schemars(with = "String")]
323        gas_budget: BigInt<u64>,
324    ) -> RpcResult<TransactionBlockBytes>;
325
326    /// Withdraw stake from a validator's staking pool.
327    #[rustfmt::skip]
328    #[method(name = "requestWithdrawStake")]
329    async fn request_withdraw_stake(
330        &self,
331        /// the transaction signer's IOTA address
332        #[schemars(with = "AddressSchema")]
333        signer: Address,
334        /// StakedIota object ID
335        #[schemars(with = "ObjectIdSchema")]
336        staked_iota: ObjectId,
337        /// gas object to be used in this transaction, node will pick one from the signer's possession if not provided
338        #[schemars(with = "Option<ObjectIdSchema>")]
339        gas: Option<ObjectId>,
340        /// the gas budget, the transaction will fail if the gas cost exceed the budget
341        #[schemars(with = "String")]
342        gas_budget: BigInt<u64>,
343    ) -> RpcResult<TransactionBlockBytes>;
344
345    /// Add timelocked stake to a validator's staking pool using multiple balances
346    /// and amount.
347    #[rustfmt::skip]
348    #[method(name = "requestAddTimelockedStake")]
349    async fn request_add_timelocked_stake(
350        &self,
351        /// the transaction signer's IOTA address
352        #[schemars(with = "AddressSchema")]
353        signer: Address,
354        /// TimeLock<Balance<IOTA>> object to stake
355        #[schemars(with = "ObjectIdSchema")]
356        locked_balance: ObjectId,
357        /// the validator's IOTA address
358        #[schemars(with = "AddressSchema")]
359        validator: Address,
360        /// gas object to be used in this transaction
361        #[schemars(with = "ObjectIdSchema")]
362        gas: ObjectId,
363        /// the gas budget, the transaction will fail if the gas cost exceed the budget
364        #[schemars(with = "String")]
365        gas_budget: BigInt<u64>,
366    ) -> RpcResult<TransactionBlockBytes>;
367
368    /// Withdraw timelocked stake from a validator's staking pool.
369    #[rustfmt::skip]
370    #[method(name = "requestWithdrawTimelockedStake")]
371    async fn request_withdraw_timelocked_stake(
372        &self,
373        /// the transaction signer's IOTA address
374        #[schemars(with = "AddressSchema")]
375        signer: Address,
376        /// TimelockedStakedIota object ID
377        #[schemars(with = "ObjectIdSchema")]
378        timelocked_staked_iota: ObjectId,
379        /// gas object to be used in this transaction
380        #[schemars(with = "ObjectIdSchema")]
381        gas: ObjectId,
382        /// the gas budget, the transaction will fail if the gas cost exceed the budget
383        #[schemars(with = "String")]
384        gas_budget: BigInt<u64>,
385    ) -> RpcResult<TransactionBlockBytes>;
386}