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