1use std::path::PathBuf;
6
7use iota_genesis_builder::validator_info::GenesisValidatorMetadata;
8use iota_move_build::{BuildConfig, CompiledPackage};
9use iota_sdk::{
10 rpc_types::{
11 IotaObjectDataOptions, IotaTransactionBlockEffectsAPI, IotaTransactionBlockResponse,
12 get_new_package_obj_from_response,
13 },
14 wallet_context::WalletContext,
15};
16use iota_types::{
17 IOTA_RANDOMNESS_STATE_OBJECT_ID, IOTA_SYSTEM_PACKAGE_ID, TypeTag,
18 base_types::{IotaAddress, ObjectID, ObjectRef, SequenceNumber},
19 crypto::{AccountKeyPair, Signature, Signer, get_key_pair},
20 digests::TransactionDigest,
21 iota_system_state::IOTA_SYSTEM_MODULE_NAME,
22 multisig::{BitmapUnit, MultiSig, MultiSigPublicKey},
23 object::Owner,
24 signature::GenericSignature,
25 transaction::{
26 CallArg, DEFAULT_VALIDATOR_GAS_PRICE, ObjectArg, ProgrammableTransaction,
27 TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE, TEST_ONLY_GAS_UNIT_FOR_TRANSFER,
28 Transaction, TransactionData,
29 },
30 utils::to_sender_signed_transaction,
31};
32use move_core_types::ident_str;
33use shared_crypto::intent::{Intent, IntentMessage};
34
35pub struct TestTransactionBuilder {
36 test_data: TestTransactionData,
37 sender: IotaAddress,
38 gas_object: ObjectRef,
39 gas_price: u64,
40 gas_budget: Option<u64>,
41}
42
43impl TestTransactionBuilder {
44 pub fn new(sender: IotaAddress, gas_object: ObjectRef, gas_price: u64) -> Self {
45 Self {
46 test_data: TestTransactionData::Empty,
47 sender,
48 gas_object,
49 gas_price,
50 gas_budget: None,
51 }
52 }
53
54 pub fn sender(&self) -> IotaAddress {
55 self.sender
56 }
57
58 pub fn gas_object(&self) -> ObjectRef {
59 self.gas_object
60 }
61
62 pub fn move_call(
64 mut self,
65 package_id: ObjectID,
66 module: &'static str,
67 function: &'static str,
68 args: Vec<CallArg>,
69 ) -> Self {
70 assert!(matches!(self.test_data, TestTransactionData::Empty));
71 self.test_data = TestTransactionData::Move(MoveData {
72 package_id,
73 module,
74 function,
75 args,
76 type_args: vec![],
77 });
78 self
79 }
80
81 pub fn with_type_args(mut self, type_args: Vec<TypeTag>) -> Self {
82 if let TestTransactionData::Move(data) = &mut self.test_data {
83 assert!(data.type_args.is_empty());
84 data.type_args = type_args;
85 } else {
86 panic!("Cannot set type args for non-move call");
87 }
88 self
89 }
90
91 pub fn with_gas_budget(mut self, gas_budget: u64) -> Self {
92 self.gas_budget = Some(gas_budget);
93 self
94 }
95
96 pub fn call_counter_create(self, package_id: ObjectID) -> Self {
97 self.move_call(package_id, "counter", "create", vec![])
98 }
99
100 pub fn call_counter_increment(
101 self,
102 package_id: ObjectID,
103 counter_id: ObjectID,
104 counter_initial_shared_version: SequenceNumber,
105 ) -> Self {
106 self.move_call(
107 package_id,
108 "counter",
109 "increment",
110 vec![CallArg::Object(ObjectArg::SharedObject {
111 id: counter_id,
112 initial_shared_version: counter_initial_shared_version,
113 mutable: true,
114 })],
115 )
116 }
117
118 pub fn call_counter_read(
119 self,
120 package_id: ObjectID,
121 counter_id: ObjectID,
122 counter_initial_shared_version: SequenceNumber,
123 ) -> Self {
124 self.move_call(
125 package_id,
126 "counter",
127 "value",
128 vec![CallArg::Object(ObjectArg::SharedObject {
129 id: counter_id,
130 initial_shared_version: counter_initial_shared_version,
131 mutable: false,
132 })],
133 )
134 }
135
136 pub fn call_counter_delete(
137 self,
138 package_id: ObjectID,
139 counter_id: ObjectID,
140 counter_initial_shared_version: SequenceNumber,
141 ) -> Self {
142 self.move_call(
143 package_id,
144 "counter",
145 "delete",
146 vec![CallArg::Object(ObjectArg::SharedObject {
147 id: counter_id,
148 initial_shared_version: counter_initial_shared_version,
149 mutable: true,
150 })],
151 )
152 }
153
154 pub fn call_nft_create(self, package_id: ObjectID) -> Self {
155 self.move_call(
156 package_id,
157 "testnet_nft",
158 "mint_to_sender",
159 vec![
160 CallArg::Pure(bcs::to_bytes("example_nft_name").unwrap()),
161 CallArg::Pure(bcs::to_bytes("example_nft_description").unwrap()),
162 CallArg::Pure(
163 bcs::to_bytes("https://iota.org/_nuxt/img/iota-logo.8d3c44e.svg").unwrap(),
164 ),
165 ],
166 )
167 }
168
169 pub fn call_nft_delete(self, package_id: ObjectID, nft_to_delete: ObjectRef) -> Self {
170 self.move_call(
171 package_id,
172 "testnet_nft",
173 "burn",
174 vec![CallArg::Object(ObjectArg::ImmOrOwnedObject(nft_to_delete))],
175 )
176 }
177
178 pub fn call_staking(self, stake_coin: ObjectRef, validator: IotaAddress) -> Self {
179 self.move_call(
180 IOTA_SYSTEM_PACKAGE_ID,
181 IOTA_SYSTEM_MODULE_NAME.as_str(),
182 "request_add_stake",
183 vec![
184 CallArg::IOTA_SYSTEM_MUT,
185 CallArg::Object(ObjectArg::ImmOrOwnedObject(stake_coin)),
186 CallArg::Pure(bcs::to_bytes(&validator).unwrap()),
187 ],
188 )
189 }
190
191 pub fn call_emit_random(
192 self,
193 package_id: ObjectID,
194 randomness_initial_shared_version: SequenceNumber,
195 ) -> Self {
196 self.move_call(
197 package_id,
198 "random",
199 "new",
200 vec![CallArg::Object(ObjectArg::SharedObject {
201 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
202 initial_shared_version: randomness_initial_shared_version,
203 mutable: false,
204 })],
205 )
206 }
207
208 pub fn call_request_add_validator(self) -> Self {
209 self.move_call(
210 IOTA_SYSTEM_PACKAGE_ID,
211 IOTA_SYSTEM_MODULE_NAME.as_str(),
212 "request_add_validator",
213 vec![CallArg::IOTA_SYSTEM_MUT],
214 )
215 }
216
217 pub fn call_request_add_validator_candidate(
218 self,
219 validator: &GenesisValidatorMetadata,
220 ) -> Self {
221 self.move_call(
222 IOTA_SYSTEM_PACKAGE_ID,
223 IOTA_SYSTEM_MODULE_NAME.as_str(),
224 "request_add_validator_candidate",
225 vec![
226 CallArg::IOTA_SYSTEM_MUT,
227 CallArg::Pure(bcs::to_bytes(&validator.authority_public_key).unwrap()),
228 CallArg::Pure(bcs::to_bytes(&validator.network_public_key).unwrap()),
229 CallArg::Pure(bcs::to_bytes(&validator.protocol_public_key).unwrap()),
230 CallArg::Pure(bcs::to_bytes(&validator.proof_of_possession).unwrap()),
231 CallArg::Pure(bcs::to_bytes(validator.name.as_bytes()).unwrap()),
232 CallArg::Pure(bcs::to_bytes(validator.description.as_bytes()).unwrap()),
233 CallArg::Pure(bcs::to_bytes(validator.image_url.as_bytes()).unwrap()),
234 CallArg::Pure(bcs::to_bytes(validator.project_url.as_bytes()).unwrap()),
235 CallArg::Pure(bcs::to_bytes(&validator.network_address).unwrap()),
236 CallArg::Pure(bcs::to_bytes(&validator.p2p_address).unwrap()),
237 CallArg::Pure(bcs::to_bytes(&validator.primary_address).unwrap()),
238 CallArg::Pure(bcs::to_bytes(&DEFAULT_VALIDATOR_GAS_PRICE).unwrap()), CallArg::Pure(bcs::to_bytes(&0u64).unwrap()), ],
241 )
242 }
243
244 pub fn call_request_remove_validator(self) -> Self {
245 self.move_call(
246 IOTA_SYSTEM_PACKAGE_ID,
247 IOTA_SYSTEM_MODULE_NAME.as_str(),
248 "request_remove_validator",
249 vec![CallArg::IOTA_SYSTEM_MUT],
250 )
251 }
252
253 pub fn transfer(mut self, object: ObjectRef, recipient: IotaAddress) -> Self {
254 self.test_data = TestTransactionData::Transfer(TransferData { object, recipient });
255 self
256 }
257
258 pub fn transfer_iota(mut self, amount: Option<u64>, recipient: IotaAddress) -> Self {
259 self.test_data = TestTransactionData::TransferIota(TransferIotaData { amount, recipient });
260 self
261 }
262
263 pub fn split_coin(mut self, coin: ObjectRef, amounts: Vec<u64>) -> Self {
264 self.test_data = TestTransactionData::SplitCoin(SplitCoinData { coin, amounts });
265 self
266 }
267
268 pub fn publish(mut self, path: PathBuf) -> Self {
269 assert!(matches!(self.test_data, TestTransactionData::Empty));
270 self.test_data = TestTransactionData::Publish(PublishData::Source(path, false));
271 self
272 }
273
274 pub fn publish_with_deps(mut self, path: PathBuf) -> Self {
275 assert!(matches!(self.test_data, TestTransactionData::Empty));
276 self.test_data = TestTransactionData::Publish(PublishData::Source(path, true));
277 self
278 }
279
280 pub fn publish_with_data(mut self, data: PublishData) -> Self {
281 assert!(matches!(self.test_data, TestTransactionData::Empty));
282 self.test_data = TestTransactionData::Publish(data);
283 self
284 }
285
286 pub fn publish_examples(self, subpath: &'static str) -> Self {
287 let path = if let Ok(p) = std::env::var("MOVE_EXAMPLES_DIR") {
288 let mut path = PathBuf::from(p);
289 path.extend([subpath]);
290 path
291 } else {
292 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
293 path.extend(["..", "..", "examples", "move", subpath]);
294 path
295 };
296 self.publish(path)
297 }
298
299 pub fn programmable(mut self, programmable: ProgrammableTransaction) -> Self {
300 self.test_data = TestTransactionData::Programmable(programmable);
301 self
302 }
303
304 pub fn build(self) -> TransactionData {
305 match self.test_data {
306 TestTransactionData::Move(data) => TransactionData::new_move_call(
307 self.sender,
308 data.package_id,
309 ident_str!(data.module).to_owned(),
310 ident_str!(data.function).to_owned(),
311 data.type_args,
312 self.gas_object,
313 data.args,
314 self.gas_budget
315 .unwrap_or(self.gas_price * TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE),
316 self.gas_price,
317 )
318 .unwrap(),
319 TestTransactionData::Transfer(data) => TransactionData::new_transfer(
320 data.recipient,
321 data.object,
322 self.sender,
323 self.gas_object,
324 self.gas_budget
325 .unwrap_or(self.gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
326 self.gas_price,
327 ),
328 TestTransactionData::TransferIota(data) => TransactionData::new_transfer_iota(
329 data.recipient,
330 self.sender,
331 data.amount,
332 self.gas_object,
333 self.gas_budget
334 .unwrap_or(self.gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
335 self.gas_price,
336 ),
337 TestTransactionData::SplitCoin(data) => TransactionData::new_split_coin(
338 self.sender,
339 data.coin,
340 data.amounts,
341 self.gas_object,
342 self.gas_budget
343 .unwrap_or(self.gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER),
344 self.gas_price,
345 ),
346 TestTransactionData::Publish(data) => {
347 let (all_module_bytes, dependencies) = match data {
348 PublishData::Source(path, with_unpublished_deps) => {
349 let compiled_package = BuildConfig::new_for_testing().build(&path).unwrap();
350 let all_module_bytes =
351 compiled_package.get_package_bytes(with_unpublished_deps);
352 let dependencies = compiled_package.get_dependency_storage_package_ids();
353 (all_module_bytes, dependencies)
354 }
355 PublishData::ModuleBytes(bytecode) => (bytecode, vec![]),
356 PublishData::CompiledPackage(compiled_package) => {
357 let all_module_bytes = compiled_package.get_package_bytes(false);
358 let dependencies = compiled_package.get_dependency_storage_package_ids();
359 (all_module_bytes, dependencies)
360 }
361 };
362
363 TransactionData::new_module(
364 self.sender,
365 self.gas_object,
366 all_module_bytes,
367 dependencies,
368 self.gas_budget.unwrap_or(
369 self.gas_price * TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE,
370 ),
371 self.gas_price,
372 )
373 }
374 TestTransactionData::Programmable(pt) => TransactionData::new_programmable(
375 self.sender,
376 vec![self.gas_object],
377 pt,
378 self.gas_budget
379 .unwrap_or(self.gas_price * TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE),
380 self.gas_price,
381 ),
382 TestTransactionData::Empty => {
383 panic!("Cannot build empty transaction");
384 }
385 }
386 }
387
388 pub fn build_and_sign(self, signer: &dyn Signer<Signature>) -> Transaction {
389 Transaction::from_data_and_signer(self.build(), vec![signer])
390 }
391
392 pub fn build_and_sign_multisig(
393 self,
394 multisig_pk: MultiSigPublicKey,
395 signers: &[&dyn Signer<Signature>],
396 bitmap: BitmapUnit,
397 ) -> Transaction {
398 let data = self.build();
399 let intent_msg = IntentMessage::new(Intent::iota_transaction(), data.clone());
400
401 let mut signatures = Vec::with_capacity(signers.len());
402 for signer in signers {
403 signatures.push(
404 GenericSignature::from(Signature::new_secure(&intent_msg, *signer))
405 .to_compressed()
406 .unwrap(),
407 );
408 }
409
410 let multisig =
411 GenericSignature::MultiSig(MultiSig::insecure_new(signatures, bitmap, multisig_pk));
412
413 Transaction::from_generic_sig_data(data, vec![multisig])
414 }
415}
416
417#[expect(clippy::large_enum_variant)]
418enum TestTransactionData {
419 Move(MoveData),
420 Transfer(TransferData),
421 TransferIota(TransferIotaData),
422 SplitCoin(SplitCoinData),
423 Publish(PublishData),
424 Programmable(ProgrammableTransaction),
425 Empty,
426}
427
428struct MoveData {
429 package_id: ObjectID,
430 module: &'static str,
431 function: &'static str,
432 args: Vec<CallArg>,
433 type_args: Vec<TypeTag>,
434}
435
436#[expect(clippy::large_enum_variant)]
437pub enum PublishData {
438 Source(PathBuf, bool),
442 ModuleBytes(Vec<Vec<u8>>),
443 CompiledPackage(CompiledPackage),
444}
445
446struct TransferData {
447 object: ObjectRef,
448 recipient: IotaAddress,
449}
450
451struct TransferIotaData {
452 amount: Option<u64>,
453 recipient: IotaAddress,
454}
455
456struct SplitCoinData {
457 coin: ObjectRef,
458 amounts: Vec<u64>,
459}
460
461pub async fn batch_make_transfer_transactions(
472 context: &WalletContext,
473 max_txn_num: usize,
474) -> Vec<Transaction> {
475 let recipient = get_key_pair::<AccountKeyPair>().0;
476 let result = context.get_all_accounts_and_gas_objects().await;
477 let accounts_and_objs = result.unwrap();
478 let mut res = Vec::with_capacity(max_txn_num);
479
480 let gas_price = context.get_reference_gas_price().await.unwrap();
481 for (address, objs) in accounts_and_objs {
482 for obj in objs {
483 if res.len() >= max_txn_num {
484 return res;
485 }
486 let data = TransactionData::new_transfer_iota(
487 recipient,
488 address,
489 Some(2),
490 obj,
491 gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER,
492 gas_price,
493 );
494 let tx = context.sign_transaction(&data);
495 res.push(tx);
496 }
497 }
498 res
499}
500
501pub async fn make_transfer_iota_transaction(
502 context: &WalletContext,
503 recipient: Option<IotaAddress>,
504 amount: Option<u64>,
505) -> Transaction {
506 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
507 let gas_price = context.get_reference_gas_price().await.unwrap();
508 context.sign_transaction(
509 &TestTransactionBuilder::new(sender, gas_object, gas_price)
510 .transfer_iota(amount, recipient.unwrap_or(sender))
511 .build(),
512 )
513}
514
515pub async fn make_staking_transaction(
516 context: &WalletContext,
517 validator_address: IotaAddress,
518) -> Transaction {
519 let accounts_and_objs = context.get_all_accounts_and_gas_objects().await.unwrap();
520 let sender = accounts_and_objs[0].0;
521 let gas_object = accounts_and_objs[0].1[0];
522 let stake_object = accounts_and_objs[0].1[1];
523 let gas_price = context.get_reference_gas_price().await.unwrap();
524 context.sign_transaction(
525 &TestTransactionBuilder::new(sender, gas_object, gas_price)
526 .call_staking(stake_object, validator_address)
527 .build(),
528 )
529}
530
531pub async fn make_publish_transaction(context: &WalletContext, path: PathBuf) -> Transaction {
532 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
533 let gas_price = context.get_reference_gas_price().await.unwrap();
534 context.sign_transaction(
535 &TestTransactionBuilder::new(sender, gas_object, gas_price)
536 .publish(path)
537 .build(),
538 )
539}
540
541pub async fn make_publish_transaction_with_deps(
542 context: &WalletContext,
543 path: PathBuf,
544) -> Transaction {
545 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
546 let gas_price = context.get_reference_gas_price().await.unwrap();
547 context.sign_transaction(
548 &TestTransactionBuilder::new(sender, gas_object, gas_price)
549 .publish_with_deps(path)
550 .build(),
551 )
552}
553
554pub async fn publish_package(context: &WalletContext, path: PathBuf) -> ObjectRef {
555 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
556 let gas_price = context.get_reference_gas_price().await.unwrap();
557 let txn = context.sign_transaction(
558 &TestTransactionBuilder::new(sender, gas_object, gas_price)
559 .publish(path)
560 .build(),
561 );
562 let resp = context.execute_transaction_must_succeed(txn).await;
563 get_new_package_obj_from_response(&resp).unwrap()
564}
565
566pub async fn publish_basics_package(context: &WalletContext) -> ObjectRef {
569 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
570 let gas_price = context.get_reference_gas_price().await.unwrap();
571 let txn = context.sign_transaction(
572 &TestTransactionBuilder::new(sender, gas_object, gas_price)
573 .publish_examples("basics")
574 .build(),
575 );
576 let resp = context.execute_transaction_must_succeed(txn).await;
577 get_new_package_obj_from_response(&resp).unwrap()
578}
579
580pub async fn publish_basics_package_and_make_counter(
583 context: &WalletContext,
584) -> (ObjectRef, ObjectRef) {
585 let package_ref = publish_basics_package(context).await;
586 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
587 let gas_price = context.get_reference_gas_price().await.unwrap();
588 let counter_creation_txn = context.sign_transaction(
589 &TestTransactionBuilder::new(sender, gas_object, gas_price)
590 .call_counter_create(package_ref.0)
591 .build(),
592 );
593 let resp = context
594 .execute_transaction_must_succeed(counter_creation_txn)
595 .await;
596 let counter_ref = resp
597 .effects
598 .unwrap()
599 .created()
600 .iter()
601 .find(|obj_ref| matches!(obj_ref.owner, Owner::Shared { .. }))
602 .unwrap()
603 .reference
604 .to_object_ref();
605 (package_ref, counter_ref)
606}
607
608pub async fn increment_counter(
611 context: &WalletContext,
612 sender: IotaAddress,
613 gas_object_id: Option<ObjectID>,
614 package_id: ObjectID,
615 counter_id: ObjectID,
616 initial_shared_version: SequenceNumber,
617) -> IotaTransactionBlockResponse {
618 let gas_object = if let Some(gas_object_id) = gas_object_id {
619 context.get_object_ref(gas_object_id).await.unwrap()
620 } else {
621 context
622 .get_one_gas_object_owned_by_address(sender)
623 .await
624 .unwrap()
625 .unwrap()
626 };
627 let rgp = context.get_reference_gas_price().await.unwrap();
628 let txn = context.sign_transaction(
629 &TestTransactionBuilder::new(sender, gas_object, rgp)
630 .call_counter_increment(package_id, counter_id, initial_shared_version)
631 .build(),
632 );
633 context.execute_transaction_must_succeed(txn).await
634}
635
636pub async fn emit_new_random_u128(
639 context: &WalletContext,
640 package_id: ObjectID,
641) -> IotaTransactionBlockResponse {
642 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
643 let rgp = context.get_reference_gas_price().await.unwrap();
644
645 let client = context.get_client().await.unwrap();
646 let random_obj = client
647 .read_api()
648 .get_object_with_options(
649 IOTA_RANDOMNESS_STATE_OBJECT_ID,
650 IotaObjectDataOptions::new().with_owner(),
651 )
652 .await
653 .unwrap()
654 .into_object()
655 .unwrap();
656 let random_obj_owner = random_obj
657 .owner
658 .expect("Expect Randomness object to have an owner");
659
660 let Owner::Shared {
661 initial_shared_version,
662 } = random_obj_owner
663 else {
664 panic!("Expect Randomness to be shared object")
665 };
666 let random_call_arg = CallArg::Object(ObjectArg::SharedObject {
667 id: IOTA_RANDOMNESS_STATE_OBJECT_ID,
668 initial_shared_version,
669 mutable: false,
670 });
671
672 let txn = context.sign_transaction(
673 &TestTransactionBuilder::new(sender, gas_object, rgp)
674 .move_call(package_id, "random", "new", vec![random_call_arg])
675 .build(),
676 );
677 context.execute_transaction_must_succeed(txn).await
678}
679
680pub async fn publish_example_package(
683 context: &WalletContext,
684 example_subpath: &'static str,
685 sender_key_pair: &AccountKeyPair,
686 sender: IotaAddress,
687 gas: ObjectRef,
688) -> (ObjectID, TransactionDigest) {
689 let gas_price = context.get_reference_gas_price().await.unwrap();
690 let tx = to_sender_signed_transaction(
691 TestTransactionBuilder::new(sender, gas, gas_price)
692 .publish_examples(example_subpath)
693 .build(),
694 sender_key_pair,
695 );
696
697 let resp = context.execute_transaction_must_succeed(tx).await;
698 let package_id = get_new_package_obj_from_response(&resp).unwrap().0;
699 (package_id, resp.digest)
700}
701
702pub async fn publish_nfts_package(
705 context: &WalletContext,
706) -> (ObjectID, ObjectID, TransactionDigest) {
707 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
708 let gas_id = gas_object.0;
709 let gas_price = context.get_reference_gas_price().await.unwrap();
710 let txn = context.sign_transaction(
711 &TestTransactionBuilder::new(sender, gas_object, gas_price)
712 .publish_examples("nft")
713 .build(),
714 );
715 let resp = context.execute_transaction_must_succeed(txn).await;
716 let package_id = get_new_package_obj_from_response(&resp).unwrap().0;
717 (package_id, gas_id, resp.digest)
718}
719
720pub async fn publish_simple_warrior_package(
723 context: &WalletContext,
724 sender_key_pair: &AccountKeyPair,
725 sender: IotaAddress,
726 gas: ObjectRef,
727) -> (ObjectID, TransactionDigest) {
728 publish_example_package(context, "simple_warrior", sender_key_pair, sender, gas).await
729}
730
731pub async fn create_nft(
735 context: &WalletContext,
736 package_id: ObjectID,
737) -> (IotaAddress, ObjectID, TransactionDigest) {
738 let (sender, gas_object) = context.get_one_gas_object().await.unwrap().unwrap();
739 let rgp = context.get_reference_gas_price().await.unwrap();
740
741 let txn = context.sign_transaction(
742 &TestTransactionBuilder::new(sender, gas_object, rgp)
743 .call_nft_create(package_id)
744 .build(),
745 );
746 let resp = context.execute_transaction_must_succeed(txn).await;
747
748 let object_id = resp
749 .effects
750 .as_ref()
751 .unwrap()
752 .created()
753 .first()
754 .unwrap()
755 .reference
756 .object_id;
757
758 (sender, object_id, resp.digest)
759}
760
761pub async fn delete_nft(
763 context: &WalletContext,
764 sender: IotaAddress,
765 package_id: ObjectID,
766 nft_to_delete: ObjectRef,
767) -> IotaTransactionBlockResponse {
768 let gas = context
769 .get_one_gas_object_owned_by_address(sender)
770 .await
771 .unwrap()
772 .unwrap_or_else(|| panic!("Expect {sender} to have at least one gas object"));
773 let rgp = context.get_reference_gas_price().await.unwrap();
774 let txn = context.sign_transaction(
775 &TestTransactionBuilder::new(sender, gas, rgp)
776 .call_nft_delete(package_id, nft_to_delete)
777 .build(),
778 );
779 context.execute_transaction_must_succeed(txn).await
780}