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