Skip to main content

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