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}