solana_stake_interface/
instruction.rs

1// Remove the following `allow` when the `Redelegate` variant is renamed to
2// `Unused` starting from v3.
3// Required to avoid warnings from uses of deprecated types during trait derivations.
4#![allow(deprecated)]
5
6use {
7    crate::state::{Authorized, Lockup, StakeAuthorize},
8    solana_clock::{Epoch, UnixTimestamp},
9    solana_pubkey::Pubkey,
10};
11#[cfg(feature = "bincode")]
12use {
13    crate::{config, program::ID, state::StakeStateV2},
14    solana_instruction::{AccountMeta, Instruction},
15};
16
17// Inline some constants to avoid dependencies.
18//
19// Note: replace these inline IDs with the corresponding value from
20// `solana_sdk_ids` once the version is updated to 2.2.0.
21
22#[cfg(feature = "bincode")]
23const CLOCK_ID: Pubkey = Pubkey::from_str_const("SysvarC1ock11111111111111111111111111111111");
24
25#[cfg(feature = "bincode")]
26const RENT_ID: Pubkey = Pubkey::from_str_const("SysvarRent111111111111111111111111111111111");
27
28#[cfg(feature = "bincode")]
29const STAKE_HISTORY_ID: Pubkey =
30    Pubkey::from_str_const("SysvarStakeHistory1111111111111111111111111");
31
32#[cfg_attr(
33    feature = "serde",
34    derive(serde_derive::Deserialize, serde_derive::Serialize)
35)]
36#[derive(Debug, PartialEq, Eq, Clone)]
37pub enum StakeInstruction {
38    /// Initialize a stake with lockup and authorization information
39    ///
40    /// # Account references
41    ///   0. `[WRITE]` Uninitialized stake account
42    ///   1. `[]` Rent sysvar
43    ///
44    /// [`Authorized`] carries pubkeys that must sign staker transactions
45    /// and withdrawer transactions; [`Lockup`] carries information about
46    /// withdrawal restrictions.
47    Initialize(Authorized, Lockup),
48
49    /// Authorize a key to manage stake or withdrawal
50    ///
51    /// # Account references
52    ///   0. `[WRITE]` Stake account to be updated
53    ///   1. `[]` Clock sysvar
54    ///   2. `[SIGNER]` The stake or withdraw authority
55    ///   3. Optional: `[SIGNER]` Lockup authority, if updating StakeAuthorize::Withdrawer before
56    ///      lockup expiration
57    Authorize(Pubkey, StakeAuthorize),
58
59    /// Delegate a stake to a particular vote account
60    ///
61    /// # Account references
62    ///   0. `[WRITE]` Initialized stake account to be delegated
63    ///   1. `[]` Vote account to which this stake will be delegated
64    ///   2. `[]` Clock sysvar
65    ///   3. `[]` Stake history sysvar that carries stake warmup/cooldown history
66    ///   4. `[]` Unused account, formerly the stake config
67    ///   5. `[SIGNER]` Stake authority
68    ///
69    /// The entire balance of the staking account is staked. `DelegateStake`
70    /// can be called multiple times, but re-delegation is delayed by one epoch.
71    DelegateStake,
72
73    /// Split `u64` tokens and stake off a stake account into another stake account.
74    ///
75    /// # Account references
76    ///   0. `[WRITE]` Stake account to be split; must be in the Initialized or Stake state
77    ///   1. `[WRITE]` Uninitialized stake account that will take the split-off amount
78    ///   2. `[SIGNER]` Stake authority
79    Split(u64),
80
81    /// Withdraw unstaked lamports from the stake account
82    ///
83    /// # Account references
84    ///   0. `[WRITE]` Stake account from which to withdraw
85    ///   1. `[WRITE]` Recipient account
86    ///   2. `[]` Clock sysvar
87    ///   3. `[]` Stake history sysvar that carries stake warmup/cooldown history
88    ///   4. `[SIGNER]` Withdraw authority
89    ///   5. Optional: `[SIGNER]` Lockup authority, if before lockup expiration
90    ///
91    /// The `u64` is the portion of the stake account balance to be withdrawn,
92    /// must be `<= StakeAccount.lamports - staked_lamports`.
93    Withdraw(u64),
94
95    /// Deactivates the stake in the account
96    ///
97    /// # Account references
98    ///   0. `[WRITE]` Delegated stake account
99    ///   1. `[]` Clock sysvar
100    ///   2. `[SIGNER]` Stake authority
101    Deactivate,
102
103    /// Set stake lockup
104    ///
105    /// If a lockup is not active, the withdraw authority may set a new lockup
106    /// If a lockup is active, the lockup custodian may update the lockup parameters
107    ///
108    /// # Account references
109    ///   0. `[WRITE]` Initialized stake account
110    ///   1. `[SIGNER]` Lockup authority or withdraw authority
111    SetLockup(LockupArgs),
112
113    /// Merge two stake accounts.
114    ///
115    /// Both accounts must have identical lockup and authority keys. A merge
116    /// is possible between two stakes in the following states with no additional
117    /// conditions:
118    ///
119    /// * two deactivated stakes
120    /// * an inactive stake into an activating stake during its activation epoch
121    ///
122    /// For the following cases, the voter pubkey and vote credits observed must match:
123    ///
124    /// * two activated stakes
125    /// * two activating accounts that share an activation epoch, during the activation epoch
126    ///
127    /// All other combinations of stake states will fail to merge, including all
128    /// "transient" states, where a stake is activating or deactivating with a
129    /// non-zero effective stake.
130    ///
131    /// # Account references
132    ///   0. `[WRITE]` Destination stake account for the merge
133    ///   1. `[WRITE]` Source stake account for to merge.  This account will be drained
134    ///   2. `[]` Clock sysvar
135    ///   3. `[]` Stake history sysvar that carries stake warmup/cooldown history
136    ///   4. `[SIGNER]` Stake authority
137    Merge,
138
139    /// Authorize a key to manage stake or withdrawal with a derived key
140    ///
141    /// # Account references
142    ///   0. `[WRITE]` Stake account to be updated
143    ///   1. `[SIGNER]` Base key of stake or withdraw authority
144    ///   2. `[]` Clock sysvar
145    ///   3. Optional: `[SIGNER]` Lockup authority, if updating [`StakeAuthorize::Withdrawer`]
146    ///      before lockup expiration
147    AuthorizeWithSeed(AuthorizeWithSeedArgs),
148
149    /// Initialize a stake with authorization information
150    ///
151    /// This instruction is similar to `Initialize` except that the withdraw authority
152    /// must be a signer, and no lockup is applied to the account.
153    ///
154    /// # Account references
155    ///   0. `[WRITE]` Uninitialized stake account
156    ///   1. `[]` Rent sysvar
157    ///   2. `[]` The stake authority
158    ///   3. `[SIGNER]` The withdraw authority
159    InitializeChecked,
160
161    /// Authorize a key to manage stake or withdrawal
162    ///
163    /// This instruction behaves like `Authorize` with the additional requirement that the new
164    /// stake or withdraw authority must also be a signer.
165    ///
166    /// # Account references
167    ///   0. `[WRITE]` Stake account to be updated
168    ///   1. `[]` Clock sysvar
169    ///   2. `[SIGNER]` The stake or withdraw authority
170    ///   3. `[SIGNER]` The new stake or withdraw authority
171    ///   4. Optional: `[SIGNER]` Lockup authority, if updating [`StakeAuthorize::Withdrawer`]
172    ///      before lockup expiration
173    AuthorizeChecked(StakeAuthorize),
174
175    /// Authorize a key to manage stake or withdrawal with a derived key
176    ///
177    /// This instruction behaves like `AuthorizeWithSeed` with the additional requirement that
178    /// the new stake or withdraw authority must also be a signer.
179    ///
180    /// # Account references
181    ///   0. `[WRITE]` Stake account to be updated
182    ///   1. `[SIGNER]` Base key of stake or withdraw authority
183    ///   2. `[]` Clock sysvar
184    ///   3. `[SIGNER]` The new stake or withdraw authority
185    ///   4. Optional: `[SIGNER]` Lockup authority, if updating [`StakeAuthorize::Withdrawer`]
186    ///      before lockup expiration
187    AuthorizeCheckedWithSeed(AuthorizeCheckedWithSeedArgs),
188
189    /// Set stake lockup
190    ///
191    /// This instruction behaves like `SetLockup` with the additional requirement that
192    /// the new lockup authority also be a signer.
193    ///
194    /// If a lockup is not active, the withdraw authority may set a new lockup
195    /// If a lockup is active, the lockup custodian may update the lockup parameters
196    ///
197    /// # Account references
198    ///   0. `[WRITE]` Initialized stake account
199    ///   1. `[SIGNER]` Lockup authority or withdraw authority
200    ///   2. Optional: `[SIGNER]` New lockup authority
201    SetLockupChecked(LockupCheckedArgs),
202
203    /// Get the minimum stake delegation, in lamports
204    ///
205    /// # Account references
206    ///   None
207    ///
208    /// Returns the minimum delegation as a little-endian encoded u64 value.
209    /// Programs can use the [`get_minimum_delegation()`] helper function to invoke and
210    /// retrieve the return value for this instruction.
211    ///
212    /// [`get_minimum_delegation()`]: crate::tools::get_minimum_delegation
213    GetMinimumDelegation,
214
215    /// Deactivate stake delegated to a vote account that has been delinquent for at least
216    /// [`crate::MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION`] epochs.
217    ///
218    /// No signer is required for this instruction as it is a common good to deactivate abandoned
219    /// stake.
220    ///
221    /// # Account references
222    ///   0. `[WRITE]` Delegated stake account
223    ///   1. `[]` Delinquent vote account for the delegated stake account
224    ///   2. `[]` Reference vote account that has voted at least once in the last
225    ///      [`crate::MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION`] epochs
226    DeactivateDelinquent,
227
228    /// Redelegate activated stake to another vote account.
229    ///
230    /// Upon success:
231    ///   * the balance of the delegated stake account will be reduced to the undelegated amount in
232    ///     the account (rent exempt minimum and any additional lamports not part of the delegation),
233    ///     and scheduled for deactivation.
234    ///   * the provided uninitialized stake account will receive the original balance of the
235    ///     delegated stake account, minus the rent exempt minimum, and scheduled for activation to
236    ///     the provided vote account. Any existing lamports in the uninitialized stake account
237    ///     will also be included in the re-delegation.
238    ///
239    /// # Account references
240    ///   0. `[WRITE]` Delegated stake account to be redelegated. The account must be fully
241    ///      activated and carry a balance greater than or equal to the minimum delegation amount
242    ///      plus rent exempt minimum
243    ///   1. `[WRITE]` Uninitialized stake account that will hold the redelegated stake
244    ///   2. `[]` Vote account to which this stake will be re-delegated
245    ///   3. `[]` Unused account, formerly the stake config
246    ///   4. `[SIGNER]` Stake authority
247    ///
248    #[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
249    Redelegate,
250
251    /// Move stake between accounts with the same authorities and lockups, using Staker authority.
252    ///
253    /// The source account must be fully active. If its entire delegation is moved, it immediately
254    /// becomes inactive. Otherwise, at least the minimum delegation of active stake must remain.
255    ///
256    /// The destination account must be fully active or fully inactive. If it is active, it must
257    /// be delegated to the same vote account as the source. If it is inactive, it
258    /// immediately becomes active, and must contain at least the minimum delegation. The
259    /// destination must be pre-funded with the rent-exempt reserve.
260    ///
261    /// This instruction only affects or moves active stake. Additional unstaked lamports are never
262    /// moved, activated, or deactivated, and accounts are never deallocated.
263    ///
264    /// # Account references
265    ///   0. `[WRITE]` Active source stake account
266    ///   1. `[WRITE]` Active or inactive destination stake account
267    ///   2. `[SIGNER]` Stake authority
268    ///
269    /// The `u64` is the portion of the stake to move, which may be the entire delegation
270    MoveStake(u64),
271
272    /// Move unstaked lamports between accounts with the same authorities and lockups, using Staker
273    /// authority.
274    ///
275    /// The source account must be fully active or fully inactive. The destination may be in any
276    /// mergeable state (active, inactive, or activating, but not in warmup cooldown). Only lamports that
277    /// are neither backing a delegation nor required for rent-exemption may be moved.
278    ///
279    /// # Account references
280    ///   0. `[WRITE]` Active or inactive source stake account
281    ///   1. `[WRITE]` Mergeable destination stake account
282    ///   2. `[SIGNER]` Stake authority
283    ///
284    /// The `u64` is the portion of available lamports to move
285    MoveLamports(u64),
286}
287
288#[cfg_attr(
289    feature = "serde",
290    derive(serde_derive::Deserialize, serde_derive::Serialize)
291)]
292#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
293pub struct LockupArgs {
294    pub unix_timestamp: Option<UnixTimestamp>,
295    pub epoch: Option<Epoch>,
296    pub custodian: Option<Pubkey>,
297}
298
299#[cfg_attr(
300    feature = "serde",
301    derive(serde_derive::Deserialize, serde_derive::Serialize)
302)]
303#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
304pub struct LockupCheckedArgs {
305    pub unix_timestamp: Option<UnixTimestamp>,
306    pub epoch: Option<Epoch>,
307}
308
309#[cfg_attr(
310    feature = "serde",
311    derive(serde_derive::Deserialize, serde_derive::Serialize)
312)]
313#[derive(Debug, PartialEq, Eq, Clone)]
314pub struct AuthorizeWithSeedArgs {
315    pub new_authorized_pubkey: Pubkey,
316    pub stake_authorize: StakeAuthorize,
317    pub authority_seed: String,
318    pub authority_owner: Pubkey,
319}
320
321#[cfg_attr(
322    feature = "serde",
323    derive(serde_derive::Deserialize, serde_derive::Serialize)
324)]
325#[derive(Debug, PartialEq, Eq, Clone)]
326pub struct AuthorizeCheckedWithSeedArgs {
327    pub stake_authorize: StakeAuthorize,
328    pub authority_seed: String,
329    pub authority_owner: Pubkey,
330}
331
332#[cfg(feature = "bincode")]
333pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction {
334    Instruction::new_with_bincode(
335        ID,
336        &StakeInstruction::Initialize(*authorized, *lockup),
337        vec![
338            AccountMeta::new(*stake_pubkey, false),
339            AccountMeta::new_readonly(RENT_ID, false),
340        ],
341    )
342}
343
344#[cfg(feature = "bincode")]
345pub fn initialize_checked(stake_pubkey: &Pubkey, authorized: &Authorized) -> Instruction {
346    Instruction::new_with_bincode(
347        ID,
348        &StakeInstruction::InitializeChecked,
349        vec![
350            AccountMeta::new(*stake_pubkey, false),
351            AccountMeta::new_readonly(RENT_ID, false),
352            AccountMeta::new_readonly(authorized.staker, false),
353            AccountMeta::new_readonly(authorized.withdrawer, true),
354        ],
355    )
356}
357
358#[cfg(feature = "bincode")]
359pub fn create_account_with_seed(
360    from_pubkey: &Pubkey,
361    stake_pubkey: &Pubkey,
362    base: &Pubkey,
363    seed: &str,
364    authorized: &Authorized,
365    lockup: &Lockup,
366    lamports: u64,
367) -> Vec<Instruction> {
368    vec![
369        solana_system_interface::instruction::create_account_with_seed(
370            from_pubkey,
371            stake_pubkey,
372            base,
373            seed,
374            lamports,
375            StakeStateV2::size_of() as u64,
376            &ID,
377        ),
378        initialize(stake_pubkey, authorized, lockup),
379    ]
380}
381
382#[cfg(feature = "bincode")]
383pub fn create_account(
384    from_pubkey: &Pubkey,
385    stake_pubkey: &Pubkey,
386    authorized: &Authorized,
387    lockup: &Lockup,
388    lamports: u64,
389) -> Vec<Instruction> {
390    vec![
391        solana_system_interface::instruction::create_account(
392            from_pubkey,
393            stake_pubkey,
394            lamports,
395            StakeStateV2::size_of() as u64,
396            &ID,
397        ),
398        initialize(stake_pubkey, authorized, lockup),
399    ]
400}
401
402#[cfg(feature = "bincode")]
403pub fn create_account_with_seed_checked(
404    from_pubkey: &Pubkey,
405    stake_pubkey: &Pubkey,
406    base: &Pubkey,
407    seed: &str,
408    authorized: &Authorized,
409    lamports: u64,
410) -> Vec<Instruction> {
411    vec![
412        solana_system_interface::instruction::create_account_with_seed(
413            from_pubkey,
414            stake_pubkey,
415            base,
416            seed,
417            lamports,
418            StakeStateV2::size_of() as u64,
419            &ID,
420        ),
421        initialize_checked(stake_pubkey, authorized),
422    ]
423}
424
425#[cfg(feature = "bincode")]
426pub fn create_account_checked(
427    from_pubkey: &Pubkey,
428    stake_pubkey: &Pubkey,
429    authorized: &Authorized,
430    lamports: u64,
431) -> Vec<Instruction> {
432    vec![
433        solana_system_interface::instruction::create_account(
434            from_pubkey,
435            stake_pubkey,
436            lamports,
437            StakeStateV2::size_of() as u64,
438            &ID,
439        ),
440        initialize_checked(stake_pubkey, authorized),
441    ]
442}
443
444#[cfg(feature = "bincode")]
445fn _split(
446    stake_pubkey: &Pubkey,
447    authorized_pubkey: &Pubkey,
448    lamports: u64,
449    split_stake_pubkey: &Pubkey,
450) -> Instruction {
451    let account_metas = vec![
452        AccountMeta::new(*stake_pubkey, false),
453        AccountMeta::new(*split_stake_pubkey, false),
454        AccountMeta::new_readonly(*authorized_pubkey, true),
455    ];
456
457    Instruction::new_with_bincode(ID, &StakeInstruction::Split(lamports), account_metas)
458}
459
460#[cfg(feature = "bincode")]
461pub fn split(
462    stake_pubkey: &Pubkey,
463    authorized_pubkey: &Pubkey,
464    lamports: u64,
465    split_stake_pubkey: &Pubkey,
466) -> Vec<Instruction> {
467    vec![
468        solana_system_interface::instruction::allocate(
469            split_stake_pubkey,
470            StakeStateV2::size_of() as u64,
471        ),
472        solana_system_interface::instruction::assign(split_stake_pubkey, &ID),
473        _split(
474            stake_pubkey,
475            authorized_pubkey,
476            lamports,
477            split_stake_pubkey,
478        ),
479    ]
480}
481
482#[cfg(feature = "bincode")]
483pub fn split_with_seed(
484    stake_pubkey: &Pubkey,
485    authorized_pubkey: &Pubkey,
486    lamports: u64,
487    split_stake_pubkey: &Pubkey, // derived using create_with_seed()
488    base: &Pubkey,               // base
489    seed: &str,                  // seed
490) -> Vec<Instruction> {
491    vec![
492        solana_system_interface::instruction::allocate_with_seed(
493            split_stake_pubkey,
494            base,
495            seed,
496            StakeStateV2::size_of() as u64,
497            &ID,
498        ),
499        _split(
500            stake_pubkey,
501            authorized_pubkey,
502            lamports,
503            split_stake_pubkey,
504        ),
505    ]
506}
507
508#[cfg(feature = "bincode")]
509pub fn merge(
510    destination_stake_pubkey: &Pubkey,
511    source_stake_pubkey: &Pubkey,
512    authorized_pubkey: &Pubkey,
513) -> Vec<Instruction> {
514    let account_metas = vec![
515        AccountMeta::new(*destination_stake_pubkey, false),
516        AccountMeta::new(*source_stake_pubkey, false),
517        AccountMeta::new_readonly(CLOCK_ID, false),
518        AccountMeta::new_readonly(STAKE_HISTORY_ID, false),
519        AccountMeta::new_readonly(*authorized_pubkey, true),
520    ];
521
522    vec![Instruction::new_with_bincode(
523        ID,
524        &StakeInstruction::Merge,
525        account_metas,
526    )]
527}
528
529#[cfg(feature = "bincode")]
530pub fn create_account_and_delegate_stake(
531    from_pubkey: &Pubkey,
532    stake_pubkey: &Pubkey,
533    vote_pubkey: &Pubkey,
534    authorized: &Authorized,
535    lockup: &Lockup,
536    lamports: u64,
537) -> Vec<Instruction> {
538    let mut instructions = create_account(from_pubkey, stake_pubkey, authorized, lockup, lamports);
539    instructions.push(delegate_stake(
540        stake_pubkey,
541        &authorized.staker,
542        vote_pubkey,
543    ));
544    instructions
545}
546
547#[cfg(feature = "bincode")]
548#[allow(clippy::too_many_arguments)]
549pub fn create_account_with_seed_and_delegate_stake(
550    from_pubkey: &Pubkey,
551    stake_pubkey: &Pubkey,
552    base: &Pubkey,
553    seed: &str,
554    vote_pubkey: &Pubkey,
555    authorized: &Authorized,
556    lockup: &Lockup,
557    lamports: u64,
558) -> Vec<Instruction> {
559    let mut instructions = create_account_with_seed(
560        from_pubkey,
561        stake_pubkey,
562        base,
563        seed,
564        authorized,
565        lockup,
566        lamports,
567    );
568    instructions.push(delegate_stake(
569        stake_pubkey,
570        &authorized.staker,
571        vote_pubkey,
572    ));
573    instructions
574}
575
576#[cfg(feature = "bincode")]
577pub fn authorize(
578    stake_pubkey: &Pubkey,
579    authorized_pubkey: &Pubkey,
580    new_authorized_pubkey: &Pubkey,
581    stake_authorize: StakeAuthorize,
582    custodian_pubkey: Option<&Pubkey>,
583) -> Instruction {
584    let mut account_metas = vec![
585        AccountMeta::new(*stake_pubkey, false),
586        AccountMeta::new_readonly(CLOCK_ID, false),
587        AccountMeta::new_readonly(*authorized_pubkey, true),
588    ];
589
590    if let Some(custodian_pubkey) = custodian_pubkey {
591        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
592    }
593
594    Instruction::new_with_bincode(
595        ID,
596        &StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
597        account_metas,
598    )
599}
600
601#[cfg(feature = "bincode")]
602pub fn authorize_checked(
603    stake_pubkey: &Pubkey,
604    authorized_pubkey: &Pubkey,
605    new_authorized_pubkey: &Pubkey,
606    stake_authorize: StakeAuthorize,
607    custodian_pubkey: Option<&Pubkey>,
608) -> Instruction {
609    let mut account_metas = vec![
610        AccountMeta::new(*stake_pubkey, false),
611        AccountMeta::new_readonly(CLOCK_ID, false),
612        AccountMeta::new_readonly(*authorized_pubkey, true),
613        AccountMeta::new_readonly(*new_authorized_pubkey, true),
614    ];
615
616    if let Some(custodian_pubkey) = custodian_pubkey {
617        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
618    }
619
620    Instruction::new_with_bincode(
621        ID,
622        &StakeInstruction::AuthorizeChecked(stake_authorize),
623        account_metas,
624    )
625}
626
627#[cfg(feature = "bincode")]
628pub fn authorize_with_seed(
629    stake_pubkey: &Pubkey,
630    authority_base: &Pubkey,
631    authority_seed: String,
632    authority_owner: &Pubkey,
633    new_authorized_pubkey: &Pubkey,
634    stake_authorize: StakeAuthorize,
635    custodian_pubkey: Option<&Pubkey>,
636) -> Instruction {
637    let mut account_metas = vec![
638        AccountMeta::new(*stake_pubkey, false),
639        AccountMeta::new_readonly(*authority_base, true),
640        AccountMeta::new_readonly(CLOCK_ID, false),
641    ];
642
643    if let Some(custodian_pubkey) = custodian_pubkey {
644        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
645    }
646
647    let args = AuthorizeWithSeedArgs {
648        new_authorized_pubkey: *new_authorized_pubkey,
649        stake_authorize,
650        authority_seed,
651        authority_owner: *authority_owner,
652    };
653
654    Instruction::new_with_bincode(
655        ID,
656        &StakeInstruction::AuthorizeWithSeed(args),
657        account_metas,
658    )
659}
660
661#[cfg(feature = "bincode")]
662pub fn authorize_checked_with_seed(
663    stake_pubkey: &Pubkey,
664    authority_base: &Pubkey,
665    authority_seed: String,
666    authority_owner: &Pubkey,
667    new_authorized_pubkey: &Pubkey,
668    stake_authorize: StakeAuthorize,
669    custodian_pubkey: Option<&Pubkey>,
670) -> Instruction {
671    let mut account_metas = vec![
672        AccountMeta::new(*stake_pubkey, false),
673        AccountMeta::new_readonly(*authority_base, true),
674        AccountMeta::new_readonly(CLOCK_ID, false),
675        AccountMeta::new_readonly(*new_authorized_pubkey, true),
676    ];
677
678    if let Some(custodian_pubkey) = custodian_pubkey {
679        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
680    }
681
682    let args = AuthorizeCheckedWithSeedArgs {
683        stake_authorize,
684        authority_seed,
685        authority_owner: *authority_owner,
686    };
687
688    Instruction::new_with_bincode(
689        ID,
690        &StakeInstruction::AuthorizeCheckedWithSeed(args),
691        account_metas,
692    )
693}
694
695#[cfg(feature = "bincode")]
696pub fn delegate_stake(
697    stake_pubkey: &Pubkey,
698    authorized_pubkey: &Pubkey,
699    vote_pubkey: &Pubkey,
700) -> Instruction {
701    let account_metas = vec![
702        AccountMeta::new(*stake_pubkey, false),
703        AccountMeta::new_readonly(*vote_pubkey, false),
704        AccountMeta::new_readonly(CLOCK_ID, false),
705        AccountMeta::new_readonly(STAKE_HISTORY_ID, false),
706        // For backwards compatibility we pass the stake config, although this account is unused
707        AccountMeta::new_readonly(config::ID, false),
708        AccountMeta::new_readonly(*authorized_pubkey, true),
709    ];
710    Instruction::new_with_bincode(ID, &StakeInstruction::DelegateStake, account_metas)
711}
712
713#[cfg(feature = "bincode")]
714pub fn withdraw(
715    stake_pubkey: &Pubkey,
716    withdrawer_pubkey: &Pubkey,
717    to_pubkey: &Pubkey,
718    lamports: u64,
719    custodian_pubkey: Option<&Pubkey>,
720) -> Instruction {
721    let mut account_metas = vec![
722        AccountMeta::new(*stake_pubkey, false),
723        AccountMeta::new(*to_pubkey, false),
724        AccountMeta::new_readonly(CLOCK_ID, false),
725        AccountMeta::new_readonly(STAKE_HISTORY_ID, false),
726        AccountMeta::new_readonly(*withdrawer_pubkey, true),
727    ];
728
729    if let Some(custodian_pubkey) = custodian_pubkey {
730        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
731    }
732
733    Instruction::new_with_bincode(ID, &StakeInstruction::Withdraw(lamports), account_metas)
734}
735
736#[cfg(feature = "bincode")]
737pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
738    let account_metas = vec![
739        AccountMeta::new(*stake_pubkey, false),
740        AccountMeta::new_readonly(CLOCK_ID, false),
741        AccountMeta::new_readonly(*authorized_pubkey, true),
742    ];
743    Instruction::new_with_bincode(ID, &StakeInstruction::Deactivate, account_metas)
744}
745
746#[cfg(feature = "bincode")]
747pub fn set_lockup(
748    stake_pubkey: &Pubkey,
749    lockup: &LockupArgs,
750    custodian_pubkey: &Pubkey,
751) -> Instruction {
752    let account_metas = vec![
753        AccountMeta::new(*stake_pubkey, false),
754        AccountMeta::new_readonly(*custodian_pubkey, true),
755    ];
756    Instruction::new_with_bincode(ID, &StakeInstruction::SetLockup(*lockup), account_metas)
757}
758
759#[cfg(feature = "bincode")]
760pub fn set_lockup_checked(
761    stake_pubkey: &Pubkey,
762    lockup: &LockupArgs,
763    custodian_pubkey: &Pubkey,
764) -> Instruction {
765    let mut account_metas = vec![
766        AccountMeta::new(*stake_pubkey, false),
767        AccountMeta::new_readonly(*custodian_pubkey, true),
768    ];
769
770    let lockup_checked = LockupCheckedArgs {
771        unix_timestamp: lockup.unix_timestamp,
772        epoch: lockup.epoch,
773    };
774    if let Some(new_custodian) = lockup.custodian {
775        account_metas.push(AccountMeta::new_readonly(new_custodian, true));
776    }
777    Instruction::new_with_bincode(
778        ID,
779        &StakeInstruction::SetLockupChecked(lockup_checked),
780        account_metas,
781    )
782}
783
784#[cfg(feature = "bincode")]
785pub fn get_minimum_delegation() -> Instruction {
786    Instruction::new_with_bincode(ID, &StakeInstruction::GetMinimumDelegation, Vec::default())
787}
788
789#[cfg(feature = "bincode")]
790pub fn deactivate_delinquent_stake(
791    stake_account: &Pubkey,
792    delinquent_vote_account: &Pubkey,
793    reference_vote_account: &Pubkey,
794) -> Instruction {
795    let account_metas = vec![
796        AccountMeta::new(*stake_account, false),
797        AccountMeta::new_readonly(*delinquent_vote_account, false),
798        AccountMeta::new_readonly(*reference_vote_account, false),
799    ];
800    Instruction::new_with_bincode(ID, &StakeInstruction::DeactivateDelinquent, account_metas)
801}
802
803#[cfg(feature = "bincode")]
804fn _redelegate(
805    stake_pubkey: &Pubkey,
806    authorized_pubkey: &Pubkey,
807    vote_pubkey: &Pubkey,
808    uninitialized_stake_pubkey: &Pubkey,
809) -> Instruction {
810    let account_metas = vec![
811        AccountMeta::new(*stake_pubkey, false),
812        AccountMeta::new(*uninitialized_stake_pubkey, false),
813        AccountMeta::new_readonly(*vote_pubkey, false),
814        // For backwards compatibility we pass the stake config, although this account is unused
815        AccountMeta::new_readonly(config::ID, false),
816        AccountMeta::new_readonly(*authorized_pubkey, true),
817    ];
818    Instruction::new_with_bincode(ID, &StakeInstruction::Redelegate, account_metas)
819}
820
821#[cfg(feature = "bincode")]
822#[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
823pub fn redelegate(
824    stake_pubkey: &Pubkey,
825    authorized_pubkey: &Pubkey,
826    vote_pubkey: &Pubkey,
827    uninitialized_stake_pubkey: &Pubkey,
828) -> Vec<Instruction> {
829    vec![
830        solana_system_interface::instruction::allocate(
831            uninitialized_stake_pubkey,
832            StakeStateV2::size_of() as u64,
833        ),
834        solana_system_interface::instruction::assign(uninitialized_stake_pubkey, &ID),
835        _redelegate(
836            stake_pubkey,
837            authorized_pubkey,
838            vote_pubkey,
839            uninitialized_stake_pubkey,
840        ),
841    ]
842}
843
844#[cfg(feature = "bincode")]
845#[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
846pub fn redelegate_with_seed(
847    stake_pubkey: &Pubkey,
848    authorized_pubkey: &Pubkey,
849    vote_pubkey: &Pubkey,
850    uninitialized_stake_pubkey: &Pubkey, // derived using create_with_seed()
851    base: &Pubkey,                       // base
852    seed: &str,                          // seed
853) -> Vec<Instruction> {
854    vec![
855        solana_system_interface::instruction::allocate_with_seed(
856            uninitialized_stake_pubkey,
857            base,
858            seed,
859            StakeStateV2::size_of() as u64,
860            &ID,
861        ),
862        _redelegate(
863            stake_pubkey,
864            authorized_pubkey,
865            vote_pubkey,
866            uninitialized_stake_pubkey,
867        ),
868    ]
869}
870
871#[cfg(feature = "bincode")]
872pub fn move_stake(
873    source_stake_pubkey: &Pubkey,
874    destination_stake_pubkey: &Pubkey,
875    authorized_pubkey: &Pubkey,
876    lamports: u64,
877) -> Instruction {
878    let account_metas = vec![
879        AccountMeta::new(*source_stake_pubkey, false),
880        AccountMeta::new(*destination_stake_pubkey, false),
881        AccountMeta::new_readonly(*authorized_pubkey, true),
882    ];
883
884    Instruction::new_with_bincode(ID, &StakeInstruction::MoveStake(lamports), account_metas)
885}
886
887#[cfg(feature = "bincode")]
888pub fn move_lamports(
889    source_stake_pubkey: &Pubkey,
890    destination_stake_pubkey: &Pubkey,
891    authorized_pubkey: &Pubkey,
892    lamports: u64,
893) -> Instruction {
894    let account_metas = vec![
895        AccountMeta::new(*source_stake_pubkey, false),
896        AccountMeta::new(*destination_stake_pubkey, false),
897        AccountMeta::new_readonly(*authorized_pubkey, true),
898    ];
899
900    Instruction::new_with_bincode(ID, &StakeInstruction::MoveLamports(lamports), account_metas)
901}
902
903#[cfg(feature = "bincode")]
904#[cfg(test)]
905mod tests {
906    use super::*;
907
908    #[allow(deprecated)]
909    #[test]
910    fn test_constants() {
911        // Ensure that the constants are in sync with the solana program.
912        assert_eq!(CLOCK_ID, solana_sdk_ids::sysvar::clock::ID);
913
914        // Ensure that the constants are in sync with the solana program.
915        assert_eq!(STAKE_HISTORY_ID, solana_sdk_ids::sysvar::stake_history::ID);
916
917        // Ensure that the constants are in sync with the solana rent.
918        assert_eq!(RENT_ID, solana_sdk_ids::sysvar::rent::ID);
919    }
920}