1use borsh::BorshDeserialize;
9use borsh::BorshSerialize;
10
11pub struct Transfer {
13 pub user_token_account: solana_program::pubkey::Pubkey,
15 pub recipient_token_account: solana_program::pubkey::Pubkey,
17 pub user: solana_program::pubkey::Pubkey,
19 pub payer: Option<solana_program::pubkey::Pubkey>,
21 pub system_program: Option<solana_program::pubkey::Pubkey>,
23}
24
25impl Transfer {
26 pub fn instruction(
27 &self,
28 args: TransferInstructionArgs,
29 ) -> solana_program::instruction::Instruction {
30 self.instruction_with_remaining_accounts(args, &[])
31 }
32 #[allow(clippy::vec_init_then_push)]
33 pub fn instruction_with_remaining_accounts(
34 &self,
35 args: TransferInstructionArgs,
36 remaining_accounts: &[solana_program::instruction::AccountMeta],
37 ) -> solana_program::instruction::Instruction {
38 let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
39 accounts.push(solana_program::instruction::AccountMeta::new(
40 self.user_token_account,
41 false,
42 ));
43 accounts.push(solana_program::instruction::AccountMeta::new(
44 self.recipient_token_account,
45 false,
46 ));
47 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
48 self.user, true,
49 ));
50 if let Some(payer) = self.payer {
51 accounts.push(solana_program::instruction::AccountMeta::new(payer, true));
52 } else {
53 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
54 crate::SIGIL_ID,
55 false,
56 ));
57 }
58 if let Some(system_program) = self.system_program {
59 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
60 system_program,
61 false,
62 ));
63 } else {
64 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
65 crate::SIGIL_ID,
66 false,
67 ));
68 }
69 accounts.extend_from_slice(remaining_accounts);
70 let mut data = TransferInstructionData::new().try_to_vec().unwrap();
71 let mut args = args.try_to_vec().unwrap();
72 data.append(&mut args);
73
74 solana_program::instruction::Instruction {
75 program_id: crate::SIGIL_ID,
76 accounts,
77 data,
78 }
79 }
80}
81
82#[derive(BorshDeserialize, BorshSerialize)]
83pub struct TransferInstructionData {
84 discriminator: u8,
85}
86
87impl TransferInstructionData {
88 pub fn new() -> Self {
89 Self { discriminator: 6 }
90 }
91}
92
93#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95pub struct TransferInstructionArgs {
96 pub ticker: [u8; 4],
97 pub amount: u32,
98}
99
100#[derive(Default)]
110pub struct TransferBuilder {
111 user_token_account: Option<solana_program::pubkey::Pubkey>,
112 recipient_token_account: Option<solana_program::pubkey::Pubkey>,
113 user: Option<solana_program::pubkey::Pubkey>,
114 payer: Option<solana_program::pubkey::Pubkey>,
115 system_program: Option<solana_program::pubkey::Pubkey>,
116 ticker: Option<[u8; 4]>,
117 amount: Option<u32>,
118 __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
119}
120
121impl TransferBuilder {
122 pub fn new() -> Self {
123 Self::default()
124 }
125 #[inline(always)]
127 pub fn user_token_account(
128 &mut self,
129 user_token_account: solana_program::pubkey::Pubkey,
130 ) -> &mut Self {
131 self.user_token_account = Some(user_token_account);
132 self
133 }
134 #[inline(always)]
136 pub fn recipient_token_account(
137 &mut self,
138 recipient_token_account: solana_program::pubkey::Pubkey,
139 ) -> &mut Self {
140 self.recipient_token_account = Some(recipient_token_account);
141 self
142 }
143 #[inline(always)]
145 pub fn user(&mut self, user: solana_program::pubkey::Pubkey) -> &mut Self {
146 self.user = Some(user);
147 self
148 }
149 #[inline(always)]
152 pub fn payer(&mut self, payer: Option<solana_program::pubkey::Pubkey>) -> &mut Self {
153 self.payer = payer;
154 self
155 }
156 #[inline(always)]
159 pub fn system_program(
160 &mut self,
161 system_program: Option<solana_program::pubkey::Pubkey>,
162 ) -> &mut Self {
163 self.system_program = system_program;
164 self
165 }
166 #[inline(always)]
167 pub fn ticker(&mut self, ticker: [u8; 4]) -> &mut Self {
168 self.ticker = Some(ticker);
169 self
170 }
171 #[inline(always)]
172 pub fn amount(&mut self, amount: u32) -> &mut Self {
173 self.amount = Some(amount);
174 self
175 }
176 #[inline(always)]
178 pub fn add_remaining_account(
179 &mut self,
180 account: solana_program::instruction::AccountMeta,
181 ) -> &mut Self {
182 self.__remaining_accounts.push(account);
183 self
184 }
185 #[inline(always)]
187 pub fn add_remaining_accounts(
188 &mut self,
189 accounts: &[solana_program::instruction::AccountMeta],
190 ) -> &mut Self {
191 self.__remaining_accounts.extend_from_slice(accounts);
192 self
193 }
194 #[allow(clippy::clone_on_copy)]
195 pub fn instruction(&self) -> solana_program::instruction::Instruction {
196 let accounts = Transfer {
197 user_token_account: self
198 .user_token_account
199 .expect("user_token_account is not set"),
200 recipient_token_account: self
201 .recipient_token_account
202 .expect("recipient_token_account is not set"),
203 user: self.user.expect("user is not set"),
204 payer: self.payer,
205 system_program: self.system_program,
206 };
207 let args = TransferInstructionArgs {
208 ticker: self.ticker.clone().expect("ticker is not set"),
209 amount: self.amount.clone().expect("amount is not set"),
210 };
211
212 accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts)
213 }
214}
215
216pub struct TransferCpiAccounts<'a, 'b> {
218 pub user_token_account: &'b solana_program::account_info::AccountInfo<'a>,
220 pub recipient_token_account: &'b solana_program::account_info::AccountInfo<'a>,
222 pub user: &'b solana_program::account_info::AccountInfo<'a>,
224 pub payer: Option<&'b solana_program::account_info::AccountInfo<'a>>,
226 pub system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
228}
229
230pub struct TransferCpi<'a, 'b> {
232 pub __program: &'b solana_program::account_info::AccountInfo<'a>,
234 pub user_token_account: &'b solana_program::account_info::AccountInfo<'a>,
236 pub recipient_token_account: &'b solana_program::account_info::AccountInfo<'a>,
238 pub user: &'b solana_program::account_info::AccountInfo<'a>,
240 pub payer: Option<&'b solana_program::account_info::AccountInfo<'a>>,
242 pub system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
244 pub __args: TransferInstructionArgs,
246}
247
248impl<'a, 'b> TransferCpi<'a, 'b> {
249 pub fn new(
250 program: &'b solana_program::account_info::AccountInfo<'a>,
251 accounts: TransferCpiAccounts<'a, 'b>,
252 args: TransferInstructionArgs,
253 ) -> Self {
254 Self {
255 __program: program,
256 user_token_account: accounts.user_token_account,
257 recipient_token_account: accounts.recipient_token_account,
258 user: accounts.user,
259 payer: accounts.payer,
260 system_program: accounts.system_program,
261 __args: args,
262 }
263 }
264 #[inline(always)]
265 pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
266 self.invoke_signed_with_remaining_accounts(&[], &[])
267 }
268 #[inline(always)]
269 pub fn invoke_with_remaining_accounts(
270 &self,
271 remaining_accounts: &[(
272 &'b solana_program::account_info::AccountInfo<'a>,
273 bool,
274 bool,
275 )],
276 ) -> solana_program::entrypoint::ProgramResult {
277 self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
278 }
279 #[inline(always)]
280 pub fn invoke_signed(
281 &self,
282 signers_seeds: &[&[&[u8]]],
283 ) -> solana_program::entrypoint::ProgramResult {
284 self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
285 }
286 #[allow(clippy::clone_on_copy)]
287 #[allow(clippy::vec_init_then_push)]
288 pub fn invoke_signed_with_remaining_accounts(
289 &self,
290 signers_seeds: &[&[&[u8]]],
291 remaining_accounts: &[(
292 &'b solana_program::account_info::AccountInfo<'a>,
293 bool,
294 bool,
295 )],
296 ) -> solana_program::entrypoint::ProgramResult {
297 let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
298 accounts.push(solana_program::instruction::AccountMeta::new(
299 *self.user_token_account.key,
300 false,
301 ));
302 accounts.push(solana_program::instruction::AccountMeta::new(
303 *self.recipient_token_account.key,
304 false,
305 ));
306 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
307 *self.user.key,
308 true,
309 ));
310 if let Some(payer) = self.payer {
311 accounts.push(solana_program::instruction::AccountMeta::new(
312 *payer.key, true,
313 ));
314 } else {
315 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
316 crate::SIGIL_ID,
317 false,
318 ));
319 }
320 if let Some(system_program) = self.system_program {
321 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
322 *system_program.key,
323 false,
324 ));
325 } else {
326 accounts.push(solana_program::instruction::AccountMeta::new_readonly(
327 crate::SIGIL_ID,
328 false,
329 ));
330 }
331 remaining_accounts.iter().for_each(|remaining_account| {
332 accounts.push(solana_program::instruction::AccountMeta {
333 pubkey: *remaining_account.0.key,
334 is_signer: remaining_account.1,
335 is_writable: remaining_account.2,
336 })
337 });
338 let mut data = TransferInstructionData::new().try_to_vec().unwrap();
339 let mut args = self.__args.try_to_vec().unwrap();
340 data.append(&mut args);
341
342 let instruction = solana_program::instruction::Instruction {
343 program_id: crate::SIGIL_ID,
344 accounts,
345 data,
346 };
347 let mut account_infos = Vec::with_capacity(5 + 1 + remaining_accounts.len());
348 account_infos.push(self.__program.clone());
349 account_infos.push(self.user_token_account.clone());
350 account_infos.push(self.recipient_token_account.clone());
351 account_infos.push(self.user.clone());
352 if let Some(payer) = self.payer {
353 account_infos.push(payer.clone());
354 }
355 if let Some(system_program) = self.system_program {
356 account_infos.push(system_program.clone());
357 }
358 remaining_accounts
359 .iter()
360 .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
361
362 if signers_seeds.is_empty() {
363 solana_program::program::invoke(&instruction, &account_infos)
364 } else {
365 solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
366 }
367 }
368}
369
370pub struct TransferCpiBuilder<'a, 'b> {
380 instruction: Box<TransferCpiBuilderInstruction<'a, 'b>>,
381}
382
383impl<'a, 'b> TransferCpiBuilder<'a, 'b> {
384 pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
385 let instruction = Box::new(TransferCpiBuilderInstruction {
386 __program: program,
387 user_token_account: None,
388 recipient_token_account: None,
389 user: None,
390 payer: None,
391 system_program: None,
392 ticker: None,
393 amount: None,
394 __remaining_accounts: Vec::new(),
395 });
396 Self { instruction }
397 }
398 #[inline(always)]
400 pub fn user_token_account(
401 &mut self,
402 user_token_account: &'b solana_program::account_info::AccountInfo<'a>,
403 ) -> &mut Self {
404 self.instruction.user_token_account = Some(user_token_account);
405 self
406 }
407 #[inline(always)]
409 pub fn recipient_token_account(
410 &mut self,
411 recipient_token_account: &'b solana_program::account_info::AccountInfo<'a>,
412 ) -> &mut Self {
413 self.instruction.recipient_token_account = Some(recipient_token_account);
414 self
415 }
416 #[inline(always)]
418 pub fn user(&mut self, user: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self {
419 self.instruction.user = Some(user);
420 self
421 }
422 #[inline(always)]
425 pub fn payer(
426 &mut self,
427 payer: Option<&'b solana_program::account_info::AccountInfo<'a>>,
428 ) -> &mut Self {
429 self.instruction.payer = payer;
430 self
431 }
432 #[inline(always)]
435 pub fn system_program(
436 &mut self,
437 system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
438 ) -> &mut Self {
439 self.instruction.system_program = system_program;
440 self
441 }
442 #[inline(always)]
443 pub fn ticker(&mut self, ticker: [u8; 4]) -> &mut Self {
444 self.instruction.ticker = Some(ticker);
445 self
446 }
447 #[inline(always)]
448 pub fn amount(&mut self, amount: u32) -> &mut Self {
449 self.instruction.amount = Some(amount);
450 self
451 }
452 #[inline(always)]
454 pub fn add_remaining_account(
455 &mut self,
456 account: &'b solana_program::account_info::AccountInfo<'a>,
457 is_writable: bool,
458 is_signer: bool,
459 ) -> &mut Self {
460 self.instruction
461 .__remaining_accounts
462 .push((account, is_writable, is_signer));
463 self
464 }
465 #[inline(always)]
470 pub fn add_remaining_accounts(
471 &mut self,
472 accounts: &[(
473 &'b solana_program::account_info::AccountInfo<'a>,
474 bool,
475 bool,
476 )],
477 ) -> &mut Self {
478 self.instruction
479 .__remaining_accounts
480 .extend_from_slice(accounts);
481 self
482 }
483 #[inline(always)]
484 pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
485 self.invoke_signed(&[])
486 }
487 #[allow(clippy::clone_on_copy)]
488 #[allow(clippy::vec_init_then_push)]
489 pub fn invoke_signed(
490 &self,
491 signers_seeds: &[&[&[u8]]],
492 ) -> solana_program::entrypoint::ProgramResult {
493 let args = TransferInstructionArgs {
494 ticker: self.instruction.ticker.clone().expect("ticker is not set"),
495 amount: self.instruction.amount.clone().expect("amount is not set"),
496 };
497 let instruction = TransferCpi {
498 __program: self.instruction.__program,
499
500 user_token_account: self
501 .instruction
502 .user_token_account
503 .expect("user_token_account is not set"),
504
505 recipient_token_account: self
506 .instruction
507 .recipient_token_account
508 .expect("recipient_token_account is not set"),
509
510 user: self.instruction.user.expect("user is not set"),
511
512 payer: self.instruction.payer,
513
514 system_program: self.instruction.system_program,
515 __args: args,
516 };
517 instruction.invoke_signed_with_remaining_accounts(
518 signers_seeds,
519 &self.instruction.__remaining_accounts,
520 )
521 }
522}
523
524struct TransferCpiBuilderInstruction<'a, 'b> {
525 __program: &'b solana_program::account_info::AccountInfo<'a>,
526 user_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>,
527 recipient_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>,
528 user: Option<&'b solana_program::account_info::AccountInfo<'a>>,
529 payer: Option<&'b solana_program::account_info::AccountInfo<'a>>,
530 system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
531 ticker: Option<[u8; 4]>,
532 amount: Option<u32>,
533 __remaining_accounts: Vec<(
535 &'b solana_program::account_info::AccountInfo<'a>,
536 bool,
537 bool,
538 )>,
539}