squads_multisig_program/instructions/
transaction_accounts_close.rs1use anchor_lang::prelude::*;
13
14use crate::errors::*;
15use crate::state::*;
16
17#[derive(Accounts)]
18pub struct ConfigTransactionAccountsClose<'info> {
19 #[account(
20 seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
21 bump = multisig.bump,
22 constraint = multisig.rent_collector.is_some() @ MultisigError::RentReclamationDisabled,
23 )]
24 pub multisig: Account<'info, Multisig>,
25
26 #[account(
27 mut,
28 has_one = multisig @ MultisigError::ProposalForAnotherMultisig,
29 close = rent_collector
30 )]
31 pub proposal: Account<'info, Proposal>,
32
33 #[account(
35 mut,
36 has_one = multisig @ MultisigError::TransactionForAnotherMultisig,
37 constraint = transaction.index == proposal.transaction_index @ MultisigError::TransactionNotMatchingProposal,
38 close = rent_collector
39 )]
40 pub transaction: Account<'info, ConfigTransaction>,
41
42 #[account(
45 mut,
46 address = multisig.rent_collector.unwrap().key() @ MultisigError::InvalidRentCollector,
47 )]
48 pub rent_collector: AccountInfo<'info>,
49
50 pub system_program: Program<'info, System>,
51}
52
53impl ConfigTransactionAccountsClose<'_> {
54 fn validate(&self) -> Result<()> {
55 let Self {
56 multisig, proposal, ..
57 } = self;
58
59 let is_stale = proposal.transaction_index <= multisig.stale_transaction_index;
60
61 #[allow(deprecated)]
63 let can_close = match proposal.status {
64 ProposalStatus::Draft { .. } => is_stale,
67 ProposalStatus::Active { .. } => is_stale,
70 ProposalStatus::Approved { .. } => is_stale,
73 ProposalStatus::Rejected { .. } => true,
75 ProposalStatus::Executed { .. } => true,
77 ProposalStatus::Cancelled { .. } => true,
79 ProposalStatus::Executing => false,
81 };
82
83 require!(can_close, MultisigError::InvalidProposalStatus);
84
85 Ok(())
86 }
87
88 #[access_control(_ctx.accounts.validate())]
93 pub fn config_transaction_accounts_close(_ctx: Context<Self>) -> Result<()> {
94 Ok(())
96 }
97}
98
99#[derive(Accounts)]
100pub struct VaultTransactionAccountsClose<'info> {
101 #[account(
102 seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
103 bump = multisig.bump,
104 constraint = multisig.rent_collector.is_some() @ MultisigError::RentReclamationDisabled,
105 )]
106 pub multisig: Account<'info, Multisig>,
107
108 #[account(
109 mut,
110 has_one = multisig @ MultisigError::ProposalForAnotherMultisig,
111 close = rent_collector
112 )]
113 pub proposal: Account<'info, Proposal>,
114
115 #[account(
117 mut,
118 has_one = multisig @ MultisigError::TransactionForAnotherMultisig,
119 constraint = transaction.index == proposal.transaction_index @ MultisigError::TransactionNotMatchingProposal,
120 close = rent_collector
121 )]
122 pub transaction: Account<'info, VaultTransaction>,
123
124 #[account(
127 mut,
128 address = multisig.rent_collector.unwrap().key() @ MultisigError::InvalidRentCollector,
129 )]
130 pub rent_collector: AccountInfo<'info>,
131
132 pub system_program: Program<'info, System>,
133}
134
135impl VaultTransactionAccountsClose<'_> {
136 fn validate(&self) -> Result<()> {
137 let Self {
138 multisig, proposal, ..
139 } = self;
140
141 let is_stale = proposal.transaction_index <= multisig.stale_transaction_index;
142
143 #[allow(deprecated)]
144 let can_close = match proposal.status {
145 ProposalStatus::Draft { .. } => is_stale,
148 ProposalStatus::Active { .. } => is_stale,
151 ProposalStatus::Approved { .. } => false,
154 ProposalStatus::Rejected { .. } => true,
156 ProposalStatus::Executed { .. } => true,
158 ProposalStatus::Cancelled { .. } => true,
160 ProposalStatus::Executing => false,
162 };
163
164 require!(can_close, MultisigError::InvalidProposalStatus);
165
166 Ok(())
167 }
168
169 #[access_control(_ctx.accounts.validate())]
174 pub fn vault_transaction_accounts_close(_ctx: Context<Self>) -> Result<()> {
175 Ok(())
177 }
178}
179
180#[derive(Accounts)]
182pub struct VaultBatchTransactionAccountClose<'info> {
183 #[account(
184 seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
185 bump = multisig.bump,
186 constraint = multisig.rent_collector.is_some() @ MultisigError::RentReclamationDisabled,
187 )]
188 pub multisig: Account<'info, Multisig>,
189
190 #[account(
191 has_one = multisig @ MultisigError::ProposalForAnotherMultisig,
192 )]
193 pub proposal: Account<'info, Proposal>,
194
195 #[account(
197 mut,
198 has_one = multisig @ MultisigError::TransactionForAnotherMultisig,
199 constraint = batch.index == proposal.transaction_index @ MultisigError::TransactionNotMatchingProposal,
200 )]
201 pub batch: Account<'info, Batch>,
202
203 #[account(
206 mut,
207 close = rent_collector,
208 )]
209 pub transaction: Account<'info, VaultBatchTransaction>,
210
211 #[account(
214 mut,
215 address = multisig.rent_collector.unwrap().key() @ MultisigError::InvalidRentCollector,
216 )]
217 pub rent_collector: AccountInfo<'info>,
218
219 pub system_program: Program<'info, System>,
220}
221
222impl VaultBatchTransactionAccountClose<'_> {
223 fn validate(&self) -> Result<()> {
224 let Self {
225 multisig,
226 proposal,
227 batch,
228 transaction,
229 ..
230 } = self;
231
232 let last_transaction_address = Pubkey::create_program_address(
237 &[
238 SEED_PREFIX,
239 multisig.key().as_ref(),
240 SEED_TRANSACTION,
241 &batch.index.to_le_bytes(),
242 SEED_BATCH_TRANSACTION,
243 &batch.size.to_le_bytes(),
245 &transaction.bump.to_le_bytes(),
247 ],
248 &crate::id(),
249 )
250 .map_err(|_| MultisigError::TransactionNotLastInBatch)?;
251
252 require_keys_eq!(
254 transaction.key(),
255 last_transaction_address,
256 MultisigError::TransactionNotLastInBatch
257 );
258
259 let is_proposal_stale = proposal.transaction_index <= multisig.stale_transaction_index;
260
261 #[allow(deprecated)]
262 let can_close = match proposal.status {
263 ProposalStatus::Draft { .. } => is_proposal_stale,
266 ProposalStatus::Active { .. } => is_proposal_stale,
269 ProposalStatus::Approved { .. } => false,
272 ProposalStatus::Rejected { .. } => true,
274 ProposalStatus::Executed { .. } => true,
276 ProposalStatus::Cancelled { .. } => true,
278 ProposalStatus::Executing => false,
280 };
281
282 require!(can_close, MultisigError::InvalidProposalStatus);
283
284 Ok(())
285 }
286
287 #[access_control(ctx.accounts.validate())]
294 pub fn vault_batch_transaction_account_close(ctx: Context<Self>) -> Result<()> {
295 let batch = &mut ctx.accounts.batch;
296
297 batch.size = batch.size.checked_sub(1).expect("overflow");
298
299 Ok(())
302 }
303}
304#[derive(Accounts)]
308pub struct BatchAccountsClose<'info> {
309 #[account(
310 seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
311 bump = multisig.bump,
312 constraint = multisig.rent_collector.is_some() @ MultisigError::RentReclamationDisabled,
313 )]
314 pub multisig: Account<'info, Multisig>,
315
316 #[account(
317 mut,
318 has_one = multisig @ MultisigError::ProposalForAnotherMultisig,
319 close = rent_collector
320 )]
321 pub proposal: Account<'info, Proposal>,
322
323 #[account(
325 mut,
326 has_one = multisig @ MultisigError::TransactionForAnotherMultisig,
327 constraint = batch.index == proposal.transaction_index @ MultisigError::TransactionNotMatchingProposal,
328 close = rent_collector
329 )]
330 pub batch: Account<'info, Batch>,
331
332 #[account(
335 mut,
336 address = multisig.rent_collector.unwrap().key() @ MultisigError::InvalidRentCollector,
337 )]
338 pub rent_collector: AccountInfo<'info>,
339
340 pub system_program: Program<'info, System>,
341}
342
343impl BatchAccountsClose<'_> {
344 fn validate(&self) -> Result<()> {
345 let Self {
346 multisig,
347 proposal,
348 batch,
349 ..
350 } = self;
351
352 let is_stale = proposal.transaction_index <= multisig.stale_transaction_index;
353
354 #[allow(deprecated)]
355 let can_close = match proposal.status {
356 ProposalStatus::Draft { .. } => is_stale,
359 ProposalStatus::Active { .. } => is_stale,
362 ProposalStatus::Approved { .. } => false,
365 ProposalStatus::Rejected { .. } => true,
367 ProposalStatus::Executed { .. } => true,
369 ProposalStatus::Cancelled { .. } => true,
371 ProposalStatus::Executing => false,
373 };
374
375 require!(can_close, MultisigError::InvalidProposalStatus);
376
377 require_eq!(batch.size, 0, MultisigError::BatchNotEmpty);
379
380 Ok(())
381 }
382
383 #[access_control(_ctx.accounts.validate())]
389 pub fn batch_accounts_close(_ctx: Context<Self>) -> Result<()> {
390 Ok(())
392 }
393}
394