1use bytemuck::{Pod, Zeroable};
2use drillx::Solution;
3use num_enum::TryFromPrimitive;
4use solana_program::{
5 instruction::{AccountMeta, Instruction},
6 pubkey::Pubkey,
7 system_program, sysvar,
8};
9
10use crate::{
11 consts::*,
12 utils::{impl_instruction_from_bytes, impl_to_bytes},
13};
14
15#[repr(u8)]
16#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
17#[rustfmt::skip]
18pub enum CoalInstruction {
19 Claim = 0,
21 Close = 1,
22 Mine = 2,
23 OpenCoal = 3,
24 Reset = 4,
25 Stake = 5,
26 Update = 6,
27 OpenWood = 7,
28 Equip = 8,
29 Unequip = 9,
30 InitReprocess = 10,
31 FinalizeReprocess = 11,
32 InitChromium = 102,
36}
37
38impl CoalInstruction {
39 pub fn to_vec(&self) -> Vec<u8> {
40 vec![*self as u8]
41 }
42}
43
44#[repr(C)]
45#[derive(Clone, Copy, Debug, Pod, Zeroable)]
46pub struct InitializeArgs {
47 pub bus_0_bump: u8,
48 pub bus_1_bump: u8,
49 pub bus_2_bump: u8,
50 pub bus_3_bump: u8,
51 pub bus_4_bump: u8,
52 pub bus_5_bump: u8,
53 pub bus_6_bump: u8,
54 pub bus_7_bump: u8,
55 pub config_bump: u8,
56 pub metadata_bump: u8,
57 pub mint_bump: u8,
58 pub treasury_bump: u8,
59}
60
61#[repr(C)]
62#[derive(Clone, Copy, Debug, Pod, Zeroable)]
63pub struct InitChromiumArgs {
64 pub metadata_bump: u8,
65 pub mint_bump: u8,
66 pub treasury_bump: u8,
67}
68
69#[repr(C)]
70#[derive(Clone, Copy, Debug, Pod, Zeroable)]
71pub struct OpenArgs {
72 pub bump: u8,
73}
74
75#[repr(C)]
76#[derive(Clone, Copy, Debug, Pod, Zeroable)]
77pub struct EquipArgs {
78 pub bump: u8,
79}
80
81#[repr(C)]
82#[derive(Clone, Copy, Debug, Pod, Zeroable)]
83pub struct UnequipArgs {
84 pub bump: u8,
85 pub plugin_authority_bump: u8,
86}
87
88#[repr(C)]
89#[derive(Clone, Copy, Debug, Pod, Zeroable)]
90pub struct MineArgs {
91 pub digest: [u8; 16],
92 pub nonce: [u8; 8],
93}
94
95#[repr(C)]
96#[derive(Clone, Copy, Debug, Pod, Zeroable)]
97pub struct MineArgsV2 {
98 pub digest: [u8; 16],
99 pub nonce: [u8; 8],
100}
101
102#[repr(C)]
103#[derive(Clone, Copy, Debug, Pod, Zeroable)]
104pub struct ClaimArgs {
105 pub amount: [u8; 8],
106}
107
108#[repr(C)]
109#[derive(Clone, Copy, Debug, Pod, Zeroable)]
110pub struct ReprocessArgs {
111 pub reprocessor_bump: u8,
112}
113
114#[repr(C)]
115#[derive(Clone, Copy, Debug, Pod, Zeroable)]
116pub struct StakeArgs {
117 pub amount: [u8; 8],
118}
119
120#[repr(C)]
121#[derive(Clone, Copy, Debug, Pod, Zeroable)]
122pub struct UpgradeArgs {
123 pub amount: [u8; 8],
124}
125
126impl_to_bytes!(InitializeArgs);
127impl_to_bytes!(InitChromiumArgs);
128impl_to_bytes!(OpenArgs);
129impl_to_bytes!(MineArgs);
130impl_to_bytes!(ClaimArgs);
131impl_to_bytes!(StakeArgs);
132impl_to_bytes!(UpgradeArgs);
133impl_to_bytes!(EquipArgs);
134impl_to_bytes!(UnequipArgs);
135impl_to_bytes!(ReprocessArgs);
136
137impl_instruction_from_bytes!(InitializeArgs);
138impl_instruction_from_bytes!(InitChromiumArgs);
139impl_instruction_from_bytes!(OpenArgs);
140impl_instruction_from_bytes!(MineArgs);
141impl_instruction_from_bytes!(ClaimArgs);
142impl_instruction_from_bytes!(StakeArgs);
143impl_instruction_from_bytes!(UpgradeArgs);
144impl_instruction_from_bytes!(EquipArgs);
145impl_instruction_from_bytes!(UnequipArgs);
146impl_instruction_from_bytes!(ReprocessArgs);
147
148pub fn auth(proof: Pubkey) -> Instruction {
150 Instruction {
151 program_id: NOOP_PROGRAM_ID,
152 accounts: vec![],
153 data: proof.to_bytes().to_vec(),
154 }
155}
156
157pub fn claim_coal(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction {
159 let proof = Pubkey::find_program_address(&[COAL_PROOF, signer.as_ref()], &crate::id()).0;
160 let treasury_tokens = spl_associated_token_account::get_associated_token_address(
161 &TREASURY_ADDRESS,
162 &COAL_MINT_ADDRESS,
163 );
164 Instruction {
165 program_id: crate::id(),
166 accounts: vec![
167 AccountMeta::new(signer, true),
168 AccountMeta::new(beneficiary, false),
169 AccountMeta::new(proof, false),
170 AccountMeta::new_readonly(TREASURY_ADDRESS, false),
171 AccountMeta::new(treasury_tokens, false),
172 AccountMeta::new_readonly(spl_token::id(), false),
173 ],
174 data: [
175 CoalInstruction::Claim.to_vec(),
176 ClaimArgs {
177 amount: amount.to_le_bytes(),
178 }
179 .to_bytes()
180 .to_vec(),
181 ]
182 .concat(),
183 }
184}
185
186pub fn claim_wood(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction {
187 let proof = Pubkey::find_program_address(&[WOOD_PROOF, signer.as_ref()], &crate::id()).0;
188 let treasury_tokens = spl_associated_token_account::get_associated_token_address(
189 &TREASURY_ADDRESS,
190 &WOOD_MINT_ADDRESS,
191 );
192 Instruction {
193 program_id: crate::id(),
194 accounts: vec![
195 AccountMeta::new(signer, true),
196 AccountMeta::new(beneficiary, false),
197 AccountMeta::new(proof, false),
198 AccountMeta::new_readonly(TREASURY_ADDRESS, false),
199 AccountMeta::new(treasury_tokens, false),
200 AccountMeta::new_readonly(spl_token::id(), false),
201 ],
202 data: [
203 CoalInstruction::Claim.to_vec(),
204 ClaimArgs {
205 amount: amount.to_le_bytes(),
206 }
207 .to_bytes()
208 .to_vec(),
209 ]
210 .concat(),
211 }
212}
213
214pub fn close_coal(signer: Pubkey) -> Instruction {
216 let proof_pda = Pubkey::find_program_address(&[COAL_PROOF, signer.as_ref()], &crate::id());
217 Instruction {
218 program_id: crate::id(),
219 accounts: vec![
220 AccountMeta::new(signer, true),
221 AccountMeta::new(proof_pda.0, false),
222 AccountMeta::new_readonly(solana_program::system_program::id(), false),
223 ],
224 data: CoalInstruction::Close.to_vec(),
225 }
226}
227
228pub fn close_wood(signer: Pubkey) -> Instruction {
229 let proof_pda = Pubkey::find_program_address(&[WOOD_PROOF, signer.as_ref()], &crate::id());
230 Instruction {
231 program_id: crate::id(),
232 accounts: vec![
233 AccountMeta::new(signer, true),
234 AccountMeta::new(proof_pda.0, false),
235 AccountMeta::new_readonly(solana_program::system_program::id(), false),
236 ],
237 data: CoalInstruction::Close.to_vec(),
238 }
239}
240
241pub fn mine_coal(
243 signer: Pubkey,
244 proof_authority: Pubkey,
245 bus: Pubkey,
246 tool: Option<Pubkey>,
247 member: Option<Pubkey>,
248 guild: Option<Pubkey>,
249 solution: Solution,
250) -> Instruction {
251 let proof = Pubkey::find_program_address(&[COAL_PROOF, proof_authority.as_ref()], &crate::id()).0;
252
253 let mut accounts = vec![
254 AccountMeta::new(signer, true),
255 AccountMeta::new(bus, false),
256 AccountMeta::new_readonly(COAL_CONFIG_ADDRESS, false),
257 AccountMeta::new(proof, false),
258 AccountMeta::new_readonly(sysvar::instructions::id(), false),
259 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
260 ];
261
262 if let Some(tool) = tool {
263 accounts.push(AccountMeta::new(tool, false));
264 }
265
266 if let Some(member) = member {
267 let guild_config = coal_guilds_api::state::config_pda().0;
268 accounts.push(AccountMeta::new_readonly(guild_config, false));
269 accounts.push(AccountMeta::new_readonly(member, false));
270 }
271
272 if let Some(guild) = guild {
273 accounts.push(AccountMeta::new_readonly(guild, false));
274 }
275
276 Instruction {
277 program_id: crate::id(),
278 accounts,
279 data: [
280 CoalInstruction::Mine.to_vec(),
281 MineArgs {
282 digest: solution.d,
283 nonce: solution.n,
284 }
285 .to_bytes()
286 .to_vec(),
287 ]
288 .concat(),
289 }
290}
291
292pub fn chop_wood(
293 signer: Pubkey,
294 proof_authority: Pubkey,
295 bus: Pubkey,
296 solution: Solution,
297) -> Instruction {
298 let proof = Pubkey::find_program_address(&[WOOD_PROOF, proof_authority.as_ref()], &crate::id()).0;
299 let tool = Pubkey::find_program_address(&[WOOD_MAIN_HAND_TOOL, proof_authority.as_ref()], &crate::id()).0;
300
301 Instruction {
302 program_id: crate::id(),
303 accounts: vec![
304 AccountMeta::new(signer, true),
305 AccountMeta::new(bus, false),
306 AccountMeta::new_readonly(WOOD_CONFIG_ADDRESS, false),
307 AccountMeta::new(proof, false),
308 AccountMeta::new_readonly(sysvar::instructions::id(), false),
309 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
310 AccountMeta::new(tool, false),
311 ],
312 data: [
313 CoalInstruction::Mine.to_vec(),
314 MineArgs {
315 digest: solution.d,
316 nonce: solution.n,
317 }
318 .to_bytes()
319 .to_vec(),
320 ]
321 .concat(),
322 }
323}
324
325pub fn open_coal(signer: Pubkey, miner: Pubkey, payer: Pubkey) -> Instruction {
327 let proof_pda = Pubkey::find_program_address(&[COAL_PROOF, signer.as_ref()], &crate::id());
328 Instruction {
329 program_id: crate::id(),
330 accounts: vec![
331 AccountMeta::new(signer, true),
332 AccountMeta::new_readonly(miner, false),
333 AccountMeta::new(payer, true),
334 AccountMeta::new(proof_pda.0, false),
335 AccountMeta::new_readonly(solana_program::system_program::id(), false),
336 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
337 ],
338 data: [
339 CoalInstruction::OpenCoal.to_vec(),
340 OpenArgs { bump: proof_pda.1 }.to_bytes().to_vec(),
341 ]
342 .concat(),
343 }
344}
345
346pub fn open_wood(signer: Pubkey, miner: Pubkey, payer: Pubkey) -> Instruction {
347 let proof_pda = Pubkey::find_program_address(&[WOOD_PROOF, signer.as_ref()], &crate::id());
348 Instruction {
349 program_id: crate::id(),
350 accounts: vec![
351 AccountMeta::new(signer, true),
352 AccountMeta::new_readonly(miner, false),
353 AccountMeta::new(payer, true),
354 AccountMeta::new(proof_pda.0, false),
355 AccountMeta::new_readonly(solana_program::system_program::id(), false),
356 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
357 ],
358 data: [
359 CoalInstruction::OpenWood.to_vec(),
360 OpenArgs { bump: proof_pda.1 }.to_bytes().to_vec(),
361 ]
362 .concat(),
363 }
364}
365
366
367pub fn equip(
369 signer: Pubkey,
370 miner: Pubkey,
371 payer: Pubkey,
372 asset: Pubkey,
373 collection: Pubkey,
374 seed: &[u8],
375) -> Instruction {
376 let tool_pda = Pubkey::find_program_address(&[seed, signer.as_ref()], &crate::id());
377
378 Instruction {
379 program_id: crate::id(),
380 accounts: vec![
381 AccountMeta::new(signer, true),
382 AccountMeta::new_readonly(miner, false),
383 AccountMeta::new(payer, true),
384 AccountMeta::new(asset, false),
385 AccountMeta::new_readonly(collection, false),
386 AccountMeta::new(tool_pda.0, false),
387 AccountMeta::new_readonly(mpl_core::ID, false),
388 AccountMeta::new_readonly(system_program::ID, false),
389 ],
390 data: [
391 CoalInstruction::Equip.to_vec(),
392 EquipArgs {
393 bump: tool_pda.1,
394 }
395 .to_bytes()
396 .to_vec(),
397 ]
398 .concat(),
399 }
400}
401
402pub fn unequip(
404 signer: Pubkey,
405 miner: Pubkey,
406 payer: Pubkey,
407 asset: Pubkey,
408 collection: Pubkey,
409 seed: &[u8],
410) -> Instruction {
411 let tool_pda = Pubkey::find_program_address(&[seed, signer.as_ref()], &crate::id());
412 let plugin_authority = Pubkey::find_program_address(&[PLUGIN_UPDATE_AUTHORITY], &crate::id());
413
414 Instruction {
415 program_id: crate::id(),
416 accounts: vec![
417 AccountMeta::new(signer, true),
418 AccountMeta::new_readonly(miner, false),
419 AccountMeta::new(payer, true),
420 AccountMeta::new(asset, false),
421 AccountMeta::new(collection, false),
422 AccountMeta::new(tool_pda.0, false),
423 AccountMeta::new(plugin_authority.0, false),
424 AccountMeta::new_readonly(mpl_core::ID, false),
425 AccountMeta::new_readonly(system_program::ID, false),
426 ],
427 data: [
428 CoalInstruction::Unequip.to_vec(),
429 UnequipArgs {
430 bump: tool_pda.1,
431 plugin_authority_bump: plugin_authority.1,
432 }
433 .to_bytes()
434 .to_vec(),
435 ]
436 .concat(),
437 }
438}
439
440pub fn reset_coal(signer: Pubkey) -> Instruction {
442 let treasury_tokens = spl_associated_token_account::get_associated_token_address(
443 &TREASURY_ADDRESS,
444 &COAL_MINT_ADDRESS,
445 );
446 Instruction {
447 program_id: crate::id(),
448 accounts: vec![
449 AccountMeta::new(signer, true),
450 AccountMeta::new(COAL_BUS_ADDRESSES[0], false),
451 AccountMeta::new(COAL_BUS_ADDRESSES[1], false),
452 AccountMeta::new(COAL_BUS_ADDRESSES[2], false),
453 AccountMeta::new(COAL_BUS_ADDRESSES[3], false),
454 AccountMeta::new(COAL_BUS_ADDRESSES[4], false),
455 AccountMeta::new(COAL_BUS_ADDRESSES[5], false),
456 AccountMeta::new(COAL_BUS_ADDRESSES[6], false),
457 AccountMeta::new(COAL_BUS_ADDRESSES[7], false),
458 AccountMeta::new(COAL_CONFIG_ADDRESS, false),
459 AccountMeta::new(COAL_MINT_ADDRESS, false),
460 AccountMeta::new(TREASURY_ADDRESS, false),
461 AccountMeta::new(treasury_tokens, false),
462 AccountMeta::new_readonly(spl_token::id(), false),
463 ],
464 data: CoalInstruction::Reset.to_vec(),
465 }
466}
467
468pub fn reset_wood(signer: Pubkey) -> Instruction {
469 let treasury_tokens = spl_associated_token_account::get_associated_token_address(
470 &TREASURY_ADDRESS,
471 &WOOD_MINT_ADDRESS,
472 );
473 Instruction {
474 program_id: crate::id(),
475 accounts: vec![
476 AccountMeta::new(signer, true),
477 AccountMeta::new(WOOD_BUS_ADDRESSES[0], false),
478 AccountMeta::new(WOOD_BUS_ADDRESSES[1], false),
479 AccountMeta::new(WOOD_BUS_ADDRESSES[2], false),
480 AccountMeta::new(WOOD_BUS_ADDRESSES[3], false),
481 AccountMeta::new(WOOD_BUS_ADDRESSES[4], false),
482 AccountMeta::new(WOOD_BUS_ADDRESSES[5], false),
483 AccountMeta::new(WOOD_BUS_ADDRESSES[6], false),
484 AccountMeta::new(WOOD_BUS_ADDRESSES[7], false),
485 AccountMeta::new(WOOD_CONFIG_ADDRESS, false),
486 AccountMeta::new(WOOD_MINT_ADDRESS, false),
487 AccountMeta::new(TREASURY_ADDRESS, false),
488 AccountMeta::new(treasury_tokens, false),
489 AccountMeta::new_readonly(spl_token::id(), false),
490 ],
491 data: CoalInstruction::Reset.to_vec(),
492 }
493}
494
495pub fn stake_coal(signer: Pubkey, sender: Pubkey, amount: u64) -> Instruction {
497 let proof = Pubkey::find_program_address(&[COAL_PROOF, signer.as_ref()], &crate::id()).0;
498 let treasury_tokens = spl_associated_token_account::get_associated_token_address(
499 &TREASURY_ADDRESS,
500 &COAL_MINT_ADDRESS,
501 );
502 Instruction {
503 program_id: crate::id(),
504 accounts: vec![
505 AccountMeta::new(signer, true),
506 AccountMeta::new(proof, false),
507 AccountMeta::new(sender, false),
508 AccountMeta::new(treasury_tokens, false),
509 AccountMeta::new_readonly(spl_token::id(), false),
510 ],
511 data: [
512 CoalInstruction::Stake.to_vec(),
513 StakeArgs {
514 amount: amount.to_le_bytes(),
515 }
516 .to_bytes()
517 .to_vec(),
518 ]
519 .concat(),
520 }
521}
522
523pub fn stake_wood(signer: Pubkey, sender: Pubkey, amount: u64) -> Instruction {
524 let proof = Pubkey::find_program_address(&[WOOD_PROOF, signer.as_ref()], &crate::id()).0;
525 let treasury_tokens = spl_associated_token_account::get_associated_token_address(
526 &TREASURY_ADDRESS,
527 &WOOD_MINT_ADDRESS,
528 );
529 Instruction {
530 program_id: crate::id(),
531 accounts: vec![
532 AccountMeta::new(signer, true),
533 AccountMeta::new(proof, false),
534 AccountMeta::new(sender, false),
535 AccountMeta::new(treasury_tokens, false),
536 AccountMeta::new_readonly(spl_token::id(), false),
537 ],
538 data: [
539 CoalInstruction::Stake.to_vec(),
540 StakeArgs {
541 amount: amount.to_le_bytes(),
542 }
543 .to_bytes()
544 .to_vec(),
545 ]
546 .concat(),
547 }
548}
549
550pub fn update_coal(signer: Pubkey, miner: Pubkey) -> Instruction {
552 let proof = Pubkey::find_program_address(&[COAL_PROOF, signer.as_ref()], &crate::id()).0;
553 Instruction {
554 program_id: crate::id(),
555 accounts: vec![
556 AccountMeta::new(signer, true),
557 AccountMeta::new_readonly(miner, false),
558 AccountMeta::new(proof, false),
559 ],
560 data: CoalInstruction::Update.to_vec(),
561 }
562}
563
564pub fn update_wood(signer: Pubkey, miner: Pubkey) -> Instruction {
565 let proof = Pubkey::find_program_address(&[WOOD_PROOF, signer.as_ref()], &crate::id()).0;
566 Instruction {
567 program_id: crate::id(),
568 accounts: vec![
569 AccountMeta::new(signer, true),
570 AccountMeta::new_readonly(miner, false),
571 AccountMeta::new(proof, false),
572 ],
573 data: CoalInstruction::Update.to_vec(),
574 }
575}
576
577pub fn init_chromium(signer: Pubkey) -> Instruction {
578 let mint_pda = Pubkey::find_program_address(&[CHROMIUM_MINT, MINT_NOISE.as_slice()], &crate::id());
579 let metadata_pda = Pubkey::find_program_address(
580 &[
581 METADATA,
582 mpl_token_metadata::ID.as_ref(),
583 mint_pda.0.as_ref(),
584 ],
585 &mpl_token_metadata::ID,
586 );
587 let treasury_pda = Pubkey::find_program_address(&[TREASURY], &crate::id());
588 let treasury_tokens =
589 spl_associated_token_account::get_associated_token_address(&treasury_pda.0, &mint_pda.0);
590
591 Instruction {
592 program_id: crate::id(),
593 accounts: vec![
594 AccountMeta::new(signer, true),
595 AccountMeta::new(mint_pda.0, false),
596 AccountMeta::new(metadata_pda.0, false),
597 AccountMeta::new_readonly(treasury_pda.0, false),
598 AccountMeta::new(treasury_tokens, false),
599 AccountMeta::new_readonly(system_program::id(), false),
600 AccountMeta::new_readonly(spl_token::id(), false),
601 AccountMeta::new_readonly(spl_associated_token_account::id(), false),
602 AccountMeta::new_readonly(mpl_token_metadata::ID, false),
603 AccountMeta::new_readonly(sysvar::rent::id(), false),
604 ],
605 data: [
606 CoalInstruction::InitChromium.to_vec(),
607 InitChromiumArgs {
608 metadata_bump: metadata_pda.1,
609 mint_bump: mint_pda.1,
610 treasury_bump: treasury_pda.1,
611 }
612 .to_bytes()
613 .to_vec(),
614 ]
615 .concat(),
616 }
617}
618
619pub fn init_reprocess(signer: Pubkey) -> Instruction {
620 let (reprocessor, reprocessor_bump) = Pubkey::find_program_address(&[REPROCESSOR, signer.as_ref()], &crate::id());
621
622 Instruction {
623 program_id: crate::id(),
624 accounts: vec![
625 AccountMeta::new(signer, true),
626 AccountMeta::new(TREASURY_ADDRESS, false),
627 AccountMeta::new(reprocessor, false),
628 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
629 AccountMeta::new_readonly(system_program::id(), false),
630 ],
631 data: [
632 CoalInstruction::InitReprocess.to_vec(),
633 ReprocessArgs {
634 reprocessor_bump,
635 }
636 .to_bytes()
637 .to_vec(),
638 ]
639 .concat(),
640 }
641}
642
643pub fn reprocess(signer: Pubkey) -> Instruction {
644 let (proof, _proof_bump) = Pubkey::find_program_address(&[COAL_PROOF, signer.as_ref()], &crate::id());
645 let (reprocessor, reprocessor_bump) = Pubkey::find_program_address(&[REPROCESSOR, signer.as_ref()], &crate::id());
646 let (bus, _bus_bump) = Pubkey::find_program_address(&[COAL_BUS, &[0]], &crate::id());
647 let tokens = spl_associated_token_account::get_associated_token_address(&signer, &CHROMIUM_MINT_ADDRESS);
648
649 Instruction {
650 program_id: crate::id(),
651 accounts: vec![
652 AccountMeta::new(signer, true),
653 AccountMeta::new(reprocessor, false),
654 AccountMeta::new(proof, false),
655 AccountMeta::new(bus, false),
656 AccountMeta::new(CHROMIUM_MINT_ADDRESS, false),
657 AccountMeta::new(tokens, false),
658 AccountMeta::new_readonly(TREASURY_ADDRESS, false),
659 AccountMeta::new_readonly(spl_token::id(), false),
660 AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
661 ],
662 data: [
663 CoalInstruction::FinalizeReprocess.to_vec(),
664 ReprocessArgs {
665 reprocessor_bump,
666 }
667 .to_bytes()
668 .to_vec(),
669 ]
670 .concat(),
671 }
672}