3838 message:: Message ,
3939 native_token:: { self , Sol } ,
4040 signature:: { Keypair , Signer } ,
41+ signer:: null_signer:: NullSigner ,
4142 signers:: Signers ,
4243 transaction:: Transaction ,
4344 } ,
@@ -70,6 +71,8 @@ pub(crate) struct Config {
7071 token_owner : Box < dyn Signer > ,
7172 fee_payer : Box < dyn Signer > ,
7273 dry_run : bool ,
74+ sign_only : bool ,
75+ multisig_signers : Vec < NullSigner > ,
7376 no_update : bool ,
7477 compute_unit_price : Option < u64 > ,
7578 compute_unit_limit : ComputeUnitLimit ,
@@ -205,7 +208,11 @@ fn send_transaction_no_wait(
205208 config : & Config ,
206209 transaction : Transaction ,
207210) -> solana_client:: client_error:: Result < ( ) > {
208- if config. dry_run {
211+ if config. sign_only {
212+ let serialized = bincode:: serialize ( & transaction) . unwrap ( ) ;
213+ let encoded = bs58:: encode ( & serialized) . into_string ( ) ;
214+ println ! ( "{}" , encoded) ;
215+ } else if config. dry_run {
209216 let result = config. rpc_client . simulate_transaction ( & transaction) ?;
210217 println ! ( "Simulate result: {:?}" , result) ;
211218 } else {
@@ -219,7 +226,11 @@ fn send_transaction(
219226 config : & Config ,
220227 transaction : Transaction ,
221228) -> solana_client:: client_error:: Result < ( ) > {
222- if config. dry_run {
229+ if config. sign_only {
230+ let serialized = bincode:: serialize ( & transaction) . unwrap ( ) ;
231+ let encoded = bs58:: encode ( & serialized) . into_string ( ) ;
232+ println ! ( "{}" , encoded) ;
233+ } else if config. dry_run {
223234 let result = config. rpc_client . simulate_transaction ( & transaction) ?;
224235 println ! ( "Simulate result: {:?}" , result) ;
225236 } else {
@@ -265,10 +276,12 @@ fn checked_transaction_with_signers_and_additional_fee<T: Signers>(
265276 Some ( & config. fee_payer . pubkey ( ) ) ,
266277 & recent_blockhash,
267278 ) ;
268- check_fee_payer_balance (
269- config,
270- additional_fee. saturating_add ( config. rpc_client . get_fee_for_message ( & message) ?) ,
271- ) ?;
279+ if !config. sign_only {
280+ check_fee_payer_balance (
281+ config,
282+ additional_fee. saturating_add ( config. rpc_client . get_fee_for_message ( & message) ?) ,
283+ ) ?;
284+ }
272285 let transaction = Transaction :: new ( signers, message, recent_blockhash) ;
273286 Ok ( transaction)
274287}
@@ -2097,18 +2110,26 @@ fn command_set_manager(
20972110 config : & Config ,
20982111 stake_pool_address : & Pubkey ,
20992112 new_manager : & Option < Box < dyn Signer > > ,
2113+ new_manager_pubkey : & Option < Pubkey > ,
21002114 new_fee_receiver : & Option < Pubkey > ,
21012115) -> CommandResult {
21022116 if !config. no_update {
21032117 command_update ( config, stake_pool_address, false , false , false ) ?;
21042118 }
21052119 let stake_pool = get_stake_pool ( & config. rpc_client , stake_pool_address) ?;
21062120
2107- // If new accounts are missing in the arguments use the old ones
2108- let ( new_manager_pubkey, mut signers) : ( Pubkey , Vec < & dyn Signer > ) = match new_manager {
2109- None => ( stake_pool. manager , vec ! [ ] ) ,
2110- Some ( value) => ( value. pubkey ( ) , vec ! [ value. as_ref( ) ] ) ,
2111- } ;
2121+ // Determine new manager pubkey and signers
2122+ let ( new_manager_pk, mut signers) : ( Pubkey , Vec < & dyn Signer > ) =
2123+ if let Some ( signer) = new_manager {
2124+ // New manager provided as keypair
2125+ ( signer. pubkey ( ) , vec ! [ signer. as_ref( ) ] )
2126+ } else if let Some ( pubkey) = new_manager_pubkey {
2127+ // New manager provided as pubkey only (for multisig vaults)
2128+ ( * pubkey, vec ! [ ] )
2129+ } else {
2130+ // Keep current manager
2131+ ( stake_pool. manager , vec ! [ ] )
2132+ } ;
21122133
21132134 let new_fee_receiver = match new_fee_receiver {
21142135 None => stake_pool. manager_fee_account ,
@@ -2137,7 +2158,7 @@ fn command_set_manager(
21372158 & config. stake_pool_program_id ,
21382159 stake_pool_address,
21392160 & config. manager . pubkey ( ) ,
2140- & new_manager_pubkey ,
2161+ & new_manager_pk ,
21412162 & new_fee_receiver,
21422163 ) ] ,
21432164 & signers,
@@ -2201,17 +2222,33 @@ fn command_set_fee(
22012222 stake_pool_address : & Pubkey ,
22022223 new_fee : FeeType ,
22032224) -> CommandResult {
2204- if !config. no_update {
2225+ if !config. no_update && !config . sign_only {
22052226 command_update ( config, stake_pool_address, false , false , false ) ?;
22062227 }
2207- let mut signers = vec ! [ config. fee_payer. as_ref( ) , config. manager. as_ref( ) ] ;
2208- unique_signers ! ( signers) ;
2228+
2229+ // Determine manager pubkey and signer based on mode
2230+ let ( manager_pubkey, signers) : ( Pubkey , Vec < & dyn Signer > ) =
2231+ if config. sign_only && !config. multisig_signers . is_empty ( ) {
2232+ // Use first multisig signer as manager for sign-only mode
2233+ let manager_pubkey = config. multisig_signers [ 0 ] . pubkey ( ) ;
2234+ let mut signers: Vec < & dyn Signer > = vec ! [ config. fee_payer. as_ref( ) ] ;
2235+ for signer in & config. multisig_signers {
2236+ signers. push ( signer) ;
2237+ }
2238+ ( manager_pubkey, signers)
2239+ } else {
2240+ let mut signers: Vec < & dyn Signer > =
2241+ vec ! [ config. fee_payer. as_ref( ) , config. manager. as_ref( ) ] ;
2242+ unique_signers ! ( signers) ;
2243+ ( config. manager . pubkey ( ) , signers)
2244+ } ;
2245+
22092246 let transaction = checked_transaction_with_signers (
22102247 config,
22112248 & [ spl_stake_pool:: instruction:: set_fee (
22122249 & config. stake_pool_program_id ,
22132250 stake_pool_address,
2214- & config . manager . pubkey ( ) ,
2251+ & manager_pubkey ,
22152252 new_fee,
22162253 ) ] ,
22172254 & signers,
@@ -2279,6 +2316,24 @@ fn main() {
22792316 . global ( true )
22802317 . help ( "Simulate transaction instead of executing" ) ,
22812318 )
2319+ . arg (
2320+ Arg :: with_name ( "sign_only" )
2321+ . long ( "sign-only" )
2322+ . takes_value ( false )
2323+ . global ( true )
2324+ . help ( "Output base58-encoded transaction for multisig import instead of executing" ) ,
2325+ )
2326+ . arg (
2327+ Arg :: with_name ( "multisig_signer" )
2328+ . long ( "multisig-signer" )
2329+ . value_name ( "PUBKEY" )
2330+ . validator ( is_valid_pubkey)
2331+ . takes_value ( true )
2332+ . multiple ( true )
2333+ . global ( true )
2334+ . requires ( "sign_only" )
2335+ . help ( "Pubkey of a multisig signer (requires --sign-only). Can be specified multiple times." ) ,
2336+ )
22822337 . arg (
22832338 Arg :: with_name ( "no_update" )
22842339 . long ( "no-update" )
@@ -2987,8 +3042,18 @@ fn main() {
29873042 . validator ( is_valid_signer)
29883043 . value_name ( "KEYPAIR" )
29893044 . takes_value ( true )
3045+ . conflicts_with ( "new_manager_pubkey" )
29903046 . help ( "Keypair for the new stake pool manager." ) ,
29913047 )
3048+ . arg (
3049+ Arg :: with_name ( "new_manager_pubkey" )
3050+ . long ( "new-manager-pubkey" )
3051+ . validator ( is_pubkey)
3052+ . value_name ( "PUBKEY" )
3053+ . takes_value ( true )
3054+ . conflicts_with ( "new_manager" )
3055+ . help ( "Public key for the new stake pool manager (use for multisig vaults)." ) ,
3056+ )
29923057 . arg (
29933058 Arg :: with_name ( "new_fee_receiver" )
29943059 . long ( "new-fee-receiver" )
@@ -2999,6 +3064,7 @@ fn main() {
29993064 )
30003065 . group ( ArgGroup :: with_name ( "new_accounts" )
30013066 . arg ( "new_manager" )
3067+ . arg ( "new_manager_pubkey" )
30023068 . arg ( "new_fee_receiver" )
30033069 . required ( true )
30043070 . multiple ( true )
@@ -3212,6 +3278,15 @@ fn main() {
32123278 OutputFormat :: Display
32133279 } ) ;
32143280 let dry_run = matches. is_present ( "dry_run" ) ;
3281+ let sign_only = matches. is_present ( "sign_only" ) ;
3282+ let multisig_signers: Vec < NullSigner > = matches
3283+ . values_of ( "multisig_signer" )
3284+ . map ( |values| {
3285+ values
3286+ . map ( |s| NullSigner :: new ( & s. parse :: < Pubkey > ( ) . unwrap ( ) ) )
3287+ . collect ( )
3288+ } )
3289+ . unwrap_or_default ( ) ;
32153290 let no_update = matches. is_present ( "no_update" ) ;
32163291 let compute_unit_price = value_t ! ( matches, COMPUTE_UNIT_PRICE_ARG . name, u64 ) . ok ( ) ;
32173292 let compute_unit_limit = matches
@@ -3239,6 +3314,8 @@ fn main() {
32393314 token_owner,
32403315 fee_payer,
32413316 dry_run,
3317+ sign_only,
3318+ multisig_signers,
32423319 no_update,
32433320 compute_unit_price,
32443321 compute_unit_limit,
@@ -3433,7 +3510,8 @@ fn main() {
34333510 ( "set-manager" , Some ( arg_matches) ) => {
34343511 let stake_pool_address = pubkey_of ( arg_matches, "pool" ) . unwrap ( ) ;
34353512
3436- let new_manager = if arg_matches. value_of ( "new_manager" ) . is_some ( ) {
3513+ let ( new_manager, new_manager_pubkey) = if arg_matches. value_of ( "new_manager" ) . is_some ( )
3514+ {
34373515 let signer = get_signer (
34383516 arg_matches,
34393517 "new-manager" ,
@@ -3445,16 +3523,19 @@ fn main() {
34453523 allow_null_signer : true ,
34463524 } ,
34473525 ) ;
3448- Some ( signer)
3526+ ( Some ( signer) , None )
3527+ } else if let Some ( pubkey) = pubkey_of ( arg_matches, "new_manager_pubkey" ) {
3528+ ( None , Some ( pubkey) )
34493529 } else {
3450- None
3530+ ( None , None )
34513531 } ;
34523532
34533533 let new_fee_receiver: Option < Pubkey > = pubkey_of ( arg_matches, "new_fee_receiver" ) ;
34543534 command_set_manager (
34553535 & config,
34563536 & stake_pool_address,
34573537 & new_manager,
3538+ & new_manager_pubkey,
34583539 & new_fee_receiver,
34593540 )
34603541 }
0 commit comments