marsh_api/
instruction.rs

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