smelter_api/
instruction.rs

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 SmelterInstruction {
19    // User
20    Claim = 0,
21    Close = 1,
22    Smelt = 2,
23    Open = 3,
24    Reset = 4,
25    Stake = 5,
26    Update = 6,
27
28    // Admin
29    Initialize = 100,
30}
31
32impl SmelterInstruction {
33    pub fn to_vec(&self) -> Vec<u8> {
34        vec![*self as u8]
35    }
36}
37
38#[repr(C)]
39#[derive(Clone, Copy, Debug, Pod, Zeroable)]
40pub struct InitializeArgs {
41    pub bus_0_bump: u8,
42    pub bus_1_bump: u8,
43    pub bus_2_bump: u8,
44    pub bus_3_bump: u8,
45    pub bus_4_bump: u8,
46    pub bus_5_bump: u8,
47    pub bus_6_bump: u8,
48    pub bus_7_bump: u8,
49    pub config_bump: u8,
50    pub metadata_bump: u8,
51    pub mint_bump: u8,
52    pub treasury_bump: u8,
53}
54
55#[repr(C)]
56#[derive(Clone, Copy, Debug, Pod, Zeroable)]
57pub struct OpenArgs {
58    pub bump: u8,
59}
60
61#[repr(C)]
62#[derive(Clone, Copy, Debug, Pod, Zeroable)]
63pub struct MineArgs {
64    pub digest: [u8; 16],
65    pub nonce: [u8; 8],
66}
67
68#[repr(C)]
69#[derive(Clone, Copy, Debug, Pod, Zeroable)]
70pub struct ClaimArgs {
71    pub amount: [u8; 8],
72}
73
74#[repr(C)]
75#[derive(Clone, Copy, Debug, Pod, Zeroable)]
76pub struct StakeArgs {
77    pub amount: [u8; 8],
78}
79
80#[repr(C)]
81#[derive(Clone, Copy, Debug, Pod, Zeroable)]
82pub struct UpgradeArgs {
83    pub amount: [u8; 8],
84}
85
86impl_to_bytes!(InitializeArgs);
87impl_to_bytes!(OpenArgs);
88impl_to_bytes!(MineArgs);
89impl_to_bytes!(ClaimArgs);
90impl_to_bytes!(StakeArgs);
91impl_to_bytes!(UpgradeArgs);
92
93impl_instruction_from_bytes!(InitializeArgs);
94impl_instruction_from_bytes!(OpenArgs);
95impl_instruction_from_bytes!(MineArgs);
96impl_instruction_from_bytes!(ClaimArgs);
97impl_instruction_from_bytes!(StakeArgs);
98impl_instruction_from_bytes!(UpgradeArgs);
99
100/// Builds an auth instruction.
101pub fn auth(proof: Pubkey) -> Instruction {
102    Instruction {
103        program_id: NOOP_PROGRAM_ID,
104        accounts: vec![],
105        data: proof.to_bytes().to_vec(),
106    }
107}
108
109/// Builds a claim instruction.
110pub fn claim(signer: Pubkey, beneficiary: Pubkey, amount: u64) -> Instruction {
111    let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
112    let treasury_tokens = spl_associated_token_account::get_associated_token_address(
113        &TREASURY_ADDRESS,
114        &MINT_ADDRESS,
115    );
116    Instruction {
117        program_id: crate::id(),
118        accounts: vec![
119            AccountMeta::new(signer, true),
120            AccountMeta::new(beneficiary, false),
121            AccountMeta::new(proof, false),
122            AccountMeta::new_readonly(TREASURY_ADDRESS, false),
123            AccountMeta::new(treasury_tokens, false),
124            AccountMeta::new_readonly(spl_token::id(), false),
125        ],
126        data: [
127            SmelterInstruction::Claim.to_vec(),
128            ClaimArgs {
129                amount: amount.to_le_bytes(),
130            }
131            .to_bytes()
132            .to_vec(),
133        ]
134        .concat(),
135    }
136}
137
138/// Builds a close instruction.
139pub fn close(signer: Pubkey) -> Instruction {
140    let proof_pda = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id());
141    Instruction {
142        program_id: crate::id(),
143        accounts: vec![
144            AccountMeta::new(signer, true),
145            AccountMeta::new(proof_pda.0, false),
146            AccountMeta::new_readonly(solana_program::system_program::id(), false),
147        ],
148        data: SmelterInstruction::Close.to_vec(),
149    }
150}
151
152/// Builds a mine instruction.
153pub fn smelt(
154    signer: Pubkey,
155    proof_authority: Pubkey,
156    bus: Pubkey,
157    solution: Solution,
158) -> Instruction {
159    let proof = Pubkey::find_program_address(&[PROOF, proof_authority.as_ref()], &crate::id()).0;
160    let ore_token_address = spl_associated_token_account::get_associated_token_address(
161        &signer,
162        &ORE_MINT_ADDRESS,
163    );
164    let coal_token_address = spl_associated_token_account::get_associated_token_address(
165        &signer,
166        &COAL_MINT_ADDRESS,
167    );
168    
169    Instruction {
170        program_id: crate::id(),
171        accounts: vec![
172            AccountMeta::new(signer, true),
173            AccountMeta::new(bus, false),
174            AccountMeta::new_readonly(CONFIG_ADDRESS, false),
175            AccountMeta::new(proof, false),
176            AccountMeta::new(COAL_MINT_ADDRESS, false),
177            AccountMeta::new(coal_token_address, false),
178            AccountMeta::new(ore_token_address, false),
179            AccountMeta::new(TREASURY_ORE_TOKENS_ADDRESS, false),
180            AccountMeta::new_readonly(spl_token::id(), false),
181            AccountMeta::new_readonly(sysvar::instructions::id(), false),
182            AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
183        ],
184        data: [
185            SmelterInstruction::Smelt.to_vec(),
186            MineArgs {
187                digest: solution.d,
188                nonce: solution.n,
189            }
190            .to_bytes()
191            .to_vec(),
192        ]
193        .concat(),
194    }
195}
196
197/// Builds an open instruction.
198pub fn open(signer: Pubkey, miner: Pubkey, payer: Pubkey) -> Instruction {
199    let proof_pda = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id());
200    Instruction {
201        program_id: crate::id(),
202        accounts: vec![
203            AccountMeta::new(signer, true),
204            AccountMeta::new_readonly(miner, false),
205            AccountMeta::new(payer, true),
206            AccountMeta::new(proof_pda.0, false),
207            AccountMeta::new_readonly(solana_program::system_program::id(), false),
208            AccountMeta::new_readonly(sysvar::slot_hashes::id(), false),
209        ],
210        data: [
211            SmelterInstruction::Open.to_vec(),
212            OpenArgs { bump: proof_pda.1 }.to_bytes().to_vec(),
213        ]
214        .concat(),
215    }
216}
217
218/// Builds a reset instruction.
219pub fn reset(signer: Pubkey) -> Instruction {
220    let treasury_tokens = spl_associated_token_account::get_associated_token_address(
221        &TREASURY_ADDRESS,
222        &MINT_ADDRESS,
223    );
224    Instruction {
225        program_id: crate::id(),
226        accounts: vec![
227            AccountMeta::new(signer, true),
228            AccountMeta::new(BUS_ADDRESSES[0], false),
229            AccountMeta::new(BUS_ADDRESSES[1], false),
230            AccountMeta::new(BUS_ADDRESSES[2], false),
231            AccountMeta::new(BUS_ADDRESSES[3], false),
232            AccountMeta::new(BUS_ADDRESSES[4], false),
233            AccountMeta::new(BUS_ADDRESSES[5], false),
234            AccountMeta::new(BUS_ADDRESSES[6], false),
235            AccountMeta::new(BUS_ADDRESSES[7], false),
236            AccountMeta::new(CONFIG_ADDRESS, false),
237            AccountMeta::new(MINT_ADDRESS, false),
238            AccountMeta::new(TREASURY_ADDRESS, false),
239            AccountMeta::new(treasury_tokens, false),
240            AccountMeta::new_readonly(spl_token::id(), false),
241        ],
242        data: SmelterInstruction::Reset.to_vec(),
243    }
244}
245
246/// Build a stake instruction.
247pub fn stake(signer: Pubkey, sender: Pubkey, amount: u64) -> Instruction {
248    let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
249    let treasury_tokens = spl_associated_token_account::get_associated_token_address(
250        &TREASURY_ADDRESS,
251        &MINT_ADDRESS,
252    );
253    Instruction {
254        program_id: crate::id(),
255        accounts: vec![
256            AccountMeta::new(signer, true),
257            AccountMeta::new(proof, false),
258            AccountMeta::new(sender, false),
259            AccountMeta::new(treasury_tokens, false),
260            AccountMeta::new_readonly(spl_token::id(), false),
261        ],
262        data: [
263            SmelterInstruction::Stake.to_vec(),
264            StakeArgs {
265                amount: amount.to_le_bytes(),
266            }
267            .to_bytes()
268            .to_vec(),
269        ]
270        .concat(),
271    }
272}
273
274// Build an update instruction.
275pub fn update(signer: Pubkey, miner: Pubkey) -> Instruction {
276    let proof = Pubkey::find_program_address(&[PROOF, signer.as_ref()], &crate::id()).0;
277    Instruction {
278        program_id: crate::id(),
279        accounts: vec![
280            AccountMeta::new(signer, true),
281            AccountMeta::new_readonly(miner, false),
282            AccountMeta::new(proof, false),
283        ],
284        data: SmelterInstruction::Update.to_vec(),
285    }
286}
287
288/// Builds an initialize instruction.
289pub fn initialize(signer: Pubkey) -> Instruction {
290    let bus_pdas = [
291        Pubkey::find_program_address(&[BUS, &[0]], &crate::id()),
292        Pubkey::find_program_address(&[BUS, &[1]], &crate::id()),
293        Pubkey::find_program_address(&[BUS, &[2]], &crate::id()),
294        Pubkey::find_program_address(&[BUS, &[3]], &crate::id()),
295        Pubkey::find_program_address(&[BUS, &[4]], &crate::id()),
296        Pubkey::find_program_address(&[BUS, &[5]], &crate::id()),
297        Pubkey::find_program_address(&[BUS, &[6]], &crate::id()),
298        Pubkey::find_program_address(&[BUS, &[7]], &crate::id()),
299    ];
300    let config_pda = Pubkey::find_program_address(&[CONFIG], &crate::id());
301    let mint_pda = Pubkey::find_program_address(&[MINT, MINT_NOISE.as_slice()], &crate::id());
302
303    let treasury_pda = Pubkey::find_program_address(&[TREASURY], &crate::id());
304    let treasury_tokens =
305        spl_associated_token_account::get_associated_token_address(&treasury_pda.0, &mint_pda.0);
306    let ore_treasury_tokens =
307        spl_associated_token_account::get_associated_token_address(&treasury_pda.0, &ORE_MINT_ADDRESS);
308    let metadata_pda = Pubkey::find_program_address(
309        &[
310            METADATA,
311            mpl_token_metadata::ID.as_ref(),
312            mint_pda.0.as_ref(),
313        ],
314        &mpl_token_metadata::ID,
315    );
316    Instruction {
317        program_id: crate::id(),
318        accounts: vec![
319            AccountMeta::new(signer, true),
320            AccountMeta::new(bus_pdas[0].0, false),
321            AccountMeta::new(bus_pdas[1].0, false),
322            AccountMeta::new(bus_pdas[2].0, false),
323            AccountMeta::new(bus_pdas[3].0, false),
324            AccountMeta::new(bus_pdas[4].0, false),
325            AccountMeta::new(bus_pdas[5].0, false),
326            AccountMeta::new(bus_pdas[6].0, false),
327            AccountMeta::new(bus_pdas[7].0, false),
328            AccountMeta::new(config_pda.0, false),
329            AccountMeta::new(metadata_pda.0, false),
330            AccountMeta::new(mint_pda.0, false),
331            AccountMeta::new(ORE_MINT_ADDRESS, false),
332            AccountMeta::new(treasury_pda.0, false),
333            AccountMeta::new(treasury_tokens, false),
334            AccountMeta::new(ore_treasury_tokens, false),
335            AccountMeta::new_readonly(system_program::id(), false),
336            AccountMeta::new_readonly(spl_token::id(), false),
337            AccountMeta::new_readonly(spl_associated_token_account::id(), false),
338            AccountMeta::new_readonly(mpl_token_metadata::ID, false),
339            AccountMeta::new_readonly(sysvar::rent::id(), false),
340        ],
341        data: [
342            SmelterInstruction::Initialize.to_vec(),
343            InitializeArgs {
344                bus_0_bump: bus_pdas[0].1,
345                bus_1_bump: bus_pdas[1].1,
346                bus_2_bump: bus_pdas[2].1,
347                bus_3_bump: bus_pdas[3].1,
348                bus_4_bump: bus_pdas[4].1,
349                bus_5_bump: bus_pdas[5].1,
350                bus_6_bump: bus_pdas[6].1,
351                bus_7_bump: bus_pdas[7].1,
352                config_bump: config_pda.1,
353                metadata_bump: metadata_pda.1,
354                mint_bump: mint_pda.1,
355                treasury_bump: treasury_pda.1,
356            }
357            .to_bytes()
358            .to_vec(),
359        ]
360        .concat(),
361    }
362}