continuation_router/
processor.rs1use anchor_lang::prelude::*;
2use anchor_spl::token::{Token, TokenAccount};
3use vipers::{assert_keys_eq, invariant};
4
5use crate::{Action, Continuation, SwapActionEvent, TokenAmount};
6
7pub trait ActionInputOutput<'info>: Action {
8 fn input_account(&self) -> &Account<'info, TokenAccount>;
9 fn output_account(&self) -> &Account<'info, TokenAccount>;
10}
11
12pub struct ActionContext<'a, 'b, 'c, 'info, T> {
13 pub program_id: &'a Pubkey,
15 pub action: &'b T,
17 pub remaining_accounts: &'c [AccountInfo<'info>],
20 pub token_program: Program<'info, Token>,
22 pub swap_program: AccountInfo<'info>,
25 pub owner: AccountInfo<'info>,
28}
29
30pub trait Processor<'info>: ActionInputOutput<'info> {
32 fn process_unchecked(&self, amount_in: u64, minimum_amount_out: u64) -> Result<()>;
33
34 fn process(&self, continuation: &mut Account<'info, Continuation>) -> Result<()> {
35 msg!("Router action: {:?}", Self::TYPE);
36 let continuation = continuation;
37 invariant!(continuation.steps_left > 0, NoMoreSteps);
38
39 let input_account = self.input_account();
40 assert_keys_eq!(
41 input_account.key(),
42 continuation.input,
43 PathInputOutputMismatch
44 );
45 assert_keys_eq!(input_account.owner, continuation.owner, InputOwnerMismatch);
46 assert_keys_eq!(
47 input_account.mint,
48 continuation.amount_in.mint,
49 InputMintMismatch
50 );
51
52 let amount_in = continuation.amount_in;
54 invariant!(amount_in.amount != 0, ZeroSwap);
55
56 invariant!(
58 input_account.amount >= amount_in.amount,
59 InsufficientInputBalance
60 );
61
62 let output_account = self.output_account();
64 assert_keys_eq!(
65 output_account.owner,
66 continuation.owner,
67 OutputOwnerMismatch
68 );
69
70 let initial_balance = output_account.amount;
72 let minimum_amount_out = if continuation.steps_left == 1 {
73 assert_keys_eq!(
74 continuation.minimum_amount_out.mint,
75 output_account.mint,
76 OutputMintMismatch
77 );
78 continuation.minimum_amount_out.amount
79 } else {
80 0
81 };
82 self.process_unchecked(amount_in.amount, minimum_amount_out)?;
83 let output_account = &mut output_account.clone();
84 output_account.reload()?;
85 let result_balance = output_account.amount;
86
87 invariant!(result_balance >= initial_balance, BalanceLower);
89 let next_amount_in = result_balance - initial_balance;
90
91 continuation.input = output_account.key();
93 continuation.amount_in = TokenAmount::new(output_account.mint, next_amount_in);
94 continuation.steps_left -= 1;
95
96 emit!(SwapActionEvent {
97 action_type: Self::TYPE,
98 owner: continuation.owner,
99 input_amount: amount_in,
100 output_account: continuation.input,
101 output_amount: continuation.amount_in,
102 });
103 Ok(())
104 }
105}