squads_multisig_program/instructions/
config_transaction_execute.rs1use anchor_lang::prelude::*;
2
3use crate::errors::*;
4use crate::id;
5use crate::state::*;
6use crate::utils::*;
7
8#[derive(Accounts)]
9pub struct ConfigTransactionExecute<'info> {
10 #[account(
12 mut,
13 seeds = [SEED_PREFIX, SEED_MULTISIG, multisig.create_key.as_ref()],
14 bump = multisig.bump,
15 )]
16 pub multisig: Box<Account<'info, Multisig>>,
17
18 pub member: Signer<'info>,
20
21 #[account(
23 mut,
24 seeds = [
25 SEED_PREFIX,
26 multisig.key().as_ref(),
27 SEED_TRANSACTION,
28 &transaction.index.to_le_bytes(),
29 SEED_PROPOSAL,
30 ],
31 bump = proposal.bump,
32 )]
33 pub proposal: Account<'info, Proposal>,
34
35 #[account(
37 seeds = [
38 SEED_PREFIX,
39 multisig.key().as_ref(),
40 SEED_TRANSACTION,
41 &transaction.index.to_le_bytes(),
42 ],
43 bump = transaction.bump,
44 )]
45 pub transaction: Account<'info, ConfigTransaction>,
46
47 #[account(mut)]
51 pub rent_payer: Option<Signer<'info>>,
52
53 pub system_program: Option<Program<'info, System>>,
55 }
59
60impl<'info> ConfigTransactionExecute<'info> {
61 fn validate(&self) -> Result<()> {
62 let Self {
63 multisig,
64 proposal,
65 member,
66 ..
67 } = self;
68
69 require!(
71 multisig.is_member(member.key()).is_some(),
72 MultisigError::NotAMember
73 );
74 require!(
75 multisig.member_has_permission(member.key(), Permission::Execute),
76 MultisigError::Unauthorized
77 );
78
79 match proposal.status {
81 ProposalStatus::Approved { timestamp } => {
82 require!(
83 Clock::get()?.unix_timestamp - timestamp >= i64::from(multisig.time_lock),
84 MultisigError::TimeLockNotReleased
85 );
86 }
87 _ => return err!(MultisigError::InvalidProposalStatus),
88 }
89 require!(
91 proposal.transaction_index > multisig.stale_transaction_index,
92 MultisigError::StaleProposal
93 );
94
95 Ok(())
98 }
99
100 #[access_control(ctx.accounts.validate())]
103 pub fn config_transaction_execute(ctx: Context<'_, '_, 'info, 'info, Self>) -> Result<()> {
104 let multisig = &mut ctx.accounts.multisig;
105 let transaction = &ctx.accounts.transaction;
106 let proposal = &mut ctx.accounts.proposal;
107
108 let rent = Rent::get()?;
109
110 for action in transaction.actions.iter() {
112 match action {
113 ConfigAction::AddMember { new_member } => {
114 multisig.add_member(new_member.to_owned());
115
116 multisig.invalidate_prior_transactions();
117 }
118
119 ConfigAction::RemoveMember { old_member } => {
120 multisig.remove_member(old_member.to_owned())?;
121
122 multisig.invalidate_prior_transactions();
123 }
124
125 ConfigAction::ChangeThreshold { new_threshold } => {
126 multisig.threshold = *new_threshold;
127
128 multisig.invalidate_prior_transactions();
129 }
130
131 ConfigAction::SetTimeLock { new_time_lock } => {
132 multisig.time_lock = *new_time_lock;
133
134 multisig.invalidate_prior_transactions();
135 }
136
137 ConfigAction::AddSpendingLimit {
138 create_key,
139 vault_index,
140 mint,
141 amount,
142 period,
143 members,
144 destinations,
145 } => {
146 let (spending_limit_key, spending_limit_bump) = Pubkey::find_program_address(
147 &[
148 SEED_PREFIX,
149 multisig.key().as_ref(),
150 SEED_SPENDING_LIMIT,
151 create_key.as_ref(),
152 ],
153 ctx.program_id,
154 );
155
156 let spending_limit_info = ctx
158 .remaining_accounts
159 .iter()
160 .find(|acc| acc.key == &spending_limit_key)
161 .ok_or(MultisigError::MissingAccount)?;
162
163 let rent_payer = &ctx
165 .accounts
166 .rent_payer
167 .as_ref()
168 .ok_or(MultisigError::MissingAccount)?;
169 let system_program = &ctx
170 .accounts
171 .system_program
172 .as_ref()
173 .ok_or(MultisigError::MissingAccount)?;
174
175 create_account(
177 rent_payer,
178 spending_limit_info,
179 system_program,
180 &id(),
181 &rent,
182 SpendingLimit::size(members.len(), destinations.len()),
183 vec![
184 SEED_PREFIX.to_vec(),
185 multisig.key().as_ref().to_vec(),
186 SEED_SPENDING_LIMIT.to_vec(),
187 create_key.as_ref().to_vec(),
188 vec![spending_limit_bump],
189 ],
190 )?;
191
192 let mut members = members.to_vec();
193 members.sort();
195
196 let spending_limit = SpendingLimit {
198 multisig: multisig.key().to_owned(),
199 create_key: create_key.to_owned(),
200 vault_index: *vault_index,
201 amount: *amount,
202 mint: *mint,
203 period: *period,
204 remaining_amount: *amount,
205 last_reset: Clock::get()?.unix_timestamp,
206 bump: spending_limit_bump,
207 members,
208 destinations: destinations.to_vec(),
209 };
210
211 spending_limit.invariant()?;
212
213 spending_limit
214 .try_serialize(&mut &mut spending_limit_info.data.borrow_mut()[..])?;
215 }
216
217 ConfigAction::RemoveSpendingLimit {
218 spending_limit: spending_limit_key,
219 } => {
220 let spending_limit_info = ctx
222 .remaining_accounts
223 .iter()
224 .find(|acc| acc.key == spending_limit_key)
225 .ok_or(MultisigError::MissingAccount)?;
226
227 let rent_payer = &ctx
229 .accounts
230 .rent_payer
231 .as_ref()
232 .ok_or(MultisigError::MissingAccount)?;
233
234 let spending_limit = Account::<SpendingLimit>::try_from(spending_limit_info)?;
235
236 require_keys_eq!(
238 spending_limit.multisig,
239 multisig.key(),
240 MultisigError::InvalidAccount
241 );
242
243 spending_limit.close(rent_payer.to_account_info())?;
244
245 }
248
249 ConfigAction::SetRentCollector { new_rent_collector } => {
250 multisig.rent_collector = *new_rent_collector;
251
252 }
255 }
256 }
257
258 Multisig::realloc_if_needed(
260 multisig.to_account_info(),
261 multisig.members.len(),
262 ctx.accounts
263 .rent_payer
264 .as_ref()
265 .map(ToAccountInfo::to_account_info),
266 ctx.accounts
267 .system_program
268 .as_ref()
269 .map(ToAccountInfo::to_account_info),
270 )?;
271
272 multisig.invariant()?;
274
275 proposal.status = ProposalStatus::Executed {
277 timestamp: Clock::get()?.unix_timestamp,
278 };
279
280 Ok(())
281 }
282}