1use crate::{instruction::*, state::*, *};
4use gemachain_program::{
5 account_info::{next_account_info, AccountInfo},
6 clock::Clock,
7 entrypoint::ProgramResult,
8 feature::{self, Feature},
9 msg,
10 program::{invoke, invoke_signed},
11 program_error::ProgramError,
12 pubkey::Pubkey,
13 rent::Rent,
14 system_instruction,
15 sysvar::Sysvar,
16};
17
18pub fn process_instruction(
20 program_id: &Pubkey,
21 accounts: &[AccountInfo],
22 input: &[u8],
23) -> ProgramResult {
24 let instruction = FeatureProposalInstruction::unpack_from_slice(input)?;
25 let account_info_iter = &mut accounts.iter();
26
27 match instruction {
28 FeatureProposalInstruction::Propose {
29 tokens_to_mint,
30 acceptance_criteria,
31 } => {
32 msg!("FeatureProposalInstruction::Propose");
33
34 let funder_info = next_account_info(account_info_iter)?;
35 let feature_proposal_info = next_account_info(account_info_iter)?;
36 let mint_info = next_account_info(account_info_iter)?;
37 let distributor_token_info = next_account_info(account_info_iter)?;
38 let acceptance_token_info = next_account_info(account_info_iter)?;
39 let feature_id_info = next_account_info(account_info_iter)?;
40 let system_program_info = next_account_info(account_info_iter)?;
41 let gpl_token_program_info = next_account_info(account_info_iter)?;
42 let rent_sysvar_info = next_account_info(account_info_iter)?;
43 let rent = &Rent::from_account_info(rent_sysvar_info)?;
44
45 let (mint_address, mint_bump_seed) =
46 get_mint_address_with_seed(feature_proposal_info.key);
47 if mint_address != *mint_info.key {
48 msg!("Error: mint address derivation mismatch");
49 return Err(ProgramError::InvalidArgument);
50 }
51
52 let (distributor_token_address, distributor_token_bump_seed) =
53 get_distributor_token_address_with_seed(feature_proposal_info.key);
54 if distributor_token_address != *distributor_token_info.key {
55 msg!("Error: distributor token address derivation mismatch");
56 return Err(ProgramError::InvalidArgument);
57 }
58
59 let (acceptance_token_address, acceptance_token_bump_seed) =
60 get_acceptance_token_address_with_seed(feature_proposal_info.key);
61 if acceptance_token_address != *acceptance_token_info.key {
62 msg!("Error: acceptance token address derivation mismatch");
63 return Err(ProgramError::InvalidArgument);
64 }
65
66 let (feature_id_address, feature_id_bump_seed) =
67 get_feature_id_address_with_seed(feature_proposal_info.key);
68 if feature_id_address != *feature_id_info.key {
69 msg!("Error: feature-id address derivation mismatch");
70 return Err(ProgramError::InvalidArgument);
71 }
72
73 let mint_signer_seeds: &[&[_]] = &[
74 &feature_proposal_info.key.to_bytes(),
75 br"mint",
76 &[mint_bump_seed],
77 ];
78
79 let distributor_token_signer_seeds: &[&[_]] = &[
80 &feature_proposal_info.key.to_bytes(),
81 br"distributor",
82 &[distributor_token_bump_seed],
83 ];
84
85 let acceptance_token_signer_seeds: &[&[_]] = &[
86 &feature_proposal_info.key.to_bytes(),
87 br"acceptance",
88 &[acceptance_token_bump_seed],
89 ];
90
91 let feature_id_signer_seeds: &[&[_]] = &[
92 &feature_proposal_info.key.to_bytes(),
93 br"feature-id",
94 &[feature_id_bump_seed],
95 ];
96
97 msg!("Creating feature proposal account");
98 invoke(
99 &system_instruction::create_account(
100 funder_info.key,
101 feature_proposal_info.key,
102 1.max(rent.minimum_balance(FeatureProposal::get_packed_len())),
103 FeatureProposal::get_packed_len() as u64,
104 program_id,
105 ),
106 &[
107 funder_info.clone(),
108 feature_proposal_info.clone(),
109 system_program_info.clone(),
110 ],
111 )?;
112 FeatureProposal::Pending(acceptance_criteria)
113 .pack_into_slice(&mut feature_proposal_info.data.borrow_mut());
114
115 msg!("Creating mint");
116 invoke_signed(
117 &system_instruction::create_account(
118 funder_info.key,
119 mint_info.key,
120 1.max(rent.minimum_balance(gpl_token::state::Mint::get_packed_len())),
121 gpl_token::state::Mint::get_packed_len() as u64,
122 &gpl_token::id(),
123 ),
124 &[
125 funder_info.clone(),
126 mint_info.clone(),
127 system_program_info.clone(),
128 ],
129 &[mint_signer_seeds],
130 )?;
131
132 msg!("Initializing mint");
133 invoke(
134 &gpl_token::instruction::initialize_mint(
135 &gpl_token::id(),
136 mint_info.key,
137 mint_info.key,
138 None,
139 gpl_token::native_mint::DECIMALS,
140 )?,
141 &[
142 mint_info.clone(),
143 gpl_token_program_info.clone(),
144 rent_sysvar_info.clone(),
145 ],
146 )?;
147
148 msg!("Creating distributor token account");
149 invoke_signed(
150 &system_instruction::create_account(
151 funder_info.key,
152 distributor_token_info.key,
153 1.max(rent.minimum_balance(gpl_token::state::Account::get_packed_len())),
154 gpl_token::state::Account::get_packed_len() as u64,
155 &gpl_token::id(),
156 ),
157 &[
158 funder_info.clone(),
159 distributor_token_info.clone(),
160 system_program_info.clone(),
161 ],
162 &[distributor_token_signer_seeds],
163 )?;
164
165 msg!("Initializing distributor token account");
166 invoke(
167 &gpl_token::instruction::initialize_account(
168 &gpl_token::id(),
169 distributor_token_info.key,
170 mint_info.key,
171 feature_proposal_info.key,
172 )?,
173 &[
174 distributor_token_info.clone(),
175 gpl_token_program_info.clone(),
176 rent_sysvar_info.clone(),
177 feature_proposal_info.clone(),
178 mint_info.clone(),
179 ],
180 )?;
181
182 msg!("Creating acceptance token account");
183 invoke_signed(
184 &system_instruction::create_account(
185 funder_info.key,
186 acceptance_token_info.key,
187 1.max(rent.minimum_balance(gpl_token::state::Account::get_packed_len())),
188 gpl_token::state::Account::get_packed_len() as u64,
189 &gpl_token::id(),
190 ),
191 &[
192 funder_info.clone(),
193 acceptance_token_info.clone(),
194 system_program_info.clone(),
195 ],
196 &[acceptance_token_signer_seeds],
197 )?;
198
199 msg!("Initializing acceptance token account");
200 invoke(
201 &gpl_token::instruction::initialize_account(
202 &gpl_token::id(),
203 acceptance_token_info.key,
204 mint_info.key,
205 feature_proposal_info.key,
206 )?,
207 &[
208 acceptance_token_info.clone(),
209 gpl_token_program_info.clone(),
210 rent_sysvar_info.clone(),
211 feature_proposal_info.clone(),
212 mint_info.clone(),
213 ],
214 )?;
215 invoke(
216 &gpl_token::instruction::set_authority(
217 &gpl_token::id(),
218 acceptance_token_info.key,
219 Some(feature_proposal_info.key),
220 gpl_token::instruction::AuthorityType::CloseAccount,
221 feature_proposal_info.key,
222 &[],
223 )?,
224 &[
225 gpl_token_program_info.clone(),
226 acceptance_token_info.clone(),
227 feature_proposal_info.clone(),
228 ],
229 )?;
230 invoke(
231 &gpl_token::instruction::set_authority(
232 &gpl_token::id(),
233 acceptance_token_info.key,
234 Some(program_id),
235 gpl_token::instruction::AuthorityType::AccountOwner,
236 feature_proposal_info.key,
237 &[],
238 )?,
239 &[
240 gpl_token_program_info.clone(),
241 acceptance_token_info.clone(),
242 feature_proposal_info.clone(),
243 ],
244 )?;
245
246 msg!("Minting {} tokens", tokens_to_mint);
249 invoke_signed(
250 &gpl_token::instruction::mint_to(
251 &gpl_token::id(),
252 mint_info.key,
253 distributor_token_info.key,
254 mint_info.key,
255 &[],
256 tokens_to_mint,
257 )?,
258 &[
259 mint_info.clone(),
260 distributor_token_info.clone(),
261 gpl_token_program_info.clone(),
262 ],
263 &[mint_signer_seeds],
264 )?;
265
266 msg!("Funding feature id account");
269 invoke(
270 &system_instruction::transfer(
271 funder_info.key,
272 feature_id_info.key,
273 1.max(rent.minimum_balance(Feature::size_of())),
274 ),
275 &[
276 funder_info.clone(),
277 feature_id_info.clone(),
278 system_program_info.clone(),
279 ],
280 )?;
281
282 msg!("Allocating feature id account");
283 invoke_signed(
284 &system_instruction::allocate(feature_id_info.key, Feature::size_of() as u64),
285 &[feature_id_info.clone(), system_program_info.clone()],
286 &[feature_id_signer_seeds],
287 )?;
288 }
289
290 FeatureProposalInstruction::Tally => {
291 msg!("FeatureProposalInstruction::Tally");
292
293 let feature_proposal_info = next_account_info(account_info_iter)?;
294 let feature_proposal_state =
295 FeatureProposal::unpack_from_slice(&feature_proposal_info.data.borrow())?;
296
297 match feature_proposal_state {
298 FeatureProposal::Pending(acceptance_criteria) => {
299 let acceptance_token_info = next_account_info(account_info_iter)?;
300 let feature_id_info = next_account_info(account_info_iter)?;
301 let system_program_info = next_account_info(account_info_iter)?;
302 let clock_sysvar_info = next_account_info(account_info_iter)?;
303 let clock = &Clock::from_account_info(clock_sysvar_info)?;
304
305 let acceptance_token_address =
308 get_acceptance_token_address(feature_proposal_info.key);
309 if acceptance_token_address != *acceptance_token_info.key {
310 msg!("Error: acceptance token address derivation mismatch");
311 return Err(ProgramError::InvalidArgument);
312 }
313
314 let (feature_id_address, feature_id_bump_seed) =
315 get_feature_id_address_with_seed(feature_proposal_info.key);
316 if feature_id_address != *feature_id_info.key {
317 msg!("Error: feature-id address derivation mismatch");
318 return Err(ProgramError::InvalidArgument);
319 }
320
321 let feature_id_signer_seeds: &[&[_]] = &[
322 &feature_proposal_info.key.to_bytes(),
323 br"feature-id",
324 &[feature_id_bump_seed],
325 ];
326
327 if clock.unix_timestamp >= acceptance_criteria.deadline {
328 msg!("Feature proposal expired");
329 FeatureProposal::Expired
330 .pack_into_slice(&mut feature_proposal_info.data.borrow_mut());
331 return Ok(());
332 }
333
334 msg!("Unpacking acceptance token account");
335 let acceptance_token =
336 gpl_token::state::Account::unpack(&acceptance_token_info.data.borrow())?;
337
338 msg!(
339 "Feature proposal has received {} tokens, and {} tokens required for acceptance",
340 acceptance_token.amount, acceptance_criteria.tokens_required
341 );
342 if acceptance_token.amount < acceptance_criteria.tokens_required {
343 msg!("Activation threshold has not been reached");
344 return Ok(());
345 }
346
347 msg!("Assigning feature id account");
348 invoke_signed(
349 &system_instruction::assign(feature_id_info.key, &feature::id()),
350 &[feature_id_info.clone(), system_program_info.clone()],
351 &[feature_id_signer_seeds],
352 )?;
353
354 msg!("Feature proposal accepted");
355 FeatureProposal::Accepted {
356 tokens_upon_acceptance: acceptance_token.amount,
357 }
358 .pack_into_slice(&mut feature_proposal_info.data.borrow_mut());
359 }
360 _ => {
361 msg!("Error: feature proposal account not in the pending state");
362 return Err(ProgramError::InvalidAccountData);
363 }
364 }
365 }
366 }
367
368 Ok(())
369}