1use crate::instruction::*;
3use crate::pda::*;
4use solana_program::{
5 instruction::{AccountMeta, Instruction},
6 program_error::ProgramError,
7 pubkey::Pubkey,
8 sysvar,
9};
10use spl_associated_token_account::get_associated_token_address;
11
12pub fn initialize(
14 initializer: &Pubkey,
15 admin: Pubkey,
16 mint_fee_lamports: u64,
17 allocation_fee_per_recipient_lamports: u64,
18 merkle_fee_per_recipient_lamports: u64,
19 direct_transfer_fee_per_recipient_lamports: u64,
20) -> Result<Instruction, ProgramError> {
21 let (config_pda, _) = config_pda();
22 let (fee_account_pda, _) = fee_account_pda();
23
24 let data = Initialize {
25 admin,
26 mint_fee_lamports: mint_fee_lamports.to_le_bytes(),
27 allocation_fee_per_recipient_lamports: allocation_fee_per_recipient_lamports.to_le_bytes(),
28 merkle_fee_per_recipient_lamports: merkle_fee_per_recipient_lamports.to_le_bytes(),
29 direct_transfer_fee_per_recipient_lamports: direct_transfer_fee_per_recipient_lamports
30 .to_le_bytes(),
31 };
32
33 let accounts = vec![
34 AccountMeta::new(*initializer, true),
35 AccountMeta::new(config_pda, false),
36 AccountMeta::new(fee_account_pda, false),
37 AccountMeta::new_readonly(solana_program::system_program::ID, false),
38 AccountMeta::new_readonly(sysvar::rent::ID, false),
39 ];
40
41 Ok(Instruction {
42 program_id: crate::id(),
43 accounts,
44 data: data.to_bytes().to_vec(),
45 })
46}
47
48pub fn create_mint(
50 payer: &Pubkey,
51 mint_id: [u8; 32],
52 noise: [u8; 4],
53 mint: Pubkey, name: &[u8],
55 symbol: &[u8],
56 uri: &[u8],
57 decimals: u8,
58 max_supply: u64,
59) -> Result<Instruction, ProgramError> {
60 let (config_pda, _) = config_pda();
62 let (fee_account_pda, _) = fee_account_pda();
63 let (mint_pda, _) = mint_pda(&mint_id, &noise);
64 let (mint_authority_pda, _) = mint_authority_pda(&mint_pda);
65 let (mint_treasury_pda, _) = mint_treasury_pda(&mint_pda);
66
67 let (mint_metadata_pda, _) = Pubkey::find_program_address(
69 &[
70 b"metadata",
71 mpl_token_metadata::ID.as_ref(),
72 mint_pda.as_ref(),
73 ],
74 &mpl_token_metadata::ID,
75 );
76
77 let mint_treasury_tokens = get_associated_token_address(&mint_treasury_pda, &mint_pda);
79
80 let mut name_padded = [0u8; 32];
82 let name_len = name.len().min(32);
83 name_padded[..name_len].copy_from_slice(&name[..name_len]);
84
85 let mut symbol_padded = [0u8; 10];
86 let symbol_len = symbol.len().min(10);
87 symbol_padded[..symbol_len].copy_from_slice(&symbol[..symbol_len]);
88
89 let mut uri_padded = [0u8; 128];
90 let uri_len = uri.len().min(128);
91 uri_padded[..uri_len].copy_from_slice(&uri[..uri_len]);
92
93 let data = CreateMint {
94 mint_id,
95 noise,
96 mint: if mint == Pubkey::default() {
97 mint_pda
98 } else {
99 mint
100 },
101 name: name_padded,
102 symbol: symbol_padded,
103 uri: uri_padded,
104 decimals,
105 max_supply: max_supply.to_le_bytes(),
106 _padding: [0u8; 3],
107 };
108
109 let accounts = vec![
110 AccountMeta::new(*payer, true),
111 AccountMeta::new_readonly(config_pda, false),
112 AccountMeta::new(mint_pda, false),
113 AccountMeta::new_readonly(mint_authority_pda, false),
114 AccountMeta::new(mint_metadata_pda, false),
115 AccountMeta::new_readonly(mint_treasury_pda, false),
116 AccountMeta::new(mint_treasury_tokens, false),
117 AccountMeta::new(fee_account_pda, false),
118 AccountMeta::new_readonly(solana_program::system_program::ID, false),
119 AccountMeta::new_readonly(spl_token::ID, false),
120 AccountMeta::new_readonly(spl_associated_token_account::ID, false),
121 AccountMeta::new_readonly(mpl_token_metadata::ID, false),
122 AccountMeta::new_readonly(sysvar::rent::ID, false),
123 ];
124
125 Ok(Instruction {
126 program_id: crate::id(),
127 accounts,
128 data: data.to_bytes().to_vec(),
129 })
130}
131
132pub fn create_campaign(
134 payer: &Pubkey,
135 campaign_owner: &Pubkey,
136 campaign_id: [u8; 32],
137 mint: Pubkey,
138 merkle_root: [u8; 32],
139 recipient_count: u64,
140 initial_supply: u64,
141 max_supply: u64,
142) -> Result<Instruction, ProgramError> {
143 let (config_pda, _) = config_pda();
144 let (fee_account_pda, _) = fee_account_pda();
145 let (campaign_pda, _) = campaign_pda(&campaign_id);
146 let (mint_treasury_pda, _) = mint_treasury_pda(&mint);
147 let (campaign_treasury_pda, _) = campaign_treasury_pda(&campaign_id);
148
149 let mint_treasury_tokens = get_associated_token_address(&mint_treasury_pda, &mint);
151 let campaign_treasury_tokens = get_associated_token_address(&campaign_treasury_pda, &mint);
152
153 let data = CreateCampaign {
154 campaign_id,
155 mint,
156 merkle_root,
157 recipient_count: recipient_count.to_le_bytes(),
158 initial_supply: initial_supply.to_le_bytes(),
159 max_supply: max_supply.to_le_bytes(),
160 _padding: [0u8; 7],
161 };
162
163 let accounts = vec![
164 AccountMeta::new(*payer, true),
165 AccountMeta::new(*campaign_owner, true),
166 AccountMeta::new_readonly(config_pda, false),
167 AccountMeta::new(campaign_pda, false),
168 AccountMeta::new_readonly(mint, false),
169 AccountMeta::new_readonly(mint_treasury_pda, false),
170 AccountMeta::new(mint_treasury_tokens, false),
171 AccountMeta::new_readonly(campaign_treasury_pda, false),
172 AccountMeta::new(campaign_treasury_tokens, false),
173 AccountMeta::new(fee_account_pda, false),
174 AccountMeta::new_readonly(spl_token::ID, false),
175 AccountMeta::new_readonly(spl_associated_token_account::ID, false),
176 AccountMeta::new_readonly(solana_program::system_program::ID, false),
177 AccountMeta::new_readonly(sysvar::rent::ID, false),
178 ];
179
180 Ok(Instruction {
181 program_id: crate::id(),
182 accounts,
183 data: data.to_bytes().to_vec(),
184 })
185}
186
187pub fn direct_transfer(
189 owner: &Pubkey,
190 campaign_id: &[u8; 32],
191 campaign: &Pubkey,
192 config: &Pubkey,
193 fee_account: &Pubkey,
194 treasury: &Pubkey,
195 treasury_tokens: &Pubkey,
196 mint: &Pubkey,
197 token_program: &Pubkey,
198 associated_token_program: &Pubkey,
199 system_program: &Pubkey,
200 transfers: Vec<(Pubkey, u64)>,
201) -> Result<Instruction, ProgramError> {
202 if transfers.is_empty() || transfers.len() > 10 {
203 return Err(ProgramError::InvalidInstructionData);
204 }
205
206 let mut data = DirectTransfer {
207 campaign_id: *campaign_id,
208 count: transfers.len() as u8,
209 _padding: [0u8; 7],
210 }
211 .to_bytes()
212 .to_vec();
213
214 for (recipient, amount) in &transfers {
216 data.extend_from_slice(recipient.as_ref());
217 data.extend_from_slice(&amount.to_le_bytes());
218 }
219
220 let mut accounts = vec![
221 AccountMeta::new(*owner, true),
222 AccountMeta::new_readonly(*campaign, false),
223 AccountMeta::new_readonly(*config, false),
224 AccountMeta::new(*fee_account, false),
225 AccountMeta::new_readonly(*treasury, false),
226 AccountMeta::new(*treasury_tokens, false),
227 AccountMeta::new_readonly(*mint, false),
228 AccountMeta::new_readonly(*token_program, false),
229 AccountMeta::new_readonly(*associated_token_program, false),
230 AccountMeta::new_readonly(*system_program, false),
231 ];
232
233 for (recipient, _) in &transfers {
235 let recipient_tokens = get_associated_token_address(recipient, mint);
236 accounts.push(AccountMeta::new(recipient_tokens, false));
237 }
238
239 Ok(Instruction {
240 program_id: crate::id(),
241 accounts,
242 data,
243 })
244}
245
246pub fn replenish(
248 campaign_owner: &Pubkey,
249 campaign_id: &[u8; 32],
250 campaign: &Pubkey,
251 mint: &Pubkey,
252 mint_treasury: &Pubkey,
253 mint_treasury_tokens: &Pubkey,
254 campaign_treasury: &Pubkey,
255 campaign_treasury_tokens: &Pubkey,
256 token_program: &Pubkey,
257 amount: u64,
258) -> Result<Instruction, ProgramError> {
259 let data = Replenish {
260 campaign_id: *campaign_id,
261 amount: amount.to_le_bytes(),
262 _padding: [0u8; 7],
263 };
264
265 let accounts = vec![
266 AccountMeta::new(*campaign_owner, true),
267 AccountMeta::new(*campaign, false),
268 AccountMeta::new_readonly(*mint, false),
269 AccountMeta::new_readonly(*mint_treasury, false),
270 AccountMeta::new(*mint_treasury_tokens, false),
271 AccountMeta::new_readonly(*campaign_treasury, false),
272 AccountMeta::new(*campaign_treasury_tokens, false),
273 AccountMeta::new_readonly(*token_program, false),
274 ];
275
276 Ok(Instruction {
277 program_id: crate::id(),
278 accounts,
279 data: data.to_bytes().to_vec(),
280 })
281}
282
283pub fn update_config(
285 admin: &Pubkey,
286 new_admin: Pubkey, mint_fee_lamports: u64, allocation_fee_per_recipient_lamports: u64, merkle_fee_per_recipient_lamports: u64, direct_transfer_fee_per_recipient_lamports: u64, ) -> Result<Instruction, ProgramError> {
292 let (config_pda, _) = config_pda();
293
294 let data = UpdateConfig {
295 new_admin,
296 mint_fee_lamports: mint_fee_lamports.to_le_bytes(),
297 allocation_fee_per_recipient_lamports: allocation_fee_per_recipient_lamports.to_le_bytes(),
298 merkle_fee_per_recipient_lamports: merkle_fee_per_recipient_lamports.to_le_bytes(),
299 direct_transfer_fee_per_recipient_lamports: direct_transfer_fee_per_recipient_lamports
300 .to_le_bytes(),
301 };
302
303 let accounts = vec![
304 AccountMeta::new(*admin, true),
305 AccountMeta::new(config_pda, false),
306 ];
307
308 Ok(Instruction {
309 program_id: crate::id(),
310 accounts,
311 data: data.to_bytes().to_vec(),
312 })
313}
314
315pub fn withdraw_fees(
317 admin: &Pubkey,
318 config: &Pubkey,
319 fee_account: &Pubkey,
320 beneficiary: &Pubkey,
321 system_program: &Pubkey,
322 amount: u64, ) -> Result<Instruction, ProgramError> {
324 let data = WithdrawFees {
325 amount: amount.to_le_bytes(),
326 };
327
328 let accounts = vec![
329 AccountMeta::new(*admin, true),
330 AccountMeta::new_readonly(*config, false),
331 AccountMeta::new(*fee_account, false),
332 AccountMeta::new(*beneficiary, false),
333 AccountMeta::new_readonly(*system_program, false),
334 ];
335
336 Ok(Instruction {
337 program_id: crate::id(),
338 accounts,
339 data: data.to_bytes().to_vec(),
340 })
341}
342
343pub fn allocate_onchain(
345 owner: &Pubkey,
346 campaign_id: &[u8; 32],
347 campaign: &Pubkey,
348 config: &Pubkey,
349 fee_account: &Pubkey,
350 treasury_tokens: &Pubkey,
351 token_program: &Pubkey,
352 system_program: &Pubkey,
353 allocations: Vec<(Pubkey, u64)>,
354) -> Result<Instruction, ProgramError> {
355 let mut data = Allocate {
356 campaign_id: *campaign_id,
357 count: allocations.len() as u8,
358 _padding: [0u8; 7],
359 }
360 .to_bytes()
361 .to_vec();
362
363 for (recipient, amount) in &allocations {
365 data.extend_from_slice(recipient.as_ref());
366 data.extend_from_slice(&amount.to_le_bytes());
367 }
368
369 let mut accounts = vec![
370 AccountMeta::new(*owner, true),
371 AccountMeta::new(*campaign, false),
372 AccountMeta::new_readonly(*config, false),
373 AccountMeta::new(*fee_account, false),
374 AccountMeta::new(*treasury_tokens, false),
375 AccountMeta::new_readonly(*token_program, false),
376 AccountMeta::new_readonly(*system_program, false),
377 ];
378
379 for (recipient, _) in &allocations {
381 let (allocation_pda, _) = crate::pda::allocation_pda(*campaign, *recipient);
382 accounts.push(AccountMeta::new(allocation_pda, false));
383 }
384
385 Ok(Instruction {
386 program_id: crate::id(),
387 accounts,
388 data,
389 })
390}
391
392pub fn claim_onchain(
394 recipient: &Pubkey,
395 campaign_id: &[u8; 32],
396 campaign: &Pubkey,
397 allocation: &Pubkey,
398 treasury: &Pubkey,
399 treasury_tokens: &Pubkey,
400 recipient_tokens: &Pubkey,
401 mint: &Pubkey,
402 token_program: &Pubkey,
403 associated_token_program: &Pubkey,
404 system_program: &Pubkey,
405) -> Result<Instruction, ProgramError> {
406 let data = Claim {
407 campaign_id: *campaign_id,
408 method: 0, amount: [0u8; 8],
410 merkle_proof_length: 0,
411 _padding: [0u8; 6],
412 };
413
414 let accounts = vec![
415 AccountMeta::new(*recipient, true),
416 AccountMeta::new_readonly(*campaign, false),
417 AccountMeta::new(*allocation, false),
418 AccountMeta::new_readonly(*treasury, false), AccountMeta::new(*treasury_tokens, false),
420 AccountMeta::new(*recipient_tokens, false),
421 AccountMeta::new_readonly(*mint, false),
422 AccountMeta::new_readonly(*token_program, false),
423 AccountMeta::new_readonly(*associated_token_program, false),
424 AccountMeta::new_readonly(*system_program, false),
425 ];
426
427 Ok(Instruction {
428 program_id: crate::id(),
429 accounts,
430 data: data.to_bytes().to_vec(),
431 })
432}
433
434pub fn claim_offchain(
437 recipient: &Pubkey,
438 campaign_id: &[u8; 32],
439 campaign: &Pubkey,
440 allocation: &Pubkey, treasury: &Pubkey,
442 treasury_tokens: &Pubkey,
443 recipient_tokens: &Pubkey,
444 mint: &Pubkey,
445 token_program: &Pubkey,
446 associated_token_program: &Pubkey,
447 system_program: &Pubkey,
448 amount: u64,
449 signature: &[u8; 64],
450) -> Result<Instruction, ProgramError> {
451 let mut data = Claim {
452 campaign_id: *campaign_id,
453 method: 1, amount: amount.to_le_bytes(),
455 merkle_proof_length: 0,
456 _padding: [0u8; 6],
457 }
458 .to_bytes()
459 .to_vec();
460
461 data.extend_from_slice(signature);
463
464 let accounts = vec![
465 AccountMeta::new(*recipient, true),
466 AccountMeta::new_readonly(*campaign, false),
467 AccountMeta::new_readonly(*allocation, false), AccountMeta::new_readonly(*treasury, false), AccountMeta::new(*treasury_tokens, false),
470 AccountMeta::new(*recipient_tokens, false),
471 AccountMeta::new_readonly(*mint, false),
472 AccountMeta::new_readonly(*token_program, false),
473 AccountMeta::new_readonly(*associated_token_program, false),
474 AccountMeta::new_readonly(*system_program, false),
475 ];
476
477 Ok(Instruction {
478 program_id: crate::id(),
479 accounts,
480 data,
481 })
482}
483
484pub fn claim_merkle(
487 recipient: &Pubkey,
488 campaign_id: &[u8; 32],
489 campaign: &Pubkey,
490 allocation: &Pubkey, treasury: &Pubkey,
492 treasury_tokens: &Pubkey,
493 recipient_tokens: &Pubkey,
494 mint: &Pubkey,
495 token_program: &Pubkey,
496 associated_token_program: &Pubkey,
497 system_program: &Pubkey,
498 amount: u64,
499 proof_path: &[[u8; 32]],
500 proof_indices: &[bool],
501) -> Result<Instruction, ProgramError> {
502 if proof_path.len() != proof_indices.len() {
503 return Err(ProgramError::InvalidInstructionData);
504 }
505 if proof_path.len() > crate::consts::MAX_MERKLE_PROOF_LENGTH as usize {
506 return Err(ProgramError::InvalidInstructionData);
507 }
508
509 let proof_length = proof_path.len() as u8;
510 let mut data = Claim {
511 campaign_id: *campaign_id,
512 method: 2, amount: amount.to_le_bytes(),
514 merkle_proof_length: proof_length,
515 _padding: [0u8; 6],
516 }
517 .to_bytes()
518 .to_vec();
519
520 for hash in proof_path {
522 data.extend_from_slice(hash);
523 }
524
525 for &is_right in proof_indices {
527 data.push(if is_right { 1 } else { 0 });
528 }
529
530 let accounts = vec![
531 AccountMeta::new(*recipient, true),
532 AccountMeta::new_readonly(*campaign, false),
533 AccountMeta::new_readonly(*allocation, false), AccountMeta::new_readonly(*treasury, false), AccountMeta::new(*treasury_tokens, false),
536 AccountMeta::new(*recipient_tokens, false),
537 AccountMeta::new_readonly(*mint, false),
538 AccountMeta::new_readonly(*token_program, false),
539 AccountMeta::new_readonly(*associated_token_program, false),
540 AccountMeta::new_readonly(*system_program, false),
541 ];
542
543 Ok(Instruction {
544 program_id: crate::id(),
545 accounts,
546 data,
547 })
548}
549
550pub fn mint_tokens(
553 campaign_owner: &Pubkey,
554 campaign_id: &[u8; 32],
555 campaign: &Pubkey,
556 mint: &Pubkey,
557 mint_authority: &Pubkey,
558 treasury_tokens: &Pubkey,
559 token_program: &Pubkey,
560 amount: u64,
561) -> Result<Instruction, ProgramError> {
562 let data = MintTokens {
563 campaign_id: *campaign_id,
564 amount: amount.to_le_bytes(),
565 _padding: [0u8; 7],
566 };
567
568 let accounts = vec![
569 AccountMeta::new(*campaign_owner, true),
570 AccountMeta::new(*campaign, false),
571 AccountMeta::new_readonly(*mint, false),
572 AccountMeta::new_readonly(*mint_authority, false),
573 AccountMeta::new(*treasury_tokens, false),
574 AccountMeta::new_readonly(*token_program, false),
575 ];
576
577 Ok(Instruction {
578 program_id: crate::id(),
579 accounts,
580 data: data.to_bytes().to_vec(),
581 })
582}