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