22// SPDX-License-Identifier: Apache-2.0
33
44use move_core_types:: ident_str;
5+ use move_core_types:: u256:: U256 ;
56use shared_crypto:: intent:: { Intent , IntentMessage } ;
67use std:: path:: PathBuf ;
78use sui_genesis_builder:: validator_info:: GenesisValidatorMetadata ;
@@ -10,23 +11,52 @@ use sui_sdk::rpc_types::{
1011 SuiObjectDataOptions , SuiTransactionBlockEffectsAPI , SuiTransactionBlockResponse ,
1112} ;
1213use sui_sdk:: wallet_context:: WalletContext ;
13- use sui_types:: SUI_RANDOMNESS_STATE_OBJECT_ID ;
14+ use sui_types:: balance :: Balance ;
1415use sui_types:: base_types:: { FullObjectRef , ObjectID , ObjectRef , SequenceNumber , SuiAddress } ;
1516use sui_types:: crypto:: { AccountKeyPair , Signature , Signer , get_key_pair} ;
1617use sui_types:: digests:: TransactionDigest ;
1718use sui_types:: gas_coin:: GAS ;
1819use sui_types:: multisig:: { BitmapUnit , MultiSig , MultiSigPublicKey } ;
1920use sui_types:: multisig_legacy:: { MultiSigLegacy , MultiSigPublicKeyLegacy } ;
2021use sui_types:: object:: Owner ;
22+ use sui_types:: programmable_transaction_builder:: ProgrammableTransactionBuilder ;
2123use sui_types:: signature:: GenericSignature ;
2224use sui_types:: sui_system_state:: SUI_SYSTEM_MODULE_NAME ;
2325use sui_types:: transaction:: {
24- CallArg , DEFAULT_VALIDATOR_GAS_PRICE , ObjectArg , ProgrammableTransaction ,
25- SharedObjectMutability , TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE ,
26- TEST_ONLY_GAS_UNIT_FOR_TRANSFER , Transaction , TransactionData ,
26+ Argument , CallArg , DEFAULT_VALIDATOR_GAS_PRICE , FundsWithdrawalArg , ObjectArg ,
27+ ProgrammableTransaction , SharedObjectMutability ,
28+ TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE , TEST_ONLY_GAS_UNIT_FOR_TRANSFER , Transaction ,
29+ TransactionData ,
2730} ;
31+ use sui_types:: { Identifier , SUI_FRAMEWORK_PACKAGE_ID , SUI_RANDOMNESS_STATE_OBJECT_ID } ;
2832use sui_types:: { SUI_SYSTEM_PACKAGE_ID , TypeTag } ;
2933
34+ #[ derive( Clone ) ]
35+ pub enum FundSource {
36+ Coin ( ObjectRef ) ,
37+ AddressFund {
38+ /// If None, it will be set the same as the total transfer amount.
39+ reservation : Option < u64 > ,
40+ } ,
41+ // TODO: Add object fund source
42+ }
43+
44+ impl FundSource {
45+ pub fn coin ( coin : ObjectRef ) -> Self {
46+ Self :: Coin ( coin)
47+ }
48+
49+ pub fn address_fund ( ) -> Self {
50+ Self :: AddressFund { reservation : None }
51+ }
52+
53+ pub fn address_fund_with_reservation ( reservation : u64 ) -> Self {
54+ Self :: AddressFund {
55+ reservation : Some ( reservation) ,
56+ }
57+ }
58+ }
59+
3060pub struct TestTransactionBuilder {
3161 test_data : TestTransactionData ,
3262 sender : SuiAddress ,
@@ -295,27 +325,31 @@ impl TestTransactionBuilder {
295325 self
296326 }
297327
298- pub fn transfer_sui_to_address_balance ( mut self , amount : u64 , recipient : SuiAddress ) -> Self {
328+ pub fn transfer_sui_to_address_balance (
329+ mut self ,
330+ source : FundSource ,
331+ amounts_and_recipients : Vec < ( u64 , SuiAddress ) > ,
332+ ) -> Self {
299333 self . test_data =
300334 TestTransactionData :: TransferFundsToAddressBalance ( TransferFundsToAddressBalanceData {
301- amount,
335+ source,
336+ amounts_and_recipients,
302337 type_arg : GAS :: type_tag ( ) ,
303- recipient,
304338 } ) ;
305339 self
306340 }
307341
308342 pub fn transfer_funds_to_address_balance (
309343 mut self ,
310- amount : u64 ,
344+ source : FundSource ,
345+ amounts_and_recipients : Vec < ( u64 , SuiAddress ) > ,
311346 type_arg : TypeTag ,
312- recipient : SuiAddress ,
313347 ) -> Self {
314348 self . test_data =
315349 TestTransactionData :: TransferFundsToAddressBalance ( TransferFundsToAddressBalanceData {
316- amount,
350+ source,
351+ amounts_and_recipients,
317352 type_arg,
318- recipient,
319353 } ) ;
320354 self
321355 }
@@ -395,17 +429,87 @@ impl TestTransactionBuilder {
395429 self . gas_price ,
396430 ) ,
397431 TestTransactionData :: TransferFundsToAddressBalance ( data) => {
398- TransactionData :: new_transfer_funds_to_address_balance (
399- data. recipient ,
432+ let mut builder = ProgrammableTransactionBuilder :: new ( ) ;
433+ let source = match data. source . clone ( ) {
434+ FundSource :: Coin ( coin) => {
435+ if coin == self . gas_object {
436+ Argument :: GasCoin
437+ } else {
438+ builder. obj ( ObjectArg :: ImmOrOwnedObject ( coin) ) . unwrap ( )
439+ }
440+ }
441+ FundSource :: AddressFund { reservation } => {
442+ let reservation = reservation. unwrap_or_else ( || {
443+ data. amounts_and_recipients
444+ . iter ( )
445+ . map ( |( amount, _) | * amount)
446+ . sum :: < u64 > ( )
447+ } ) ;
448+ builder
449+ . funds_withdrawal ( FundsWithdrawalArg :: balance_from_sender (
450+ reservation,
451+ data. type_arg . clone ( ) . into ( ) ,
452+ ) )
453+ . unwrap ( )
454+ }
455+ } ;
456+ for ( amount, recipient) in data. amounts_and_recipients {
457+ let balance = match data. source . clone ( ) {
458+ FundSource :: Coin ( _) => {
459+ let amount_arg = builder. pure ( amount) . unwrap ( ) ;
460+ let coin = builder. programmable_move_call (
461+ SUI_FRAMEWORK_PACKAGE_ID ,
462+ Identifier :: new ( "coin" ) . unwrap ( ) ,
463+ Identifier :: new ( "split" ) . unwrap ( ) ,
464+ vec ! [ data. type_arg. clone( ) ] ,
465+ vec ! [ source, amount_arg] ,
466+ ) ;
467+ builder. programmable_move_call (
468+ SUI_FRAMEWORK_PACKAGE_ID ,
469+ Identifier :: new ( "coin" ) . unwrap ( ) ,
470+ Identifier :: new ( "into_balance" ) . unwrap ( ) ,
471+ vec ! [ data. type_arg. clone( ) ] ,
472+ vec ! [ coin] ,
473+ )
474+ }
475+ FundSource :: AddressFund { .. } => {
476+ let amount_arg = builder. pure ( U256 :: from ( amount) ) . unwrap ( ) ;
477+ let split = builder. programmable_move_call (
478+ SUI_FRAMEWORK_PACKAGE_ID ,
479+ Identifier :: new ( "funds_accumulator" ) . unwrap ( ) ,
480+ Identifier :: new ( "withdrawal_split" ) . unwrap ( ) ,
481+ vec ! [ Balance :: type_tag( data. type_arg. clone( ) ) ] ,
482+ vec ! [ source, amount_arg] ,
483+ ) ;
484+ builder. programmable_move_call (
485+ SUI_FRAMEWORK_PACKAGE_ID ,
486+ Identifier :: new ( "balance" ) . unwrap ( ) ,
487+ Identifier :: new ( "redeem_funds" ) . unwrap ( ) ,
488+ vec ! [ data. type_arg. clone( ) ] ,
489+ vec ! [ split] ,
490+ )
491+ }
492+ } ;
493+
494+ let recipient_arg = builder. pure ( recipient) . unwrap ( ) ;
495+ builder. programmable_move_call (
496+ SUI_FRAMEWORK_PACKAGE_ID ,
497+ Identifier :: new ( "balance" ) . unwrap ( ) ,
498+ Identifier :: new ( "send_funds" ) . unwrap ( ) ,
499+ vec ! [ data. type_arg. clone( ) ] ,
500+ vec ! [ balance, recipient_arg] ,
501+ ) ;
502+ }
503+ let pt = builder. finish ( ) ;
504+ TransactionData :: new_programmable (
400505 self . sender ,
401- data . amount ,
402- data . type_arg ,
403- self . gas_object ,
404- self . gas_budget
405- . unwrap_or ( self . gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER ) ,
506+ vec ! [ self . gas_object ] ,
507+ pt ,
508+ self . gas_budget . unwrap_or (
509+ self . gas_price * TEST_ONLY_GAS_UNIT_FOR_HEAVY_COMPUTATION_STORAGE ,
510+ ) ,
406511 self . gas_price ,
407512 )
408- . unwrap ( )
409513 }
410514 TestTransactionData :: SplitCoin ( data) => TransactionData :: new_split_coin (
411515 self . sender ,
@@ -548,9 +652,9 @@ struct TransferSuiData {
548652}
549653
550654struct TransferFundsToAddressBalanceData {
551- amount : u64 ,
655+ source : FundSource ,
656+ amounts_and_recipients : Vec < ( u64 , SuiAddress ) > ,
552657 type_arg : TypeTag ,
553- recipient : SuiAddress ,
554658}
555659
556660struct SplitCoinData {
0 commit comments