marsh_api/
loaders.rs

1use marsh_utils::*;
2use solana_program::{account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey};
3
4use crate::{
5    consts::*,
6    state::{Bus, Config, Proof, Treasury},
7};
8
9/// Errors if:
10/// - Owner is not Marsh program.
11/// - Address does not match the expected bus address.
12/// - Data is empty.
13/// - Data cannot deserialize into a bus account.
14/// - Bus ID does not match the expected ID.
15/// - Expected to be writable, but is not.
16pub fn load_bus(info: &AccountInfo<'_>, id: u64, is_writable: bool) -> Result<(), ProgramError> {
17    if info.owner.ne(&crate::id()) {
18        return Err(ProgramError::InvalidAccountOwner);
19    }
20
21    if info.key.ne(&BUS_ADDRESSES[id as usize]) {
22        return Err(ProgramError::InvalidSeeds);
23    }
24
25    if info.data_is_empty() {
26        return Err(ProgramError::UninitializedAccount);
27    }
28
29    let bus_data = info.data.borrow();
30    let bus = Bus::try_from_bytes(&bus_data)?;
31
32    if bus.id.ne(&id) {
33        return Err(ProgramError::InvalidAccountData);
34    }
35
36    if is_writable && !info.is_writable {
37        return Err(ProgramError::InvalidAccountData);
38    }
39
40    Ok(())
41}
42
43/// Errors if:
44/// - Owner is not Marsh program.
45/// - Data is empty.
46/// - Data cannot deserialize into a bus account.
47/// - Bus ID is not in the expected range.
48/// - Address is not in set of valid bus address.
49/// - Expected to be writable, but is not.
50pub fn load_any_bus(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> {
51    if info.owner.ne(&crate::id()) {
52        return Err(ProgramError::InvalidAccountOwner);
53    }
54
55    if info.data_is_empty() {
56        return Err(ProgramError::UninitializedAccount);
57    }
58
59    if info.data.borrow()[0].ne(&(Bus::discriminator() as u8)) {
60        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
61    }
62
63    if !BUS_ADDRESSES.contains(info.key) {
64        return Err(ProgramError::InvalidSeeds);
65    }
66
67    if is_writable && !info.is_writable {
68        return Err(ProgramError::InvalidAccountData);
69    }
70
71    Ok(())
72}
73
74/// Errors if:
75/// - Owner is not Marsh program.
76/// - Address does not match the expected address.
77/// - Data is empty.
78/// - Data cannot deserialize into a config account.
79/// - Expected to be writable, but is not.
80pub fn load_config(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> {
81    if info.owner.ne(&crate::id()) {
82        return Err(ProgramError::InvalidAccountOwner);
83    }
84
85    if info.key.ne(&CONFIG_ADDRESS) {
86        return Err(ProgramError::InvalidSeeds);
87    }
88
89    if info.data_is_empty() {
90        return Err(ProgramError::UninitializedAccount);
91    }
92
93    if info.data.borrow()[0].ne(&(Config::discriminator() as u8)) {
94        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
95    }
96
97    if is_writable && !info.is_writable {
98        return Err(ProgramError::InvalidAccountData);
99    }
100
101    Ok(())
102}
103
104/// Errors if:
105/// - Owner is not Marsh program.
106/// - Data is empty.
107/// - Data cannot deserialize into a proof account.
108/// - Proof authority does not match the expected address.
109/// - Expected to be writable, but is not.
110pub fn load_proof(
111    info: &AccountInfo<'_>,
112    authority: &Pubkey,
113    is_writable: bool,
114) -> Result<(), ProgramError> {
115    if info.owner.ne(&crate::id()) {
116        return Err(ProgramError::InvalidAccountOwner);
117    }
118
119    if info.data_is_empty() {
120        return Err(ProgramError::UninitializedAccount);
121    }
122
123    let proof_data = info.data.borrow();
124    let proof = Proof::try_from_bytes(&proof_data)?;
125
126    if proof.authority.ne(&authority) {
127        return Err(ProgramError::InvalidAccountData);
128    }
129
130    if is_writable && !info.is_writable {
131        return Err(ProgramError::InvalidAccountData);
132    }
133
134    Ok(())
135}
136
137/// Errors if:
138/// - Owner is not Marsh program.
139/// - Data is empty.
140/// - Data cannot deserialize into a proof account.
141/// - Proof miner does not match the expected address.
142/// - Expected to be writable, but is not.
143pub fn load_proof_with_miner(
144    info: &AccountInfo<'_>,
145    miner: &Pubkey,
146    is_writable: bool,
147) -> Result<(), ProgramError> {
148    if info.owner.ne(&crate::id()) {
149        return Err(ProgramError::InvalidAccountOwner);
150    }
151
152    if info.data_is_empty() {
153        return Err(ProgramError::UninitializedAccount);
154    }
155
156    let proof_data = info.data.borrow();
157    let proof = Proof::try_from_bytes(&proof_data)?;
158
159    if proof.miner.ne(&miner) {
160        return Err(ProgramError::InvalidAccountData);
161    }
162
163    if is_writable && !info.is_writable {
164        return Err(ProgramError::InvalidAccountData);
165    }
166
167    Ok(())
168}
169
170/// Errors if:
171/// - Owner is not Marsh program.
172/// - Data is empty.
173/// - Data cannot deserialize into a proof account.
174/// - Expected to be writable, but is not.
175pub fn load_any_proof(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> {
176    if info.owner.ne(&crate::id()) {
177        return Err(ProgramError::InvalidAccountOwner);
178    }
179
180    if info.data_is_empty() {
181        return Err(ProgramError::UninitializedAccount);
182    }
183
184    if info.data.borrow()[0].ne(&(Proof::discriminator() as u8)) {
185        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
186    }
187
188    if is_writable && !info.is_writable {
189        return Err(ProgramError::InvalidAccountData);
190    }
191
192    Ok(())
193}
194
195/// Errors if:
196/// - Owner is not Marsh program.
197/// - Address does not match the expected address.
198/// - Data is empty.
199/// - Data cannot deserialize into a treasury account.
200/// - Expected to be writable, but is not.
201pub fn load_treasury(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> {
202    if info.owner.ne(&crate::id()) {
203        return Err(ProgramError::InvalidAccountOwner);
204    }
205
206    if info.key.ne(&TREASURY_ADDRESS) {
207        return Err(ProgramError::InvalidSeeds);
208    }
209
210    if info.data_is_empty() {
211        return Err(ProgramError::UninitializedAccount);
212    }
213
214    if info.data.borrow()[0].ne(&(Treasury::discriminator() as u8)) {
215        return Err(solana_program::program_error::ProgramError::InvalidAccountData);
216    }
217
218    if is_writable && !info.is_writable {
219        return Err(ProgramError::InvalidAccountData);
220    }
221
222    Ok(())
223}
224
225/// Errors if:
226/// - Address does not match the expected treasury tokens address.
227/// - Cannot load as a token account
228pub fn load_treasury_tokens(info: &AccountInfo<'_>, is_writable: bool) -> Result<(), ProgramError> {
229    if info.key.ne(&TREASURY_TOKENS_ADDRESS) {
230        return Err(ProgramError::InvalidSeeds);
231    }
232
233    load_token_account(info, Some(&TREASURY_ADDRESS), &MINT_ADDRESS, is_writable)
234}