spl_record/
instruction.rs

1//! Program instructions
2
3use crate::id;
4use borsh::{BorshDeserialize, BorshSerialize};
5use solana_program::{
6    instruction::{AccountMeta, Instruction},
7    pubkey::Pubkey,
8};
9
10/// Instructions supported by the program
11#[derive(Clone, Debug, BorshSerialize, BorshDeserialize, PartialEq)]
12pub enum RecordInstruction {
13    /// Create a new record
14    ///
15    /// Accounts expected by this instruction:
16    ///
17    /// 0. `[writable]` Record account, must be uninitialized
18    /// 1. `[]` Record authority
19    Initialize,
20
21    /// Write to the provided record account
22    ///
23    /// Accounts expected by this instruction:
24    ///
25    /// 0. `[writable]` Record account, must be previously initialized
26    /// 1. `[signer]` Current record authority
27    Write {
28        /// Offset to start writing record, expressed as `u64`.
29        offset: u64,
30        /// Data to replace the existing record data
31        data: Vec<u8>,
32    },
33
34    /// Update the authority of the provided record account
35    ///
36    /// Accounts expected by this instruction:
37    ///
38    /// 0. `[writable]` Record account, must be previously initialized
39    /// 1. `[signer]` Current record authority
40    /// 2. `[]` New record authority
41    SetAuthority,
42
43    /// Close the provided record account, draining lamports to recipient account
44    ///
45    /// Accounts expected by this instruction:
46    ///
47    /// 0. `[writable]` Record account, must be previously initialized
48    /// 1. `[signer]` Record authority
49    /// 2. `[]` Receiver of account lamports
50    CloseAccount,
51}
52
53/// Create a `RecordInstruction::Initialize` instruction
54pub fn initialize(record_account: &Pubkey, authority: &Pubkey) -> Instruction {
55    Instruction::new_with_borsh(
56        id(),
57        &RecordInstruction::Initialize,
58        vec![
59            AccountMeta::new(*record_account, false),
60            AccountMeta::new_readonly(*authority, false),
61        ],
62    )
63}
64
65/// Create a `RecordInstruction::Write` instruction
66pub fn write(record_account: &Pubkey, signer: &Pubkey, offset: u64, data: Vec<u8>) -> Instruction {
67    Instruction::new_with_borsh(
68        id(),
69        &RecordInstruction::Write { offset, data },
70        vec![
71            AccountMeta::new(*record_account, false),
72            AccountMeta::new_readonly(*signer, true),
73        ],
74    )
75}
76
77/// Create a `RecordInstruction::SetAuthority` instruction
78pub fn set_authority(
79    record_account: &Pubkey,
80    signer: &Pubkey,
81    new_authority: &Pubkey,
82) -> Instruction {
83    Instruction::new_with_borsh(
84        id(),
85        &RecordInstruction::SetAuthority,
86        vec![
87            AccountMeta::new(*record_account, false),
88            AccountMeta::new_readonly(*signer, true),
89            AccountMeta::new_readonly(*new_authority, false),
90        ],
91    )
92}
93
94/// Create a `RecordInstruction::CloseAccount` instruction
95pub fn close_account(record_account: &Pubkey, signer: &Pubkey, receiver: &Pubkey) -> Instruction {
96    Instruction::new_with_borsh(
97        id(),
98        &RecordInstruction::CloseAccount,
99        vec![
100            AccountMeta::new(*record_account, false),
101            AccountMeta::new_readonly(*signer, true),
102            AccountMeta::new(*receiver, false),
103        ],
104    )
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use crate::state::tests::TEST_DATA;
111    use solana_program::program_error::ProgramError;
112
113    #[test]
114    fn serialize_initialize() {
115        let instruction = RecordInstruction::Initialize;
116        let expected = vec![0];
117        assert_eq!(instruction.try_to_vec().unwrap(), expected);
118        assert_eq!(
119            RecordInstruction::try_from_slice(&expected).unwrap(),
120            instruction
121        );
122    }
123
124    #[test]
125    fn serialize_write() {
126        let data = TEST_DATA.try_to_vec().unwrap();
127        let offset = 0u64;
128        let instruction = RecordInstruction::Write {
129            offset: 0,
130            data: data.clone(),
131        };
132        let mut expected = vec![1];
133        expected.extend_from_slice(&offset.to_le_bytes());
134        expected.append(&mut data.try_to_vec().unwrap());
135        assert_eq!(instruction.try_to_vec().unwrap(), expected);
136        assert_eq!(
137            RecordInstruction::try_from_slice(&expected).unwrap(),
138            instruction
139        );
140    }
141
142    #[test]
143    fn serialize_set_authority() {
144        let instruction = RecordInstruction::SetAuthority;
145        let expected = vec![2];
146        assert_eq!(instruction.try_to_vec().unwrap(), expected);
147        assert_eq!(
148            RecordInstruction::try_from_slice(&expected).unwrap(),
149            instruction
150        );
151    }
152
153    #[test]
154    fn serialize_close_account() {
155        let instruction = RecordInstruction::CloseAccount;
156        let expected = vec![3];
157        assert_eq!(instruction.try_to_vec().unwrap(), expected);
158        assert_eq!(
159            RecordInstruction::try_from_slice(&expected).unwrap(),
160            instruction
161        );
162    }
163
164    #[test]
165    fn deserialize_invalid_instruction() {
166        let mut expected = vec![12];
167        expected.append(&mut TEST_DATA.try_to_vec().unwrap());
168        let err: ProgramError = RecordInstruction::try_from_slice(&expected)
169            .unwrap_err()
170            .into();
171        assert!(matches!(err, ProgramError::BorshIoError(_)));
172    }
173}