iota_transaction_builder/
package.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::result::Result;
6
7use anyhow::{Ok, anyhow, bail};
8use iota_json_rpc_types::IotaObjectDataOptions;
9use iota_types::{
10    base_types::{Identifier, IotaAddress, ObjectID},
11    move_package::MovePackage,
12    object::Owner,
13    programmable_transaction_builder::ProgrammableTransactionBuilder,
14    transaction::{Argument, ObjectArg, TransactionData, TransactionKind},
15};
16
17use crate::TransactionBuilder;
18
19impl TransactionBuilder {
20    /// Build a [`TransactionKind::ProgrammableTransaction`] that contains
21    /// [`iota_types::transaction::Command::Publish`] for the provided package.
22    pub async fn publish_tx_kind(
23        &self,
24        sender: IotaAddress,
25        modules: Vec<Vec<u8>>,
26        dep_ids: Vec<ObjectID>,
27    ) -> Result<TransactionKind, anyhow::Error> {
28        let pt = {
29            let mut builder = ProgrammableTransactionBuilder::new();
30            let upgrade_cap = builder.publish_upgradeable(modules, dep_ids);
31            builder.transfer_arg(sender, upgrade_cap);
32            builder.finish()
33        };
34        Ok(TransactionKind::programmable(pt))
35    }
36
37    /// Publish a new move package.
38    pub async fn publish(
39        &self,
40        sender: IotaAddress,
41        compiled_modules: Vec<Vec<u8>>,
42        dep_ids: Vec<ObjectID>,
43        gas: impl Into<Option<ObjectID>>,
44        gas_budget: u64,
45    ) -> anyhow::Result<TransactionData> {
46        let gas_price = self.0.get_reference_gas_price().await?;
47        let gas = self
48            .select_gas(sender, gas, gas_budget, vec![], gas_price)
49            .await?;
50        Ok(TransactionData::new_module(
51            sender,
52            gas,
53            compiled_modules,
54            dep_ids,
55            gas_budget,
56            gas_price,
57        ))
58    }
59
60    /// Build a [`TransactionKind::ProgrammableTransaction`] that contains
61    /// [`iota_types::transaction::Command::Upgrade`] for the provided package.
62    pub async fn upgrade_tx_kind(
63        &self,
64        package_id: ObjectID,
65        modules: Vec<Vec<u8>>,
66        dep_ids: Vec<ObjectID>,
67        upgrade_capability: ObjectID,
68        upgrade_policy: u8,
69        digest: Vec<u8>,
70    ) -> Result<TransactionKind, anyhow::Error> {
71        let upgrade_capability = self
72            .0
73            .get_object_with_options(
74                upgrade_capability,
75                IotaObjectDataOptions::new().with_owner(),
76            )
77            .await?
78            .into_object()?;
79        let capability_owner = upgrade_capability
80            .owner
81            .ok_or_else(|| anyhow!("Unable to determine ownership of upgrade capability"))?;
82        let pt = {
83            let mut builder = ProgrammableTransactionBuilder::new();
84            let capability_arg = match capability_owner {
85                Owner::Address(_) => ObjectArg::ImmOrOwnedObject(upgrade_capability.object_ref()),
86                Owner::Shared(initial_shared_version) => ObjectArg::SharedObject {
87                    id: upgrade_capability.object_ref().object_id,
88                    initial_shared_version,
89                    mutable: true,
90                },
91                Owner::Immutable => {
92                    bail!("Upgrade capability is stored immutably and cannot be used for upgrades")
93                }
94                // If the capability is owned by an object, then the module defining the owning
95                // object gets to decide how the upgrade capability should be used.
96                Owner::Object(_) => {
97                    bail!("Upgrade capability controlled by object");
98                }
99                _ => unimplemented!("a new Owner enum variant was added and needs to be handled"),
100            };
101            builder.obj(capability_arg).unwrap();
102            let upgrade_arg = builder.pure(upgrade_policy).unwrap();
103            let digest_arg = builder.pure(digest).unwrap();
104            let upgrade_ticket = builder.programmable_move_call(
105                ObjectID::FRAMEWORK,
106                Identifier::PACKAGE_MODULE,
107                Identifier::from_static("authorize_upgrade"),
108                vec![],
109                vec![Argument::Input(0), upgrade_arg, digest_arg],
110            );
111            let upgrade_receipt = builder.upgrade(package_id, upgrade_ticket, dep_ids, modules);
112
113            builder.programmable_move_call(
114                ObjectID::FRAMEWORK,
115                Identifier::PACKAGE_MODULE,
116                Identifier::from_static("commit_upgrade"),
117                vec![],
118                vec![Argument::Input(0), upgrade_receipt],
119            );
120
121            builder.finish()
122        };
123
124        Ok(TransactionKind::programmable(pt))
125    }
126
127    /// Upgrade an existing move package.
128    pub async fn upgrade(
129        &self,
130        sender: IotaAddress,
131        package_id: ObjectID,
132        compiled_modules: Vec<Vec<u8>>,
133        dep_ids: Vec<ObjectID>,
134        upgrade_capability: ObjectID,
135        upgrade_policy: u8,
136        gas: impl Into<Option<ObjectID>>,
137        gas_budget: u64,
138    ) -> anyhow::Result<TransactionData> {
139        let gas_price = self.0.get_reference_gas_price().await?;
140        let gas = self
141            .select_gas(sender, gas, gas_budget, vec![], gas_price)
142            .await?;
143        let upgrade_cap = self
144            .0
145            .get_object_with_options(
146                upgrade_capability,
147                IotaObjectDataOptions::new().with_owner(),
148            )
149            .await?
150            .into_object()?;
151        let cap_owner = upgrade_cap
152            .owner
153            .ok_or_else(|| anyhow!("Unable to determine ownership of upgrade capability"))?;
154        let digest =
155            MovePackage::compute_digest_for_modules_and_deps(&compiled_modules, &dep_ids).to_vec();
156        TransactionData::new_upgrade(
157            sender,
158            gas,
159            package_id,
160            compiled_modules,
161            dep_ids,
162            (upgrade_cap.object_ref(), cap_owner),
163            upgrade_policy,
164            digest,
165            gas_budget,
166            gas_price,
167        )
168    }
169}