superstream/
lib.rs

1//! Superstream is a protocol and a collection of libraries for real-time money streaming on Solana. It allows anyone to
2//! continuously stream money to anyone else transparently and efficiently.
3//!
4//! Superstream protocol is completely open-source. View it on
5//! [GitHub](https://github.com/gpahal/superstream).
6//!
7//! Learn more about Superstream on [superstream.finance](https://superstream.finance/).
8//!
9//! ## Usage in CPI (Cross-Program Invocation)
10//!
11//! > **NOTE:** For a complete example, see
12//! > [superstream-cpi-example](https://github.com/gpahal/superstream/tree/main/program/programs/superstream-cpi-example)
13//!
14//! - Add the dependency in your program's Cargo.toml
15//!
16//! ```toml ignore
17//! superstream = { version = "0.3.1", features = ["cpi"] }
18//! ```
19//!
20//! - Invoke Superstream's instruction. In the example below, we are calling Superstream's cancel instruction.
21//!
22//! ```rust ignore
23//! #[program]
24//! pub mod superstream_cpi_example {
25//!     /// Cancel a stream.
26//!     pub fn cancel(ctx: Context<Cancel>, seed: u64, name: String, recipient: Pubkey) -> Result<()> {
27//!         let cpi_program = ctx.accounts.superstream_program.to_account_info();
28//!         let cpi_accounts = superstream::cpi::accounts::Cancel {
29//!             stream: ctx.accounts.stream.to_account_info(),
30//!             signer: ctx.accounts.signer.to_account_info(),
31//!             sender: ctx.accounts.sender.to_account_info(),
32//!             mint: ctx.accounts.sender.to_account_info(),
33//!             signer_token: ctx.accounts.signer_token.to_account_info(),
34//!             sender_token: ctx.accounts.sender_token.to_account_info(),
35//!             recipient_token: ctx.accounts.recipient_token.to_account_info(),
36//!             escrow_token: ctx.accounts.escrow_token.to_account_info(),
37//!             token_program: ctx.accounts.token_program.to_account_info(),
38//!         };
39//!         let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts);
40//!
41//!         superstream::cpi::cancel(cpi_ctx, seed, name, recipient)
42//!     }
43//!
44//!     // ... other stuff
45//! }
46//!
47//! /// Accounts struct for cancelling a stream.
48//! #[derive(Accounts)]
49//! pub struct Cancel<'info> {
50//!     /// Stream PDA account.
51//!     #[account(mut)]
52//!     pub stream: AccountInfo<'info>,
53//!
54//!     /// Signer wallet.
55//!     pub signer: Signer<'info>,
56//!
57//!     /// Stream sender account.
58//!     pub sender: AccountInfo<'info>,
59//!     /// SPL token mint account.
60//!     pub mint: Box<Account<'info, Mint>>,
61//!
62//!     /// Associated token account of the signer.
63//!     #[account(mut)]
64//!     pub signer_token: Box<Account<'info, TokenAccount>>,
65//!     /// Associated token account of the sender.
66//!     #[account(mut)]
67//!     pub sender_token: Box<Account<'info, TokenAccount>>,
68//!     /// Associated token account of the recipient.
69//!     #[account(mut)]
70//!     pub recipient_token: Box<Account<'info, TokenAccount>>,
71//!     /// Associated token escrow account holding the funds for this stream.
72//!     #[account(mut)]
73//!     pub escrow_token: Box<Account<'info, TokenAccount>>,
74//!
75//!     /// SPL token program.
76//!     pub token_program: Program<'info, Token>,
77//!
78//!     /// Superstream program.
79//!     pub superstream_program: Program<'info, superstream::program::Superstream>,
80//! }
81//!
82//! // ... other stuff
83//! ```
84
85mod transfer;
86mod utils;
87
88pub mod error;
89pub mod state;
90
91use anchor_lang::prelude::*;
92use anchor_spl::token::{Mint, Token, TokenAccount};
93
94use crate::{
95    error::StreamError,
96    state::Stream,
97    transfer::{transfer_from_escrow, transfer_to_escrow},
98    utils::is_token_account_rent_exempt,
99};
100
101declare_id!("4WLNkJ6RKt54sv85iTgJPLgoaxfrxAasZWBxAPLUfuVG");
102
103/// PDA account seed to create new stream PDA accounts.
104pub const STREAM_ACCOUNT_SEED: &[u8] = b"stream";
105
106#[program]
107pub mod superstream {
108    //! Module for superstream cpi methods and other utilities.
109
110    use super::*;
111
112    /// Create a new prepaid stream.
113    ///
114    /// # Arguments
115    ///
116    /// For more information on the arguments, see fields of the [`Stream`] struct.
117    pub fn create_prepaid(
118        mut ctx: Context<Create>,
119        seed: u64,
120        name: String,
121        recipient: Pubkey,
122        starts_at: u64,
123        ends_at: u64,
124        initial_amount: u64,
125        flow_interval: u64,
126        flow_rate: u64,
127        sender_can_cancel: bool,
128        sender_can_cancel_at: u64,
129        sender_can_change_sender: bool,
130        sender_can_change_sender_at: u64,
131        sender_can_pause: bool,
132        sender_can_pause_at: u64,
133        recipient_can_resume_pause_by_sender: bool,
134        recipient_can_resume_pause_by_sender_at: u64,
135        anyone_can_withdraw_for_recipient: bool,
136        anyone_can_withdraw_for_recipient_at: u64,
137    ) -> Result<()> {
138        create(
139            &mut ctx,
140            true,
141            recipient,
142            name,
143            starts_at,
144            ends_at,
145            initial_amount,
146            flow_interval,
147            flow_rate,
148            sender_can_cancel,
149            sender_can_cancel_at,
150            sender_can_change_sender,
151            sender_can_change_sender_at,
152            sender_can_pause,
153            sender_can_pause_at,
154            recipient_can_resume_pause_by_sender,
155            recipient_can_resume_pause_by_sender_at,
156            anyone_can_withdraw_for_recipient,
157            anyone_can_withdraw_for_recipient_at,
158            seed,
159        )?;
160
161        let stream = &mut ctx.accounts.stream;
162        let prepaid_amount_needed = stream.initialize_prepaid()?;
163        ctx.accounts.transfer_to_escrow(prepaid_amount_needed)
164    }
165
166    /// Create a new non-prepaid stream.
167    ///
168    /// # Arguments
169    ///
170    /// * `topup_amount` - Initial topup amount for the stream. The topup amount should be >= minimum deposit required.
171    ///   See [`DEPOSIT_AMOUNT_PERIOD_IN_SECS`](crate::state::DEPOSIT_AMOUNT_PERIOD_IN_SECS) for more information.
172    ///
173    /// For more information on the other arguments, see fields of the [`Stream`] struct.
174    pub fn create_non_prepaid(
175        mut ctx: Context<Create>,
176        seed: u64,
177        name: String,
178        recipient: Pubkey,
179        starts_at: u64,
180        ends_at: u64,
181        initial_amount: u64,
182        flow_interval: u64,
183        flow_rate: u64,
184        sender_can_cancel: bool,
185        sender_can_cancel_at: u64,
186        sender_can_change_sender: bool,
187        sender_can_change_sender_at: u64,
188        sender_can_pause: bool,
189        sender_can_pause_at: u64,
190        recipient_can_resume_pause_by_sender: bool,
191        recipient_can_resume_pause_by_sender_at: u64,
192        anyone_can_withdraw_for_recipient: bool,
193        anyone_can_withdraw_for_recipient_at: u64,
194        topup_amount: u64,
195    ) -> Result<()> {
196        create(
197            &mut ctx,
198            false,
199            recipient,
200            name,
201            starts_at,
202            ends_at,
203            initial_amount,
204            flow_interval,
205            flow_rate,
206            sender_can_cancel,
207            sender_can_cancel_at,
208            sender_can_change_sender,
209            sender_can_change_sender_at,
210            sender_can_pause,
211            sender_can_pause_at,
212            recipient_can_resume_pause_by_sender,
213            recipient_can_resume_pause_by_sender_at,
214            anyone_can_withdraw_for_recipient,
215            anyone_can_withdraw_for_recipient_at,
216            seed,
217        )?;
218
219        let stream = &mut ctx.accounts.stream;
220        stream.initialize_non_prepaid(topup_amount)?;
221        ctx.accounts.transfer_to_escrow(topup_amount)
222    }
223
224    /// Cancel a stream.
225    ///
226    /// # Arguments
227    ///
228    /// For more information on the arguments, see fields of the [`Stream`] struct.
229    pub fn cancel(ctx: Context<Cancel>, seed: u64, name: String, recipient: Pubkey) -> Result<()> {
230        let stream = &mut ctx.accounts.stream;
231        let stream_key = stream.to_account_info().key;
232        let bump = stream.bump;
233        let params = stream.cancel(*stream_key, &ctx.accounts.signer, recipient)?;
234        ctx.accounts
235            .transfer_from_escrow_to_sender(seed, &name, bump, params.transfer_amount_sender)?;
236        ctx.accounts
237            .transfer_from_escrow_to_signer(seed, &name, bump, params.transfer_amount_signer)?;
238        ctx.accounts
239            .transfer_from_escrow_to_recipient(seed, &name, bump, params.transfer_amount_recipient)?;
240
241        Ok(())
242    }
243
244    /// Withdraw excess sender topup from a non-prepaid stream.
245    ///
246    /// # Arguments
247    ///
248    /// For more information on the arguments, see fields of the [`Stream`] struct.
249    pub fn withdraw_excess_topup_non_prepaid_ended(
250        ctx: Context<WithdrawExcessTopupNonPrepaidEnded>,
251        seed: u64,
252        name: String,
253    ) -> Result<()> {
254        let stream = &mut ctx.accounts.stream;
255        let amount = stream.withdraw_excess_topup_non_prepaid_ended()?;
256        if amount > 0 {
257            let bump = stream.bump;
258            ctx.accounts.transfer_from_escrow(seed, &name, bump, amount)?;
259        }
260        Ok(())
261    }
262
263    /// Topup a non-prepaid stream.
264    ///
265    /// # Arguments
266    ///
267    /// * `topup_amount` - Topup amount for the stream. The topup amount should be <= maximum acceptable topup amount.
268    ///
269    /// For more information on the other arguments, see fields of the [`Stream`] struct.
270    pub fn topup_non_prepaid(
271        ctx: Context<TopupNonPrepaid>,
272        _seed: u64,
273        _name: String,
274        topup_amount: u64,
275    ) -> Result<()> {
276        let stream = &mut ctx.accounts.stream;
277        stream.topup_non_prepaid(topup_amount)?;
278        ctx.accounts.transfer_to_escrow(topup_amount)
279    }
280
281    /// Change sender of a non-prepaid stream.
282    ///
283    /// # Arguments
284    ///
285    /// * `new_sender` - The new sender
286    ///
287    /// For more information on the other arguments, see fields of the [`Stream`] struct.
288    pub fn change_sender_non_prepaid(
289        ctx: Context<ChangeSenderNonPrepaid>,
290        _seed: u64,
291        _name: String,
292        new_sender: Pubkey,
293    ) -> Result<()> {
294        let stream = &mut ctx.accounts.stream;
295        stream.change_sender_non_prepaid(&ctx.accounts.sender, new_sender)
296    }
297
298    /// Withdraw recipient funds from a stream.
299    ///
300    /// # Arguments
301    ///
302    /// For more information on the arguments, see fields of the [`Stream`] struct.
303    pub fn withdraw(
304        ctx: Context<WithdrawAndChangeRecipient>,
305        seed: u64,
306        name: String,
307        recipient: Pubkey,
308    ) -> Result<()> {
309        withdraw_and_change_recipient(ctx, seed, name, recipient, Pubkey::default())
310    }
311
312    /// Withdraw recipient funds from a stream and change recipient of a stream.
313    ///
314    /// # Arguments
315    ///
316    /// * `new_recipient` - The new recipient
317    ///
318    /// For more information on the other arguments, see fields of the [`Stream`] struct.
319    pub fn withdraw_and_change_recipient(
320        ctx: Context<WithdrawAndChangeRecipient>,
321        seed: u64,
322        name: String,
323        recipient: Pubkey,
324        new_recipient: Pubkey,
325    ) -> Result<()> {
326        let stream = &mut ctx.accounts.stream;
327        let amount_available_to_withdraw =
328            stream.withdraw_and_change_recipient(&ctx.accounts.signer, recipient, new_recipient)?;
329        let bump = stream.bump;
330        ctx.accounts
331            .transfer_from_escrow(seed, &name, bump, amount_available_to_withdraw)
332    }
333
334    /// Pause a non-prepaid stream.
335    ///
336    /// # Arguments
337    ///
338    /// For more information on the arguments, see fields of the [`Stream`] struct.
339    pub fn pause_non_prepaid(ctx: Context<PauseNonPrepaid>, _seed: u64, _name: String) -> Result<()> {
340        let stream = &mut ctx.accounts.stream;
341        stream.pause_non_prepaid(&ctx.accounts.signer)
342    }
343
344    /// Resume a non-prepaid stream.
345    ///
346    /// # Arguments
347    ///
348    /// For more information on the arguments, see fields of the [`Stream`] struct.
349    pub fn resume_non_prepaid(ctx: Context<ResumeNonPrepaid>, _seed: u64, _name: String) -> Result<()> {
350        let stream = &mut ctx.accounts.stream;
351        stream.resume_non_prepaid(&ctx.accounts.signer)
352    }
353}
354
355pub(crate) fn create(
356    ctx: &mut Context<Create>,
357    is_prepaid: bool,
358    recipient: Pubkey,
359    name: String,
360    starts_at: u64,
361    ends_at: u64,
362    initial_amount: u64,
363    flow_interval: u64,
364    flow_rate: u64,
365    sender_can_cancel: bool,
366    sender_can_cancel_at: u64,
367    sender_can_change_sender: bool,
368    sender_can_change_sender_at: u64,
369    sender_can_pause: bool,
370    sender_can_pause_at: u64,
371    recipient_can_resume_pause_by_sender: bool,
372    recipient_can_resume_pause_by_sender_at: u64,
373    anyone_can_withdraw_for_recipient: bool,
374    anyone_can_withdraw_for_recipient_at: u64,
375    seed: u64,
376) -> Result<()> {
377    let escrow_token_account = &ctx.accounts.escrow_token;
378    require!(
379        is_token_account_rent_exempt(escrow_token_account)?,
380        StreamError::EscrowNotRentExempt,
381    );
382
383    let stream = &mut ctx.accounts.stream;
384    stream.initialize(
385        is_prepaid,
386        ctx.accounts.mint.key(),
387        ctx.accounts.sender.key(),
388        recipient,
389        name,
390        starts_at,
391        ends_at,
392        initial_amount,
393        flow_interval,
394        flow_rate,
395        sender_can_cancel,
396        sender_can_cancel_at,
397        sender_can_change_sender,
398        sender_can_change_sender_at,
399        sender_can_pause,
400        sender_can_pause_at,
401        recipient_can_resume_pause_by_sender,
402        recipient_can_resume_pause_by_sender_at,
403        anyone_can_withdraw_for_recipient,
404        anyone_can_withdraw_for_recipient_at,
405        seed,
406        *ctx.bumps.get("stream").unwrap(),
407    )
408}
409
410/// Accounts struct for creating a new stream.
411#[derive(Accounts)]
412#[instruction(seed: u64, name: String)]
413pub struct Create<'info> {
414    /// Stream PDA account. This is initialized by the program.
415    #[account(
416        init,
417        seeds = [
418            STREAM_ACCOUNT_SEED,
419            seed.to_le_bytes().as_ref(),
420            mint.key().as_ref(),
421            name.as_bytes(),
422        ],
423        payer = sender,
424        space = Stream::space(&name),
425        bump,
426    )]
427    pub stream: Account<'info, Stream>,
428
429    /// Stream sender wallet.
430    #[account(mut)]
431    pub sender: Signer<'info>,
432    /// SPL token mint account.
433    pub mint: Box<Account<'info, Mint>>,
434
435    /// Associated token account of the sender.
436    #[account(
437        mut,
438        constraint =
439            sender_token.mint == mint.key()
440            && sender_token.owner == sender.key(),
441    )]
442    pub sender_token: Box<Account<'info, TokenAccount>>,
443    /// Associated token escrow account holding the funds for this stream.
444    #[account(
445        mut,
446        constraint =
447            escrow_token.mint == mint.key()
448            && escrow_token.owner == stream.key(),
449    )]
450    pub escrow_token: Box<Account<'info, TokenAccount>>,
451
452    /// SPL token program.
453    pub token_program: Program<'info, Token>,
454    /// Solana system program.
455    pub system_program: Program<'info, System>,
456}
457
458/// Accounts struct for cancelling a stream.
459#[derive(Accounts)]
460#[instruction(seed: u64, name: String, recipient: Pubkey)]
461pub struct Cancel<'info> {
462    /// Stream PDA account.
463    #[account(
464        mut,
465        seeds = [
466            STREAM_ACCOUNT_SEED,
467            seed.to_le_bytes().as_ref(),
468            mint.key().as_ref(),
469            name.as_bytes(),
470        ],
471        bump,
472    )]
473    pub stream: Account<'info, Stream>,
474
475    /// Signer wallet. Either the sender or the receiver can cancel the stream till it's solvent. After insolvency,
476    /// anyone can cancel.
477    pub signer: Signer<'info>,
478
479    /// Stream sender account.
480    ///
481    /// CHECK: Only 1 check is needed which is in the constraint. That is enough to verify that we are sending the funds
482    /// to the stream sender.
483    #[account(constraint = sender.key() == stream.sender)]
484    pub sender: UncheckedAccount<'info>,
485    /// SPL token mint account.
486    pub mint: Box<Account<'info, Mint>>,
487
488    /// Associated token account of the signer.
489    #[account(
490        mut,
491        constraint =
492            signer_token.mint == mint.key()
493            && signer_token.owner == signer.key(),
494    )]
495    pub signer_token: Box<Account<'info, TokenAccount>>,
496    /// Associated token account of the sender.
497    #[account(
498        mut,
499        constraint =
500            sender_token.mint == mint.key()
501            && sender_token.owner == sender.key(),
502    )]
503    pub sender_token: Box<Account<'info, TokenAccount>>,
504    /// Associated token account of the recipient.
505    #[account(
506        mut,
507        constraint =
508            recipient_token.mint == mint.key()
509            && recipient_token.owner == recipient,
510    )]
511    pub recipient_token: Box<Account<'info, TokenAccount>>,
512    /// Associated token escrow account holding the funds for this stream.
513    #[account(
514        mut,
515        constraint =
516            escrow_token.mint == mint.key()
517            && escrow_token.owner == stream.key(),
518    )]
519    pub escrow_token: Box<Account<'info, TokenAccount>>,
520
521    /// SPL token program.
522    pub token_program: Program<'info, Token>,
523}
524
525/// Accounts struct for withdrawing excess sender topup from a non-prepaid stream.
526#[derive(Accounts)]
527#[instruction(seed: u64, name: String)]
528pub struct WithdrawExcessTopupNonPrepaidEnded<'info> {
529    /// Stream PDA account.
530    #[account(
531        mut,
532        seeds = [
533            STREAM_ACCOUNT_SEED,
534            seed.to_le_bytes().as_ref(),
535            mint.key().as_ref(),
536            name.as_bytes(),
537        ],
538        bump,
539    )]
540    pub stream: Account<'info, Stream>,
541
542    /// Signer wallet.
543    pub signer: Signer<'info>,
544
545    /// Stream sender account.
546    ///
547    /// CHECK: Only 1 check is needed which is in the constraint. That is enough to verify that we are sending the funds
548    /// to the stream sender.
549    #[account(constraint = sender.key() == stream.sender)]
550    pub sender: UncheckedAccount<'info>,
551    /// SPL token mint account.
552    pub mint: Box<Account<'info, Mint>>,
553
554    /// Associated token account of the sender.
555    #[account(
556        mut,
557        constraint =
558            sender_token.mint == mint.key()
559            && sender_token.owner == sender.key(),
560    )]
561    pub sender_token: Box<Account<'info, TokenAccount>>,
562    /// Associated token escrow account holding the funds for this stream.
563    #[account(
564        mut,
565        constraint =
566            escrow_token.mint == mint.key()
567            && escrow_token.owner == stream.key(),
568    )]
569    pub escrow_token: Box<Account<'info, TokenAccount>>,
570
571    /// SPL token program.
572    pub token_program: Program<'info, Token>,
573}
574
575/// Accounts struct for topping up a non-prepaid stream.
576#[derive(Accounts)]
577#[instruction(seed: u64, name: String)]
578pub struct TopupNonPrepaid<'info> {
579    /// Stream PDA account.
580    #[account(
581        mut,
582        seeds = [
583            STREAM_ACCOUNT_SEED,
584            seed.to_le_bytes().as_ref(),
585            mint.key().as_ref(),
586            name.as_bytes(),
587        ],
588        bump,
589    )]
590    pub stream: Account<'info, Stream>,
591
592    /// Signer wallet. Anyone can topup a stream. But the refund when the stream gets cancelled will only go to the
593    /// stream sender.
594    pub signer: Signer<'info>,
595    /// SPL token mint account.
596    pub mint: Account<'info, Mint>,
597
598    /// Associated token account of the signer.
599    #[account(
600        mut,
601        constraint =
602            signer_token.mint == mint.key()
603            && signer_token.owner == signer.key(),
604    )]
605    pub signer_token: Account<'info, TokenAccount>,
606    /// Associated token escrow account holding the funds for this stream.
607    #[account(
608        mut,
609        constraint =
610            escrow_token.mint == mint.key()
611            && escrow_token.owner == stream.key(),
612    )]
613    pub escrow_token: Account<'info, TokenAccount>,
614
615    /// SPL token program.
616    pub token_program: Program<'info, Token>,
617}
618
619/// Accounts struct for changing the sender of a non-prepaid stream.
620#[derive(Accounts)]
621#[instruction(seed: u64, name: String)]
622pub struct ChangeSenderNonPrepaid<'info> {
623    /// Stream PDA account.
624    #[account(
625        mut,
626        seeds = [
627            STREAM_ACCOUNT_SEED,
628            seed.to_le_bytes().as_ref(),
629            mint.key().as_ref(),
630            name.as_bytes(),
631        ],
632        bump,
633    )]
634    pub stream: Account<'info, Stream>,
635
636    // Stream sender wallet.
637    pub sender: Signer<'info>,
638    /// SPL token mint account.
639    pub mint: Account<'info, Mint>,
640}
641
642/// Accounts struct for withdrawing recipient funds from a stream and changing recipient of a stream.
643#[derive(Accounts)]
644#[instruction(seed: u64, name: String, recipient: Pubkey)]
645pub struct WithdrawAndChangeRecipient<'info> {
646    /// Stream PDA account.
647    #[account(
648        mut,
649        seeds = [
650            STREAM_ACCOUNT_SEED,
651            seed.to_le_bytes().as_ref(),
652            mint.key().as_ref(),
653            name.as_bytes(),
654        ],
655        bump,
656    )]
657    pub stream: Account<'info, Stream>,
658
659    /// Signer wallet. Anybody can call the withdraw method. The recipient of the withdrawn amount is not related to the
660    /// signer. Recipient is passed as an argument, based on which the stream PDA is accessed, so if a malicious user
661    /// tries to send themselves as a recipient, but a different stream account, the constraint for the stream account
662    /// will fail.
663    pub signer: Signer<'info>,
664    /// SPL token mint account.
665    pub mint: Box<Account<'info, Mint>>,
666
667    /// Associated token account of the recipient.
668    #[account(
669        mut,
670        constraint =
671            recipient_token.mint == mint.key()
672            && recipient_token.owner == recipient,
673    )]
674    pub recipient_token: Box<Account<'info, TokenAccount>>,
675    /// Associated token escrow account holding the funds for this stream.
676    #[account(
677        mut,
678        constraint =
679            escrow_token.mint == mint.key()
680            && escrow_token.owner == stream.key(),
681    )]
682    pub escrow_token: Box<Account<'info, TokenAccount>>,
683
684    /// SPL token program.
685    pub token_program: Program<'info, Token>,
686}
687
688/// Accounts struct for pausing a non-prepaid stream.
689#[derive(Accounts)]
690#[instruction(seed: u64, name: String)]
691pub struct PauseNonPrepaid<'info> {
692    /// Stream PDA account.
693    #[account(
694        mut,
695        seeds = [
696            STREAM_ACCOUNT_SEED,
697            seed.to_le_bytes().as_ref(),
698            mint.key().as_ref(),
699            name.as_bytes(),
700        ],
701        bump,
702    )]
703    pub stream: Account<'info, Stream>,
704
705    /// Signer wallet. Signer needs to be either the sender (if they are allowed to) or the recipient.
706    pub signer: Signer<'info>,
707    /// SPL token mint account.
708    pub mint: Account<'info, Mint>,
709}
710
711/// Accounts struct for resuming a non-prepaid stream.
712#[derive(Accounts)]
713#[instruction(seed: u64, name: String)]
714pub struct ResumeNonPrepaid<'info> {
715    /// Stream PDA account.
716    #[account(
717        mut,
718        seeds = [
719            STREAM_ACCOUNT_SEED,
720            seed.to_le_bytes().as_ref(),
721            mint.key().as_ref(),
722            name.as_bytes(),
723        ],
724        bump,
725    )]
726    pub stream: Account<'info, Stream>,
727
728    /// Signer wallet. Signer needs to be either the sender (if they are allowed to) or the recipient (exception is if
729    /// the stream was paused by the sender and recipient is not allowed to resume a stream paused by sender).
730    pub signer: Signer<'info>,
731    /// SPL token mint account.
732    pub mint: Account<'info, Mint>,
733}
734
735impl<'info> Create<'info> {
736    /// Transfer funds from the associated token account of the sender to associated token escrow account holding the
737    /// funds for this stream.
738    pub fn transfer_to_escrow(&self, amount: u64) -> Result<()> {
739        transfer_to_escrow(
740            &self.sender,
741            &self.sender_token,
742            &self.escrow_token,
743            &self.token_program,
744            amount,
745        )
746    }
747}
748
749impl<'info> Cancel<'info> {
750    /// Transfer funds from the associated token escrow account holding the funds for this stream to the associated
751    /// token account of the sender.
752    pub fn transfer_from_escrow_to_sender(&self, seed: u64, name: &str, bump: u8, amount: u64) -> Result<()> {
753        self.transfer_from_escrow(&self.sender_token, seed, name, bump, amount)
754    }
755
756    /// Transfer funds from the associated token escrow account holding the funds for this stream to the associated
757    /// token account of the signer.
758    pub fn transfer_from_escrow_to_signer(&self, seed: u64, name: &str, bump: u8, amount: u64) -> Result<()> {
759        self.transfer_from_escrow(&self.signer_token, seed, name, bump, amount)
760    }
761
762    /// Transfer funds from the associated token escrow account holding the funds for this stream to the associated
763    /// token account of the recipient.
764    pub fn transfer_from_escrow_to_recipient(&self, seed: u64, name: &str, bump: u8, amount: u64) -> Result<()> {
765        self.transfer_from_escrow(&self.recipient_token, seed, name, bump, amount)
766    }
767
768    fn transfer_from_escrow(
769        &self,
770        destination_token: &Account<'info, TokenAccount>,
771        seed: u64,
772        name: &str,
773        bump: u8,
774        amount: u64,
775    ) -> Result<()> {
776        transfer_from_escrow(
777            &self.stream,
778            destination_token,
779            &self.escrow_token,
780            &self.token_program,
781            seed,
782            &self.mint.key(),
783            name,
784            bump,
785            amount,
786        )
787    }
788}
789
790impl<'info> WithdrawExcessTopupNonPrepaidEnded<'info> {
791    /// Transfer funds from the associated token escrow account holding the funds for this stream to the associated
792    /// token account of the sender.
793    fn transfer_from_escrow(&self, seed: u64, name: &str, bump: u8, amount: u64) -> Result<()> {
794        transfer_from_escrow(
795            &self.stream,
796            &self.sender_token,
797            &self.escrow_token,
798            &self.token_program,
799            seed,
800            &self.mint.key(),
801            name,
802            bump,
803            amount,
804        )
805    }
806}
807
808impl<'info> TopupNonPrepaid<'info> {
809    /// Transfer funds from the associated token account of the sender to associated token escrow account holding the
810    /// funds for this stream.
811    pub fn transfer_to_escrow(&self, amount: u64) -> Result<()> {
812        transfer_to_escrow(
813            &self.signer,
814            &self.signer_token,
815            &self.escrow_token,
816            &self.token_program,
817            amount,
818        )
819    }
820}
821
822impl<'info> WithdrawAndChangeRecipient<'info> {
823    /// Transfer funds from the associated token escrow account holding the funds for this stream to the associated
824    /// token account of the recipient.
825    pub fn transfer_from_escrow(&self, seed: u64, name: &str, bump: u8, amount: u64) -> Result<()> {
826        transfer_from_escrow(
827            &self.stream,
828            &self.recipient_token,
829            &self.escrow_token,
830            &self.token_program,
831            seed,
832            &self.mint.key(),
833            name,
834            bump,
835            amount,
836        )
837    }
838}