Skip to main content

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
6#[cfg(feature = "codama")]
7use codama_macros::{CodamaInstructions, CodamaType};
8use {
9    crate::state::{Authorized, Lockup, StakeAuthorize},
10    solana_clock::{Epoch, UnixTimestamp},
11    solana_pubkey::Pubkey,
12};
13#[cfg(feature = "bincode")]
14use {
15    crate::{program::ID, state::StakeStateV2},
16    solana_instruction::{AccountMeta, Instruction},
17};
18
19// NOTE the stake program is in the process of removing dependence on all sysvars
20// once this version of the program is live on all clusters, we can remove them here
21// namely, from all doc comments in `StakeInstruction` and in all instruction builders
22// we may also remove all use of and reference to the stake config account
23#[cfg_attr(
24    feature = "serde",
25    derive(serde_derive::Deserialize, serde_derive::Serialize)
26)]
27#[cfg_attr(feature = "codama", derive(CodamaInstructions))]
28#[cfg_attr(feature = "codama", codama(enum_discriminator(size = number(u32))))]
29#[cfg_attr(feature = "codama", codama(optional_account_strategy = omitted))]
30#[derive(Debug, PartialEq, Eq, Clone)]
31pub enum StakeInstruction {
32    /// Initialize a stake with lockup and authorization information
33    ///
34    /// # Account references
35    ///   0. `[WRITE]` Uninitialized stake account
36    ///
37    /// [`Authorized`] carries pubkeys that must sign staker transactions
38    /// and withdrawer transactions; [`Lockup`] carries information about
39    /// withdrawal restrictions.
40    #[cfg_attr(
41        feature = "codama",
42        codama(display(
43            intent = "Initialize stake account",
44            interpolated_intent = "Initialize stake account ${accounts.stake}"
45        )),
46        codama(account(
47            name = "stake",
48            writable,
49            docs = "Uninitialized stake account",
50            display(label = "Stake Account")
51        ))
52    )]
53    Initialize(
54        #[cfg_attr(feature = "codama", codama(display(flatten = true)))] Authorized,
55        #[cfg_attr(feature = "codama", codama(display(flatten = true)))] Lockup,
56    ),
57
58    /// Authorize a key to manage stake or withdrawal
59    ///
60    /// # Account references
61    ///   0. `[WRITE]` Stake account to be updated
62    ///   1. `[SIGNER]` The stake or withdraw authority
63    ///   2. Optional: `[SIGNER]` Lockup authority, if updating `StakeAuthorize::Withdrawer` before
64    ///      lockup expiration
65    #[cfg_attr(
66        feature = "codama",
67        codama(display(
68            intent = "Update stake authority",
69            interpolated_intent = "Set the ${data.arg1} authority of ${accounts.stake} to ${data.arg0}"
70        )),
71        codama(account(
72            name = "stake",
73            writable,
74            docs = "Stake account to be updated",
75            display(label = "Stake Account")
76        )),
77        codama(account(name = "authority", signer, docs = "The stake or withdraw authority")),
78        codama(account(
79            name = "lockup_authority",
80            optional,
81            signer,
82            docs = "Lockup authority, if updating `StakeAuthorize::Withdrawer` before lockup expiration"
83        ))
84    )]
85    Authorize(
86        #[cfg_attr(feature = "codama", codama(display(label = "New Authority")))] Pubkey,
87        #[cfg_attr(feature = "codama", codama(display(label = "Authority Type")))] StakeAuthorize,
88    ),
89
90    /// Delegate a stake to a particular vote account
91    ///
92    /// # Account references
93    ///   0. `[WRITE]` Initialized stake account to be delegated
94    ///   1. `[]` Vote account to which this stake will be delegated
95    ///   2. `[SIGNER]` Stake authority
96    ///
97    /// The entire balance of the staking account is staked. `DelegateStake`
98    /// can be called multiple times, but re-delegation is delayed by one epoch.
99    #[cfg_attr(
100        feature = "codama",
101        codama(display(
102            intent = "Delegate stake",
103            interpolated_intent = "Delegate ${accounts.stake} to vote account ${accounts.vote}"
104        )),
105        codama(account(
106            name = "stake",
107            writable,
108            docs = "Initialized stake account to be delegated",
109            display(label = "Stake Account")
110        )),
111        codama(account(
112            name = "vote",
113            docs = "Vote account to which this stake will be delegated",
114            display(label = "Vote Account")
115        )),
116        codama(account(name = "stake_authority", signer, docs = "Stake authority"))
117    )]
118    DelegateStake,
119
120    /// Split `u64` tokens and stake off a stake account into another stake account.
121    ///
122    /// # Account references
123    ///   0. `[WRITE]` Stake account to be split; must be in the Initialized or Stake state
124    ///   1. `[WRITE]` Uninitialized stake account that will take the split-off amount
125    ///   2. `[SIGNER]` Stake authority
126    #[cfg_attr(
127        feature = "codama",
128        codama(display(
129            intent = "Split stake",
130            interpolated_intent = "Split ${data.args} from ${accounts.stake} into ${accounts.splitStake}"
131        )),
132        codama(account(
133            name = "stake",
134            writable,
135            docs = "Stake account to be split; must be in the Initialized or Stake state",
136            display(label = "Stake Account")
137        )),
138        codama(account(
139            name = "split_stake",
140            writable,
141            docs = "Uninitialized stake account that will take the split-off amount",
142            display(label = "New Stake Account")
143        )),
144        codama(account(name = "stake_authority", signer, docs = "Stake authority"))
145    )]
146    // `args` name is required for backwards compatibility with the old Anchor-generated
147    // IDL. Changing this name could break existing clients.
148    Split(
149        #[cfg_attr(
150            feature = "codama",
151            codama(name = "args"),
152            codama(display(label = "Amount", amount(decimals = 9, unit = "SOL")))
153        )]
154        u64,
155    ),
156
157    /// Withdraw unstaked lamports from the stake account
158    ///
159    /// # Account references
160    ///   0. `[WRITE]` Stake account from which to withdraw
161    ///   1. `[WRITE]` Recipient account
162    ///   2. `[SIGNER]` Withdraw authority
163    ///   3. Optional: `[SIGNER]` Lockup authority, if before lockup expiration
164    ///
165    /// The `u64` is the portion of the stake account balance to be withdrawn,
166    /// must be `<= StakeAccount.lamports - staked_lamports`.
167    #[cfg_attr(
168        feature = "codama",
169        codama(display(
170            intent = "Withdraw stake",
171            interpolated_intent = "Withdraw ${data.args} from ${accounts.stake} to ${accounts.recipient}"
172        )),
173        codama(account(
174            name = "stake",
175            writable,
176            docs = "Stake account from which to withdraw",
177            display(label = "Stake Account")
178        )),
179        codama(account(name = "recipient", writable, docs = "Recipient account")),
180        codama(account(name = "withdraw_authority", signer, docs = "Withdraw authority")),
181        codama(account(
182            name = "lockup_authority",
183            optional,
184            signer,
185            docs = "Lockup authority, if before lockup expiration"
186        ))
187    )]
188    // `args` name is required for backwards compatibility with the old Anchor-generated
189    // IDL. Changing this name could break existing clients.
190    Withdraw(
191        #[cfg_attr(
192            feature = "codama",
193            codama(name = "args"),
194            codama(display(label = "Amount", amount(decimals = 9, unit = "SOL")))
195        )]
196        u64,
197    ),
198
199    /// Deactivates the stake in the account
200    ///
201    /// # Account references
202    ///   0. `[WRITE]` Delegated stake account
203    ///   1. `[SIGNER]` Stake authority
204    #[cfg_attr(
205        feature = "codama",
206        codama(display(
207            intent = "Deactivate stake",
208            interpolated_intent = "Deactivate ${accounts.stake}"
209        )),
210        codama(account(
211            name = "stake",
212            writable,
213            docs = "Delegated stake account to be deactivated",
214            display(label = "Stake Account")
215        )),
216        codama(account(name = "stake_authority", signer, docs = "Stake authority"))
217    )]
218    Deactivate,
219
220    /// Set stake lockup
221    ///
222    /// If a lockup is not active, the withdraw authority may set a new lockup
223    /// If a lockup is active, the lockup custodian may update the lockup parameters
224    ///
225    /// # Account references
226    ///   0. `[WRITE]` Initialized stake account
227    ///   1. `[SIGNER]` Lockup authority or withdraw authority
228    #[cfg_attr(
229        feature = "codama",
230        codama(display(
231            intent = "Set stake lockup",
232            interpolated_intent = "Update the lockup of ${accounts.stake}"
233        )),
234        codama(account(
235            name = "stake",
236            writable,
237            docs = "Initialized stake account",
238            display(label = "Stake Account")
239        )),
240        codama(account(
241            name = "authority",
242            signer,
243            docs = "Lockup authority or withdraw authority"
244        ))
245    )]
246    SetLockup(
247        #[cfg_attr(
248            feature = "codama",
249            codama(type = link("lockupParams")),
250            codama(display(flatten = true))
251        )]
252        LockupArgs,
253    ),
254
255    /// Merge two stake accounts.
256    ///
257    /// Both accounts must have identical lockup and authority keys. A merge
258    /// is possible between two stakes in the following states with no additional
259    /// conditions:
260    ///
261    /// * two deactivated stakes
262    /// * an inactive stake into an activating stake during its activation epoch
263    ///
264    /// For the following cases, the voter pubkey and vote credits observed must match:
265    ///
266    /// * two activated stakes
267    /// * two activating accounts that share an activation epoch, during the activation epoch
268    ///
269    /// All other combinations of stake states will fail to merge, including all
270    /// "transient" states, where a stake is activating or deactivating with a
271    /// non-zero effective stake.
272    ///
273    /// # Account references
274    ///   0. `[WRITE]` Destination stake account for the merge
275    ///   1. `[WRITE]` Source stake account for to merge.  This account will be drained
276    ///   2. `[SIGNER]` Stake authority
277    #[cfg_attr(
278        feature = "codama",
279        codama(display(
280            intent = "Merge stake accounts",
281            interpolated_intent = "Merge ${accounts.sourceStake} into ${accounts.destinationStake}"
282        )),
283        codama(account(
284            name = "destination_stake",
285            writable,
286            docs = "Destination stake account for the merge",
287            display(label = "To")
288        )),
289        codama(account(
290            name = "source_stake",
291            writable,
292            docs = "Source stake account for to merge.  This account will be drained",
293            display(label = "From")
294        )),
295        codama(account(name = "stake_authority", signer, docs = "Stake authority"))
296    )]
297    Merge,
298
299    /// Authorize a key to manage stake or withdrawal with a derived key
300    ///
301    /// # Account references
302    ///   0. `[WRITE]` Stake account to be updated
303    ///   1. `[SIGNER]` Base key of stake or withdraw authority
304    ///   2. Optional: `[SIGNER]` Lockup authority, if updating [`StakeAuthorize::Withdrawer`]
305    ///      before lockup expiration
306    #[cfg_attr(
307        feature = "codama",
308        codama(display(
309            intent = "Update stake authority",
310            interpolated_intent = "Change an authority of ${accounts.stake}"
311        )),
312        codama(account(
313            name = "stake",
314            writable,
315            docs = "Stake account to be updated",
316            display(label = "Stake Account")
317        )),
318        codama(account(
319            name = "base",
320            signer,
321            docs = "Base key of stake or withdraw authority",
322            display(label = "Base Key")
323        )),
324        codama(account(
325            name = "lockup_authority",
326            optional,
327            signer,
328            docs = "Lockup authority, if updating `StakeAuthorize::Withdrawer` before lockup expiration"
329        ))
330    )]
331    AuthorizeWithSeed(
332        #[cfg_attr(
333            feature = "codama",
334            codama(type = link("authorizeWithSeedParams")),
335            codama(display(flatten = true))
336        )]
337        AuthorizeWithSeedArgs,
338    ),
339
340    /// Initialize a stake with authorization information
341    ///
342    /// This instruction is similar to `Initialize` except that the withdraw authority
343    /// must be a signer, and no lockup is applied to the account.
344    ///
345    /// # Account references
346    ///   0. `[WRITE]` Uninitialized stake account
347    ///   1. `[]` The stake authority
348    ///   2. `[SIGNER]` The withdraw authority
349    #[cfg_attr(
350        feature = "codama",
351        codama(display(
352            intent = "Initialize stake account",
353            interpolated_intent = "Initialize stake account ${accounts.stake}"
354        )),
355        codama(account(
356            name = "stake",
357            writable,
358            docs = "Uninitialized stake account",
359            display(label = "Stake Account")
360        )),
361        codama(account(name = "stake_authority", docs = "The stake authority")),
362        codama(account(name = "withdraw_authority", signer, docs = "The withdraw authority"))
363    )]
364    InitializeChecked,
365
366    /// Authorize a key to manage stake or withdrawal
367    ///
368    /// This instruction behaves like `Authorize` with the additional requirement that the new
369    /// stake or withdraw authority must also be a signer.
370    ///
371    /// # Account references
372    ///   0. `[WRITE]` Stake account to be updated
373    ///   1. `[SIGNER]` The stake or withdraw authority
374    ///   2. `[SIGNER]` The new stake or withdraw authority
375    ///   3. Optional: `[SIGNER]` Lockup authority, if updating [`StakeAuthorize::Withdrawer`]
376    ///      before lockup expiration
377    #[cfg_attr(
378        feature = "codama",
379        codama(display(
380            intent = "Update stake authority",
381            interpolated_intent = "Set the ${data.stakeAuthorize} authority of ${accounts.stake} to ${accounts.newAuthority}"
382        )),
383        codama(account(
384            name = "stake",
385            writable,
386            docs = "Stake account to be updated",
387            display(label = "Stake Account")
388        )),
389        codama(account(name = "authority", signer, docs = "The stake or withdraw authority")),
390        codama(account(
391            name = "new_authority",
392            signer,
393            docs = "The new stake or withdraw authority"
394        )),
395        codama(account(
396            name = "lockup_authority",
397            optional,
398            signer,
399            docs = "Lockup authority, if updating `StakeAuthorize::Withdrawer` before lockup expiration"
400        ))
401    )]
402    AuthorizeChecked(
403        #[cfg_attr(
404            feature = "codama",
405            codama(name = "stakeAuthorize"),
406            codama(display(label = "Authority Type"))
407        )]
408        StakeAuthorize,
409    ),
410
411    /// Authorize a key to manage stake or withdrawal with a derived key
412    ///
413    /// This instruction behaves like `AuthorizeWithSeed` with the additional requirement that
414    /// the new stake or withdraw authority must also be a signer.
415    ///
416    /// # Account references
417    ///   0. `[WRITE]` Stake account to be updated
418    ///   1. `[SIGNER]` Base key of stake or withdraw authority
419    ///   2. `[SIGNER]` The new stake or withdraw authority
420    ///   3. Optional: `[SIGNER]` Lockup authority, if updating [`StakeAuthorize::Withdrawer`]
421    ///      before lockup expiration
422    #[cfg_attr(
423        feature = "codama",
424        codama(display(
425            intent = "Update stake authority",
426            interpolated_intent = "Set an authority of ${accounts.stake} to ${accounts.newAuthority}"
427        )),
428        codama(account(
429            name = "stake",
430            writable,
431            docs = "Stake account to be updated",
432            display(label = "Stake Account")
433        )),
434        codama(account(
435            name = "base",
436            signer,
437            docs = "Base key of stake or withdraw authority",
438            display(label = "Base Key")
439        )),
440        codama(account(
441            name = "new_authority",
442            signer,
443            docs = "The new stake or withdraw authority"
444        )),
445        codama(account(
446            name = "lockup_authority",
447            optional,
448            signer,
449            docs = "Lockup authority, if updating `StakeAuthorize::Withdrawer` before lockup expiration"
450        ))
451    )]
452    AuthorizeCheckedWithSeed(
453        #[cfg_attr(
454            feature = "codama",
455            codama(type = link("authorizeCheckedWithSeedParams")),
456            codama(display(flatten = true))
457        )]
458        AuthorizeCheckedWithSeedArgs,
459    ),
460
461    /// Set stake lockup
462    ///
463    /// This instruction behaves like `SetLockup` with the additional requirement that
464    /// the new lockup authority also be a signer.
465    ///
466    /// If a lockup is not active, the withdraw authority may set a new lockup
467    /// If a lockup is active, the lockup custodian may update the lockup parameters
468    ///
469    /// # Account references
470    ///   0. `[WRITE]` Initialized stake account
471    ///   1. `[SIGNER]` Lockup authority or withdraw authority
472    ///   2. Optional: `[SIGNER]` New lockup authority
473    #[cfg_attr(
474        feature = "codama",
475        codama(display(
476            intent = "Set stake lockup",
477            interpolated_intent = "Update the lockup of ${accounts.stake}"
478        )),
479        codama(account(
480            name = "stake",
481            writable,
482            docs = "Initialized stake account",
483            display(label = "Stake Account")
484        )),
485        codama(account(
486            name = "authority",
487            signer,
488            docs = "Lockup authority or withdraw authority"
489        )),
490        codama(account(
491            name = "new_authority",
492            optional,
493            signer,
494            docs = "New lockup authority"
495        ))
496    )]
497    SetLockupChecked(
498        #[cfg_attr(
499            feature = "codama",
500            codama(type = link("lockupCheckedParams")),
501            codama(display(flatten = true))
502        )]
503        LockupCheckedArgs,
504    ),
505
506    /// Get the minimum stake delegation, in lamports
507    ///
508    /// # Account references
509    ///   None
510    ///
511    /// Returns the minimum delegation as a little-endian encoded `u64` value.
512    /// Programs can use the [`get_minimum_delegation()`] helper function to invoke and
513    /// retrieve the return value for this instruction.
514    ///
515    /// [`get_minimum_delegation()`]: crate::tools::get_minimum_delegation
516    #[cfg_attr(
517        feature = "codama",
518        codama(display(intent = "Get minimum stake delegation"))
519    )]
520    GetMinimumDelegation,
521
522    /// Deactivate stake delegated to a vote account that has been delinquent for at least
523    /// `MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION` epochs.
524    ///
525    /// No signer is required for this instruction as it is a common good to deactivate abandoned
526    /// stake.
527    ///
528    /// # Account references
529    ///   0. `[WRITE]` Delegated stake account
530    ///   1. `[]` Delinquent vote account for the delegated stake account
531    ///   2. `[]` Reference vote account that has voted at least once in the last
532    ///      `MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION` epochs
533    #[cfg_attr(
534        feature = "codama",
535        codama(display(
536            intent = "Deactivate delinquent stake",
537            interpolated_intent = "Deactivate delinquent stake ${accounts.stake}"
538        )),
539        codama(account(
540            name = "stake",
541            writable,
542            docs = "Delegated stake account",
543            display(label = "Stake Account")
544        )),
545        codama(account(
546            name = "delinquent_vote",
547            docs = "Delinquent vote account for the delegated stake account",
548            display(label = "Delinquent Vote Account")
549        )),
550        codama(account(
551            name = "reference_vote",
552            docs = "Reference vote account that has voted at least once in the last `MINIMUM_DELINQUENT_EPOCHS_FOR_DEACTIVATION` epochs",
553            display(label = "Reference Vote Account")
554        ))
555    )]
556    DeactivateDelinquent,
557
558    /// Redelegate activated stake to another vote account.
559    ///
560    /// Upon success:
561    ///   * the balance of the delegated stake account will be reduced to the undelegated amount in
562    ///     the account (rent exempt minimum and any additional lamports not part of the delegation),
563    ///     and scheduled for deactivation.
564    ///   * the provided uninitialized stake account will receive the original balance of the
565    ///     delegated stake account, minus the rent exempt minimum, and scheduled for activation to
566    ///     the provided vote account. Any existing lamports in the uninitialized stake account
567    ///     will also be included in the re-delegation.
568    ///
569    /// # Account references
570    ///   0. `[WRITE]` Delegated stake account to be redelegated. The account must be fully
571    ///      activated and carry a balance greater than or equal to the minimum delegation amount
572    ///      plus rent exempt minimum
573    ///   1. `[WRITE]` Uninitialized stake account that will hold the redelegated stake
574    ///   2. `[]` Vote account to which this stake will be re-delegated
575    ///   3. `[SIGNER]` Stake authority
576    ///
577    #[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
578    // NOTE: No codama attributes - this instruction is disabled and excluded from IDL
579    Redelegate,
580
581    /// Move stake between accounts with the same authorities and lockups, using Staker authority.
582    ///
583    /// The source account must be fully active. If its entire delegation is moved, it immediately
584    /// becomes inactive. Otherwise, at least the minimum delegation of active stake must remain.
585    ///
586    /// The destination account must be fully active or fully inactive. If it is active, it must
587    /// be delegated to the same vote account as the source. If it is inactive, it
588    /// immediately becomes active, and must contain at least the minimum delegation. The
589    /// destination must be pre-funded with the rent-exempt reserve.
590    ///
591    /// This instruction only affects or moves active stake. Additional unstaked lamports are never
592    /// moved, activated, or deactivated, and accounts are never deallocated.
593    ///
594    /// # Account references
595    ///   0. `[WRITE]` Active source stake account
596    ///   1. `[WRITE]` Active or inactive destination stake account
597    ///   2. `[SIGNER]` Stake authority
598    ///
599    /// The `u64` is the portion of the stake to move, which may be the entire delegation
600    #[cfg_attr(
601        feature = "codama",
602        codama(display(
603            intent = "Move stake",
604            interpolated_intent = "Move ${data.args} of active stake from ${accounts.sourceStake} to ${accounts.destinationStake}"
605        )),
606        codama(account(
607            name = "sourceStake",
608            writable,
609            docs = "Active source stake account",
610            display(label = "From")
611        )),
612        codama(account(
613            name = "destinationStake",
614            writable,
615            docs = "Active or inactive destination stake account",
616            display(label = "To")
617        )),
618        codama(account(name = "stake_authority", signer, docs = "Stake authority"))
619    )]
620    // sadly named `args` to avoid breaking users of old IDL
621    MoveStake(
622        #[cfg_attr(
623            feature = "codama",
624            codama(name = "args"),
625            codama(display(label = "Amount", amount(decimals = 9, unit = "SOL")))
626        )]
627        u64,
628    ),
629
630    /// Move unstaked lamports between accounts with the same authorities and lockups, using Staker
631    /// authority.
632    ///
633    /// The source account must be fully active or fully inactive. The destination may be in any
634    /// mergeable state (active, inactive, or activating, but not in warmup cooldown). Only lamports that
635    /// are neither backing a delegation nor required for rent-exemption may be moved.
636    ///
637    /// # Account references
638    ///   0. `[WRITE]` Active or inactive source stake account
639    ///   1. `[WRITE]` Mergeable destination stake account
640    ///   2. `[SIGNER]` Stake authority
641    ///
642    /// The `u64` is the portion of available lamports to move
643    #[cfg_attr(
644        feature = "codama",
645        codama(display(
646            intent = "Move unstaked SOL",
647            interpolated_intent = "Move ${data.args} from ${accounts.sourceStake} to ${accounts.destinationStake}"
648        )),
649        codama(account(
650            name = "source_stake",
651            writable,
652            docs = "Active or inactive source stake account",
653            display(label = "From")
654        )),
655        codama(account(
656            name = "destination_stake",
657            writable,
658            docs = "Mergeable destination stake account",
659            display(label = "To")
660        )),
661        codama(account(name = "stake_authority", signer, docs = "Stake authority"))
662    )]
663    // sadly named `args` to avoid breaking users of old IDL
664    MoveLamports(
665        #[cfg_attr(
666            feature = "codama",
667            codama(name = "args"),
668            codama(display(label = "Amount", amount(decimals = 9, unit = "SOL")))
669        )]
670        u64,
671    ),
672}
673
674#[cfg_attr(feature = "codama", derive(CodamaType), codama(name = "lockupParams"))]
675#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
676#[cfg_attr(
677    feature = "serde",
678    derive(serde_derive::Deserialize, serde_derive::Serialize)
679)]
680pub struct LockupArgs {
681    #[cfg_attr(feature = "codama", codama(display(label = "Locked Until")))]
682    pub unix_timestamp: Option<UnixTimestamp>,
683    #[cfg_attr(feature = "codama", codama(display(label = "Locked Until Epoch")))]
684    pub epoch: Option<Epoch>,
685    pub custodian: Option<Pubkey>,
686}
687
688#[cfg_attr(
689    feature = "codama",
690    derive(CodamaType),
691    codama(name = "lockupCheckedParams")
692)]
693#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
694#[cfg_attr(
695    feature = "serde",
696    derive(serde_derive::Deserialize, serde_derive::Serialize)
697)]
698pub struct LockupCheckedArgs {
699    #[cfg_attr(feature = "codama", codama(display(label = "Locked Until")))]
700    pub unix_timestamp: Option<UnixTimestamp>,
701    #[cfg_attr(feature = "codama", codama(display(label = "Locked Until Epoch")))]
702    pub epoch: Option<Epoch>,
703}
704
705#[cfg_attr(
706    feature = "codama",
707    derive(CodamaType),
708    codama(name = "authorizeWithSeedParams")
709)]
710#[derive(Debug, PartialEq, Eq, Clone)]
711#[cfg_attr(
712    feature = "serde",
713    derive(serde_derive::Deserialize, serde_derive::Serialize)
714)]
715pub struct AuthorizeWithSeedArgs {
716    #[cfg_attr(feature = "codama", codama(display(label = "New Authority")))]
717    pub new_authorized_pubkey: Pubkey,
718    #[cfg_attr(feature = "codama", codama(display(label = "Authority Type")))]
719    pub stake_authorize: StakeAuthorize,
720    #[cfg_attr(feature = "codama", codama(size_prefix = number(u64)))]
721    pub authority_seed: String,
722    pub authority_owner: Pubkey,
723}
724
725#[cfg_attr(
726    feature = "codama",
727    derive(CodamaType),
728    codama(name = "authorizeCheckedWithSeedParams")
729)]
730#[derive(Debug, PartialEq, Eq, Clone)]
731#[cfg_attr(
732    feature = "serde",
733    derive(serde_derive::Deserialize, serde_derive::Serialize)
734)]
735pub struct AuthorizeCheckedWithSeedArgs {
736    #[cfg_attr(feature = "codama", codama(display(label = "Authority Type")))]
737    pub stake_authorize: StakeAuthorize,
738    #[cfg_attr(feature = "codama", codama(size_prefix = number(u64)))]
739    pub authority_seed: String,
740    pub authority_owner: Pubkey,
741}
742
743#[cfg(feature = "bincode")]
744pub fn initialize(stake_pubkey: &Pubkey, authorized: &Authorized, lockup: &Lockup) -> Instruction {
745    Instruction::new_with_bincode(
746        ID,
747        &StakeInstruction::Initialize(*authorized, *lockup),
748        vec![AccountMeta::new(*stake_pubkey, false)],
749    )
750}
751
752#[cfg(feature = "bincode")]
753pub fn initialize_checked(stake_pubkey: &Pubkey, authorized: &Authorized) -> Instruction {
754    Instruction::new_with_bincode(
755        ID,
756        &StakeInstruction::InitializeChecked,
757        vec![
758            AccountMeta::new(*stake_pubkey, false),
759            AccountMeta::new_readonly(authorized.staker, false),
760            AccountMeta::new_readonly(authorized.withdrawer, true),
761        ],
762    )
763}
764
765#[cfg(feature = "bincode")]
766pub fn create_account_with_seed(
767    from_pubkey: &Pubkey,
768    stake_pubkey: &Pubkey,
769    base: &Pubkey,
770    seed: &str,
771    authorized: &Authorized,
772    lockup: &Lockup,
773    lamports: u64,
774) -> Vec<Instruction> {
775    vec![
776        solana_system_interface::instruction::create_account_with_seed(
777            from_pubkey,
778            stake_pubkey,
779            base,
780            seed,
781            lamports,
782            StakeStateV2::size_of() as u64,
783            &ID,
784        ),
785        initialize(stake_pubkey, authorized, lockup),
786    ]
787}
788
789#[cfg(feature = "bincode")]
790pub fn create_account(
791    from_pubkey: &Pubkey,
792    stake_pubkey: &Pubkey,
793    authorized: &Authorized,
794    lockup: &Lockup,
795    lamports: u64,
796) -> Vec<Instruction> {
797    vec![
798        solana_system_interface::instruction::create_account(
799            from_pubkey,
800            stake_pubkey,
801            lamports,
802            StakeStateV2::size_of() as u64,
803            &ID,
804        ),
805        initialize(stake_pubkey, authorized, lockup),
806    ]
807}
808
809#[cfg(feature = "bincode")]
810pub fn create_account_with_seed_checked(
811    from_pubkey: &Pubkey,
812    stake_pubkey: &Pubkey,
813    base: &Pubkey,
814    seed: &str,
815    authorized: &Authorized,
816    lamports: u64,
817) -> Vec<Instruction> {
818    vec![
819        solana_system_interface::instruction::create_account_with_seed(
820            from_pubkey,
821            stake_pubkey,
822            base,
823            seed,
824            lamports,
825            StakeStateV2::size_of() as u64,
826            &ID,
827        ),
828        initialize_checked(stake_pubkey, authorized),
829    ]
830}
831
832#[cfg(feature = "bincode")]
833pub fn create_account_checked(
834    from_pubkey: &Pubkey,
835    stake_pubkey: &Pubkey,
836    authorized: &Authorized,
837    lamports: u64,
838) -> Vec<Instruction> {
839    vec![
840        solana_system_interface::instruction::create_account(
841            from_pubkey,
842            stake_pubkey,
843            lamports,
844            StakeStateV2::size_of() as u64,
845            &ID,
846        ),
847        initialize_checked(stake_pubkey, authorized),
848    ]
849}
850
851#[cfg(feature = "bincode")]
852fn _split(
853    stake_pubkey: &Pubkey,
854    authorized_pubkey: &Pubkey,
855    lamports: u64,
856    split_stake_pubkey: &Pubkey,
857) -> Instruction {
858    let account_metas = vec![
859        AccountMeta::new(*stake_pubkey, false),
860        AccountMeta::new(*split_stake_pubkey, false),
861        AccountMeta::new_readonly(*authorized_pubkey, true),
862    ];
863
864    Instruction::new_with_bincode(ID, &StakeInstruction::Split(lamports), account_metas)
865}
866
867#[cfg(feature = "bincode")]
868pub fn split(
869    stake_pubkey: &Pubkey,
870    authorized_pubkey: &Pubkey,
871    lamports: u64,
872    split_stake_pubkey: &Pubkey,
873) -> Vec<Instruction> {
874    vec![
875        solana_system_interface::instruction::allocate(
876            split_stake_pubkey,
877            StakeStateV2::size_of() as u64,
878        ),
879        solana_system_interface::instruction::assign(split_stake_pubkey, &ID),
880        _split(
881            stake_pubkey,
882            authorized_pubkey,
883            lamports,
884            split_stake_pubkey,
885        ),
886    ]
887}
888
889#[cfg(feature = "bincode")]
890pub fn split_with_seed(
891    stake_pubkey: &Pubkey,
892    authorized_pubkey: &Pubkey,
893    lamports: u64,
894    split_stake_pubkey: &Pubkey, // derived using create_with_seed()
895    base: &Pubkey,               // base
896    seed: &str,                  // seed
897) -> Vec<Instruction> {
898    vec![
899        solana_system_interface::instruction::allocate_with_seed(
900            split_stake_pubkey,
901            base,
902            seed,
903            StakeStateV2::size_of() as u64,
904            &ID,
905        ),
906        _split(
907            stake_pubkey,
908            authorized_pubkey,
909            lamports,
910            split_stake_pubkey,
911        ),
912    ]
913}
914
915#[cfg(feature = "bincode")]
916pub fn merge(
917    destination_stake_pubkey: &Pubkey,
918    source_stake_pubkey: &Pubkey,
919    authorized_pubkey: &Pubkey,
920) -> Vec<Instruction> {
921    let account_metas = vec![
922        AccountMeta::new(*destination_stake_pubkey, false),
923        AccountMeta::new(*source_stake_pubkey, false),
924        AccountMeta::new_readonly(*authorized_pubkey, true),
925    ];
926
927    vec![Instruction::new_with_bincode(
928        ID,
929        &StakeInstruction::Merge,
930        account_metas,
931    )]
932}
933
934#[cfg(feature = "bincode")]
935pub fn create_account_and_delegate_stake(
936    from_pubkey: &Pubkey,
937    stake_pubkey: &Pubkey,
938    vote_pubkey: &Pubkey,
939    authorized: &Authorized,
940    lockup: &Lockup,
941    lamports: u64,
942) -> Vec<Instruction> {
943    let mut instructions = create_account(from_pubkey, stake_pubkey, authorized, lockup, lamports);
944    instructions.push(delegate_stake(
945        stake_pubkey,
946        &authorized.staker,
947        vote_pubkey,
948    ));
949    instructions
950}
951
952#[cfg(feature = "bincode")]
953#[allow(clippy::too_many_arguments)]
954pub fn create_account_with_seed_and_delegate_stake(
955    from_pubkey: &Pubkey,
956    stake_pubkey: &Pubkey,
957    base: &Pubkey,
958    seed: &str,
959    vote_pubkey: &Pubkey,
960    authorized: &Authorized,
961    lockup: &Lockup,
962    lamports: u64,
963) -> Vec<Instruction> {
964    let mut instructions = create_account_with_seed(
965        from_pubkey,
966        stake_pubkey,
967        base,
968        seed,
969        authorized,
970        lockup,
971        lamports,
972    );
973    instructions.push(delegate_stake(
974        stake_pubkey,
975        &authorized.staker,
976        vote_pubkey,
977    ));
978    instructions
979}
980
981#[cfg(feature = "bincode")]
982pub fn authorize(
983    stake_pubkey: &Pubkey,
984    authorized_pubkey: &Pubkey,
985    new_authorized_pubkey: &Pubkey,
986    stake_authorize: StakeAuthorize,
987    custodian_pubkey: Option<&Pubkey>,
988) -> Instruction {
989    let mut account_metas = vec![
990        AccountMeta::new(*stake_pubkey, false),
991        AccountMeta::new_readonly(*authorized_pubkey, true),
992    ];
993
994    if let Some(custodian_pubkey) = custodian_pubkey {
995        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
996    }
997
998    Instruction::new_with_bincode(
999        ID,
1000        &StakeInstruction::Authorize(*new_authorized_pubkey, stake_authorize),
1001        account_metas,
1002    )
1003}
1004
1005#[cfg(feature = "bincode")]
1006pub fn authorize_checked(
1007    stake_pubkey: &Pubkey,
1008    authorized_pubkey: &Pubkey,
1009    new_authorized_pubkey: &Pubkey,
1010    stake_authorize: StakeAuthorize,
1011    custodian_pubkey: Option<&Pubkey>,
1012) -> Instruction {
1013    let mut account_metas = vec![
1014        AccountMeta::new(*stake_pubkey, false),
1015        AccountMeta::new_readonly(*authorized_pubkey, true),
1016        AccountMeta::new_readonly(*new_authorized_pubkey, true),
1017    ];
1018
1019    if let Some(custodian_pubkey) = custodian_pubkey {
1020        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
1021    }
1022
1023    Instruction::new_with_bincode(
1024        ID,
1025        &StakeInstruction::AuthorizeChecked(stake_authorize),
1026        account_metas,
1027    )
1028}
1029
1030#[cfg(feature = "bincode")]
1031pub fn authorize_with_seed(
1032    stake_pubkey: &Pubkey,
1033    authority_base: &Pubkey,
1034    authority_seed: String,
1035    authority_owner: &Pubkey,
1036    new_authorized_pubkey: &Pubkey,
1037    stake_authorize: StakeAuthorize,
1038    custodian_pubkey: Option<&Pubkey>,
1039) -> Instruction {
1040    let mut account_metas = vec![
1041        AccountMeta::new(*stake_pubkey, false),
1042        AccountMeta::new_readonly(*authority_base, true),
1043    ];
1044
1045    if let Some(custodian_pubkey) = custodian_pubkey {
1046        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
1047    }
1048
1049    let args = AuthorizeWithSeedArgs {
1050        new_authorized_pubkey: *new_authorized_pubkey,
1051        stake_authorize,
1052        authority_seed,
1053        authority_owner: *authority_owner,
1054    };
1055
1056    Instruction::new_with_bincode(
1057        ID,
1058        &StakeInstruction::AuthorizeWithSeed(args),
1059        account_metas,
1060    )
1061}
1062
1063#[cfg(feature = "bincode")]
1064pub fn authorize_checked_with_seed(
1065    stake_pubkey: &Pubkey,
1066    authority_base: &Pubkey,
1067    authority_seed: String,
1068    authority_owner: &Pubkey,
1069    new_authorized_pubkey: &Pubkey,
1070    stake_authorize: StakeAuthorize,
1071    custodian_pubkey: Option<&Pubkey>,
1072) -> Instruction {
1073    let mut account_metas = vec![
1074        AccountMeta::new(*stake_pubkey, false),
1075        AccountMeta::new_readonly(*authority_base, true),
1076        AccountMeta::new_readonly(*new_authorized_pubkey, true),
1077    ];
1078
1079    if let Some(custodian_pubkey) = custodian_pubkey {
1080        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
1081    }
1082
1083    let args = AuthorizeCheckedWithSeedArgs {
1084        stake_authorize,
1085        authority_seed,
1086        authority_owner: *authority_owner,
1087    };
1088
1089    Instruction::new_with_bincode(
1090        ID,
1091        &StakeInstruction::AuthorizeCheckedWithSeed(args),
1092        account_metas,
1093    )
1094}
1095
1096#[cfg(feature = "bincode")]
1097pub fn delegate_stake(
1098    stake_pubkey: &Pubkey,
1099    authorized_pubkey: &Pubkey,
1100    vote_pubkey: &Pubkey,
1101) -> Instruction {
1102    let account_metas = vec![
1103        AccountMeta::new(*stake_pubkey, false),
1104        AccountMeta::new_readonly(*vote_pubkey, false),
1105        AccountMeta::new_readonly(*authorized_pubkey, true),
1106    ];
1107    Instruction::new_with_bincode(ID, &StakeInstruction::DelegateStake, account_metas)
1108}
1109
1110#[cfg(feature = "bincode")]
1111pub fn withdraw(
1112    stake_pubkey: &Pubkey,
1113    withdrawer_pubkey: &Pubkey,
1114    to_pubkey: &Pubkey,
1115    lamports: u64,
1116    custodian_pubkey: Option<&Pubkey>,
1117) -> Instruction {
1118    let mut account_metas = vec![
1119        AccountMeta::new(*stake_pubkey, false),
1120        AccountMeta::new(*to_pubkey, false),
1121        AccountMeta::new_readonly(*withdrawer_pubkey, true),
1122    ];
1123
1124    if let Some(custodian_pubkey) = custodian_pubkey {
1125        account_metas.push(AccountMeta::new_readonly(*custodian_pubkey, true));
1126    }
1127
1128    Instruction::new_with_bincode(ID, &StakeInstruction::Withdraw(lamports), account_metas)
1129}
1130
1131#[cfg(feature = "bincode")]
1132pub fn deactivate_stake(stake_pubkey: &Pubkey, authorized_pubkey: &Pubkey) -> Instruction {
1133    let account_metas = vec![
1134        AccountMeta::new(*stake_pubkey, false),
1135        AccountMeta::new_readonly(*authorized_pubkey, true),
1136    ];
1137    Instruction::new_with_bincode(ID, &StakeInstruction::Deactivate, account_metas)
1138}
1139
1140#[cfg(feature = "bincode")]
1141pub fn set_lockup(
1142    stake_pubkey: &Pubkey,
1143    lockup: &LockupArgs,
1144    custodian_pubkey: &Pubkey,
1145) -> Instruction {
1146    let account_metas = vec![
1147        AccountMeta::new(*stake_pubkey, false),
1148        AccountMeta::new_readonly(*custodian_pubkey, true),
1149    ];
1150    Instruction::new_with_bincode(ID, &StakeInstruction::SetLockup(*lockup), account_metas)
1151}
1152
1153#[cfg(feature = "bincode")]
1154pub fn set_lockup_checked(
1155    stake_pubkey: &Pubkey,
1156    lockup: &LockupArgs,
1157    custodian_pubkey: &Pubkey,
1158) -> Instruction {
1159    let mut account_metas = vec![
1160        AccountMeta::new(*stake_pubkey, false),
1161        AccountMeta::new_readonly(*custodian_pubkey, true),
1162    ];
1163
1164    let lockup_checked = LockupCheckedArgs {
1165        unix_timestamp: lockup.unix_timestamp,
1166        epoch: lockup.epoch,
1167    };
1168    if let Some(new_custodian) = lockup.custodian {
1169        account_metas.push(AccountMeta::new_readonly(new_custodian, true));
1170    }
1171    Instruction::new_with_bincode(
1172        ID,
1173        &StakeInstruction::SetLockupChecked(lockup_checked),
1174        account_metas,
1175    )
1176}
1177
1178#[cfg(feature = "bincode")]
1179pub fn get_minimum_delegation() -> Instruction {
1180    Instruction::new_with_bincode(ID, &StakeInstruction::GetMinimumDelegation, Vec::default())
1181}
1182
1183#[cfg(feature = "bincode")]
1184pub fn deactivate_delinquent_stake(
1185    stake_account: &Pubkey,
1186    delinquent_vote_account: &Pubkey,
1187    reference_vote_account: &Pubkey,
1188) -> Instruction {
1189    let account_metas = vec![
1190        AccountMeta::new(*stake_account, false),
1191        AccountMeta::new_readonly(*delinquent_vote_account, false),
1192        AccountMeta::new_readonly(*reference_vote_account, false),
1193    ];
1194    Instruction::new_with_bincode(ID, &StakeInstruction::DeactivateDelinquent, account_metas)
1195}
1196
1197#[cfg(feature = "bincode")]
1198fn _redelegate(
1199    stake_pubkey: &Pubkey,
1200    authorized_pubkey: &Pubkey,
1201    vote_pubkey: &Pubkey,
1202    uninitialized_stake_pubkey: &Pubkey,
1203) -> Instruction {
1204    let account_metas = vec![
1205        AccountMeta::new(*stake_pubkey, false),
1206        AccountMeta::new(*uninitialized_stake_pubkey, false),
1207        AccountMeta::new_readonly(*vote_pubkey, false),
1208        AccountMeta::new_readonly(*authorized_pubkey, true),
1209    ];
1210    Instruction::new_with_bincode(ID, &StakeInstruction::Redelegate, account_metas)
1211}
1212
1213#[cfg(feature = "bincode")]
1214#[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
1215pub fn redelegate(
1216    stake_pubkey: &Pubkey,
1217    authorized_pubkey: &Pubkey,
1218    vote_pubkey: &Pubkey,
1219    uninitialized_stake_pubkey: &Pubkey,
1220) -> Vec<Instruction> {
1221    vec![
1222        solana_system_interface::instruction::allocate(
1223            uninitialized_stake_pubkey,
1224            StakeStateV2::size_of() as u64,
1225        ),
1226        solana_system_interface::instruction::assign(uninitialized_stake_pubkey, &ID),
1227        _redelegate(
1228            stake_pubkey,
1229            authorized_pubkey,
1230            vote_pubkey,
1231            uninitialized_stake_pubkey,
1232        ),
1233    ]
1234}
1235
1236#[cfg(feature = "bincode")]
1237#[deprecated(since = "2.1.0", note = "Redelegate will not be enabled")]
1238pub fn redelegate_with_seed(
1239    stake_pubkey: &Pubkey,
1240    authorized_pubkey: &Pubkey,
1241    vote_pubkey: &Pubkey,
1242    uninitialized_stake_pubkey: &Pubkey, // derived using create_with_seed()
1243    base: &Pubkey,                       // base
1244    seed: &str,                          // seed
1245) -> Vec<Instruction> {
1246    vec![
1247        solana_system_interface::instruction::allocate_with_seed(
1248            uninitialized_stake_pubkey,
1249            base,
1250            seed,
1251            StakeStateV2::size_of() as u64,
1252            &ID,
1253        ),
1254        _redelegate(
1255            stake_pubkey,
1256            authorized_pubkey,
1257            vote_pubkey,
1258            uninitialized_stake_pubkey,
1259        ),
1260    ]
1261}
1262
1263#[cfg(feature = "bincode")]
1264pub fn move_stake(
1265    source_stake_pubkey: &Pubkey,
1266    destination_stake_pubkey: &Pubkey,
1267    authorized_pubkey: &Pubkey,
1268    lamports: u64,
1269) -> Instruction {
1270    let account_metas = vec![
1271        AccountMeta::new(*source_stake_pubkey, false),
1272        AccountMeta::new(*destination_stake_pubkey, false),
1273        AccountMeta::new_readonly(*authorized_pubkey, true),
1274    ];
1275
1276    Instruction::new_with_bincode(ID, &StakeInstruction::MoveStake(lamports), account_metas)
1277}
1278
1279#[cfg(feature = "bincode")]
1280pub fn move_lamports(
1281    source_stake_pubkey: &Pubkey,
1282    destination_stake_pubkey: &Pubkey,
1283    authorized_pubkey: &Pubkey,
1284    lamports: u64,
1285) -> Instruction {
1286    let account_metas = vec![
1287        AccountMeta::new(*source_stake_pubkey, false),
1288        AccountMeta::new(*destination_stake_pubkey, false),
1289        AccountMeta::new_readonly(*authorized_pubkey, true),
1290    ];
1291
1292    Instruction::new_with_bincode(ID, &StakeInstruction::MoveLamports(lamports), account_metas)
1293}