1use crate::{state::AcceptanceCriteria, *};
4use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
5use gemachain_program::{
6 instruction::{AccountMeta, Instruction},
7 msg,
8 program_error::ProgramError,
9 program_pack::{Pack, Sealed},
10 pubkey::Pubkey,
11 sysvar,
12};
13
14#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, BorshSchema, PartialEq)]
16pub enum FeatureProposalInstruction {
17 Propose {
52 #[allow(dead_code)] tokens_to_mint: u64,
55
56 #[allow(dead_code)] acceptance_criteria: AcceptanceCriteria,
59 },
60
61 Tally,
75}
76
77impl Sealed for FeatureProposalInstruction {}
78impl Pack for FeatureProposalInstruction {
79 const LEN: usize = 25; fn pack_into_slice(&self, dst: &mut [u8]) {
82 let data = self.pack_into_vec();
83 dst[..data.len()].copy_from_slice(&data);
84 }
85
86 fn unpack_from_slice(src: &[u8]) -> Result<Self, ProgramError> {
87 let mut mut_src: &[u8] = src;
88 Self::deserialize(&mut mut_src).map_err(|err| {
89 msg!(
90 "Error: failed to deserialize feature proposal instruction: {}",
91 err
92 );
93 ProgramError::InvalidInstructionData
94 })
95 }
96}
97
98impl FeatureProposalInstruction {
99 fn pack_into_vec(&self) -> Vec<u8> {
100 self.try_to_vec().expect("try_to_vec")
101 }
102}
103
104pub fn propose(
106 funding_address: &Pubkey,
107 feature_proposal_address: &Pubkey,
108 tokens_to_mint: u64,
109 acceptance_criteria: AcceptanceCriteria,
110) -> Instruction {
111 let mint_address = get_mint_address(feature_proposal_address);
112 let distributor_token_address = get_distributor_token_address(feature_proposal_address);
113 let acceptance_token_address = get_acceptance_token_address(feature_proposal_address);
114 let feature_id_address = get_feature_id_address(feature_proposal_address);
115
116 Instruction {
117 program_id: id(),
118 accounts: vec![
119 AccountMeta::new(*funding_address, true),
120 AccountMeta::new(*feature_proposal_address, true),
121 AccountMeta::new(mint_address, false),
122 AccountMeta::new(distributor_token_address, false),
123 AccountMeta::new(acceptance_token_address, false),
124 AccountMeta::new(feature_id_address, false),
125 AccountMeta::new_readonly(gemachain_program::system_program::id(), false),
126 AccountMeta::new_readonly(gpl_token::id(), false),
127 AccountMeta::new_readonly(sysvar::rent::id(), false),
128 ],
129 data: FeatureProposalInstruction::Propose {
130 tokens_to_mint,
131 acceptance_criteria,
132 }
133 .pack_into_vec(),
134 }
135}
136
137pub fn tally(feature_proposal_address: &Pubkey) -> Instruction {
139 let acceptance_token_address = get_acceptance_token_address(feature_proposal_address);
140 let feature_id_address = get_feature_id_address(feature_proposal_address);
141
142 Instruction {
143 program_id: id(),
144 accounts: vec![
145 AccountMeta::new(*feature_proposal_address, false),
146 AccountMeta::new_readonly(acceptance_token_address, false),
147 AccountMeta::new(feature_id_address, false),
148 AccountMeta::new_readonly(gemachain_program::system_program::id(), false),
149 AccountMeta::new_readonly(sysvar::clock::id(), false),
150 ],
151 data: FeatureProposalInstruction::Tally.pack_into_vec(),
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn test_get_packed_len() {
161 assert_eq!(
162 FeatureProposalInstruction::get_packed_len(),
163 gemachain_program::borsh::get_packed_len::<FeatureProposalInstruction>()
164 )
165 }
166
167 #[test]
168 fn test_serialize_bytes() {
169 assert_eq!(
170 FeatureProposalInstruction::Tally.try_to_vec().unwrap(),
171 vec![1]
172 );
173
174 assert_eq!(
175 FeatureProposalInstruction::Propose {
176 tokens_to_mint: 42,
177 acceptance_criteria: AcceptanceCriteria {
178 tokens_required: 0xdeadbeefdeadbeef,
179 deadline: -1,
180 }
181 }
182 .try_to_vec()
183 .unwrap(),
184 vec![
185 0, 42, 0, 0, 0, 0, 0, 0, 0, 239, 190, 173, 222, 239, 190, 173, 222, 255, 255, 255,
186 255, 255, 255, 255, 255
187 ]
188 );
189 }
190
191 #[test]
192 fn test_serialize_large_slice() {
193 let mut dst = vec![0xff; 4];
194 FeatureProposalInstruction::Tally.pack_into_slice(&mut dst);
195
196 assert_eq!(dst, vec![1, 0xff, 0xff, 0xff]);
198 }
199
200 #[test]
201 fn state_deserialize_invalid() {
202 assert_eq!(
203 FeatureProposalInstruction::unpack_from_slice(&[1]),
204 Ok(FeatureProposalInstruction::Tally),
205 );
206
207 assert_eq!(
209 FeatureProposalInstruction::unpack_from_slice(&[1, 0xff, 0xff, 0xff]),
210 Ok(FeatureProposalInstruction::Tally),
211 );
212
213 assert_eq!(
214 FeatureProposalInstruction::unpack_from_slice(&[2]),
215 Err(ProgramError::InvalidInstructionData),
216 );
217 }
218}