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}