express_relay/sdk/
helpers.rs

1use {
2    crate::{
3        accounts,
4        instruction,
5        ExpressRelayMetadata,
6        FeeToken,
7        SubmitBidArgs,
8        SwapV2Args,
9        SEED_CONFIG_ROUTER,
10        SEED_METADATA,
11    },
12    anchor_lang::{
13        prelude::*,
14        solana_program::{
15            instruction::Instruction,
16            sysvar::instructions as sysvar_instructions,
17        },
18        system_program,
19        InstructionData,
20    },
21    anchor_spl::associated_token::{
22        self,
23        get_associated_token_address_with_program_id,
24    },
25};
26
27/// Creates and adds to the provided instructions a `SubmitBid` instruction.
28#[allow(clippy::too_many_arguments)]
29pub fn add_submit_bid_instruction(
30    ixs: &mut Vec<Instruction>,
31    express_relay_pid: Pubkey,
32    searcher: Pubkey,
33    relayer_signer: Pubkey,
34    fee_receiver_relayer: Pubkey,
35    permission: Pubkey,
36    router: Pubkey,
37    bid_amount: u64,
38    deadline: i64,
39) -> Vec<Instruction> {
40    let ix_submit_bid = create_submit_bid_instruction(
41        express_relay_pid,
42        searcher,
43        relayer_signer,
44        fee_receiver_relayer,
45        permission,
46        router,
47        deadline,
48        bid_amount,
49    );
50    ixs.push(ix_submit_bid);
51
52    ixs.to_vec()
53}
54
55#[allow(clippy::too_many_arguments)]
56pub fn create_submit_bid_instruction(
57    express_relay_pid: Pubkey,
58    searcher: Pubkey,
59    relayer_signer: Pubkey,
60    fee_receiver_relayer: Pubkey,
61    permission: Pubkey,
62    router: Pubkey,
63    deadline: i64,
64    bid_amount: u64,
65) -> Instruction {
66    let config_router =
67        Pubkey::find_program_address(&[SEED_CONFIG_ROUTER, router.as_ref()], &express_relay_pid).0;
68    let express_relay_metadata =
69        Pubkey::find_program_address(&[SEED_METADATA], &express_relay_pid).0;
70
71    let accounts_submit_bid = accounts::SubmitBid {
72        searcher,
73        relayer_signer,
74        permission,
75        router,
76        config_router,
77        fee_receiver_relayer,
78        express_relay_metadata,
79        system_program: system_program::ID,
80        sysvar_instructions: sysvar_instructions::ID,
81    }
82    .to_account_metas(None);
83    let data_submit_bid = instruction::SubmitBid {
84        data: SubmitBidArgs {
85            deadline,
86            bid_amount,
87        },
88    }
89    .data();
90
91    Instruction {
92        program_id: express_relay_pid,
93        accounts:   accounts_submit_bid,
94        data:       data_submit_bid,
95    }
96}
97
98#[allow(clippy::too_many_arguments)]
99pub fn create_swap_instruction(
100    express_relay_pid: Pubkey,
101    searcher: Pubkey,
102    user: Pubkey,
103    searcher_ta_mint_searcher: Option<Pubkey>,
104    searcher_ta_mint_user: Option<Pubkey>,
105    router_fee_receiver_ta: Pubkey,
106    fee_receiver_relayer: Pubkey,
107    mint_searcher: Pubkey,
108    mint_user: Pubkey,
109    token_program_searcher: Pubkey,
110    token_program_user: Pubkey,
111    swap_args: SwapV2Args,
112    relayer_signer: Pubkey,
113) -> Instruction {
114    let express_relay_metadata =
115        Pubkey::find_program_address(&[SEED_METADATA], &express_relay_pid).0;
116
117    let (mint_fee, token_program_fee) = match swap_args.fee_token {
118        FeeToken::Searcher => (mint_searcher, token_program_searcher),
119        FeeToken::User => (mint_user, token_program_user),
120    };
121
122    let accounts_swap = accounts::Swap {
123        searcher,
124        user,
125        searcher_ta_mint_searcher: searcher_ta_mint_searcher.unwrap_or(
126            get_associated_token_address_with_program_id(
127                &searcher,
128                &mint_searcher,
129                &token_program_searcher,
130            ),
131        ),
132        searcher_ta_mint_user: searcher_ta_mint_user.unwrap_or(
133            get_associated_token_address_with_program_id(
134                &searcher,
135                &mint_user,
136                &token_program_user,
137            ),
138        ),
139        user_ata_mint_searcher: get_associated_token_address_with_program_id(
140            &user,
141            &mint_searcher,
142            &token_program_searcher,
143        ),
144        user_ata_mint_user: get_associated_token_address_with_program_id(
145            &user,
146            &mint_user,
147            &token_program_user,
148        ),
149        router_fee_receiver_ta,
150        relayer_fee_receiver_ata: get_associated_token_address_with_program_id(
151            &fee_receiver_relayer,
152            &mint_fee,
153            &token_program_fee,
154        ),
155        express_relay_fee_receiver_ata: get_associated_token_address_with_program_id(
156            &express_relay_metadata,
157            &mint_fee,
158            &token_program_fee,
159        ),
160        mint_searcher,
161        mint_user,
162        mint_fee,
163        token_program_searcher,
164        token_program_user,
165        token_program_fee,
166        express_relay_metadata,
167        relayer_signer,
168    }
169    .to_account_metas(None);
170    let data_submit_bid = instruction::SwapV2 { data: swap_args }.data();
171
172    Instruction {
173        program_id: express_relay_pid,
174        accounts:   accounts_swap,
175        data:       data_submit_bid,
176    }
177}
178
179pub fn deserialize_metadata(data: Vec<u8>) -> Result<ExpressRelayMetadata> {
180    let buf = &mut &data[..];
181    match ExpressRelayMetadata::try_deserialize(buf) {
182        Ok(metadata) => Ok(metadata),
183        Err(_) => Err(ProgramError::InvalidAccountData.into()),
184    }
185}
186
187/// Creates CreateIdempotent instruction
188pub fn create_associated_token_account_idempotent(
189    funding_address: &Pubkey,
190    wallet_address: &Pubkey,
191    token_mint_address: &Pubkey,
192    token_program_id: &Pubkey,
193) -> Instruction {
194    build_associated_token_account_instruction(
195        funding_address,
196        wallet_address,
197        token_mint_address,
198        token_program_id,
199        1, // AssociatedTokenAccountInstruction::CreateIdempotent
200    )
201}
202
203fn build_associated_token_account_instruction(
204    funding_address: &Pubkey,
205    wallet_address: &Pubkey,
206    token_mint_address: &Pubkey,
207    token_program_id: &Pubkey,
208    instruction: u8,
209) -> Instruction {
210    let associated_account_address = get_associated_token_address_with_program_id(
211        wallet_address,
212        token_mint_address,
213        token_program_id,
214    );
215    // safety check, assert if not a creation instruction, which is only 0 or 1
216    assert!(instruction <= 1);
217    Instruction {
218        program_id: associated_token::ID,
219        accounts:   vec![
220            AccountMeta::new(*funding_address, true),
221            AccountMeta::new(associated_account_address, false),
222            AccountMeta::new_readonly(*wallet_address, false),
223            AccountMeta::new_readonly(*token_mint_address, false),
224            AccountMeta::new_readonly(system_program::ID, false),
225            AccountMeta::new_readonly(*token_program_id, false),
226        ],
227        data:       vec![instruction],
228    }
229}