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 for sl_member in members.iter() {
148 require!(
149 multisig.is_member(*sl_member).is_some(),
150 MultisigError::NotAMember
151 );
152 }
153
154 let (spending_limit_key, spending_limit_bump) = Pubkey::find_program_address(
155 &[
156 SEED_PREFIX,
157 multisig.key().as_ref(),
158 SEED_SPENDING_LIMIT,
159 create_key.as_ref(),
160 ],
161 ctx.program_id,
162 );
163
164 let spending_limit_info = ctx
166 .remaining_accounts
167 .iter()
168 .find(|acc| acc.key == &spending_limit_key)
169 .ok_or(MultisigError::MissingAccount)?;
170
171 let rent_payer = &ctx
173 .accounts
174 .rent_payer
175 .as_ref()
176 .ok_or(MultisigError::MissingAccount)?;
177 let system_program = &ctx
178 .accounts
179 .system_program
180 .as_ref()
181 .ok_or(MultisigError::MissingAccount)?;
182
183 create_account(
185 rent_payer,
186 spending_limit_info,
187 system_program,
188 &id(),
189 &rent,
190 SpendingLimit::size(members.len(), destinations.len()),
191 vec![
192 SEED_PREFIX.to_vec(),
193 multisig.key().as_ref().to_vec(),
194 SEED_SPENDING_LIMIT.to_vec(),
195 create_key.as_ref().to_vec(),
196 vec![spending_limit_bump],
197 ],
198 )?;
199
200 let mut members = members.to_vec();
201 members.sort();
203
204 let spending_limit = SpendingLimit {
206 multisig: multisig.key().to_owned(),
207 create_key: create_key.to_owned(),
208 vault_index: *vault_index,
209 amount: *amount,
210 mint: *mint,
211 period: *period,
212 remaining_amount: *amount,
213 last_reset: Clock::get()?.unix_timestamp,
214 bump: spending_limit_bump,
215 members,
216 destinations: destinations.to_vec(),
217 };
218
219 spending_limit.invariant()?;
220
221 spending_limit
222 .try_serialize(&mut &mut spending_limit_info.data.borrow_mut()[..])?;
223 }
224
225 ConfigAction::RemoveSpendingLimit {
226 spending_limit: spending_limit_key,
227 } => {
228 let spending_limit_info = ctx
230 .remaining_accounts
231 .iter()
232 .find(|acc| acc.key == spending_limit_key)
233 .ok_or(MultisigError::MissingAccount)?;
234
235 let rent_payer = &ctx
237 .accounts
238 .rent_payer
239 .as_ref()
240 .ok_or(MultisigError::MissingAccount)?;
241
242 let spending_limit = Account::<SpendingLimit>::try_from(spending_limit_info)?;
243
244 require_keys_eq!(
246 spending_limit.multisig,
247 multisig.key(),
248 MultisigError::InvalidAccount
249 );
250
251 spending_limit.close(rent_payer.to_account_info())?;
252
253 }
256
257 ConfigAction::SetRentCollector { new_rent_collector } => {
258 multisig.rent_collector = *new_rent_collector;
259
260 }
263 }
264 }
265
266 Multisig::realloc_if_needed(
268 multisig.to_account_info(),
269 multisig.members.len(),
270 ctx.accounts
271 .rent_payer
272 .as_ref()
273 .map(ToAccountInfo::to_account_info),
274 ctx.accounts
275 .system_program
276 .as_ref()
277 .map(ToAccountInfo::to_account_info),
278 )?;
279
280 multisig.invariant()?;
282
283 proposal.status = ProposalStatus::Executed {
285 timestamp: Clock::get()?.unix_timestamp,
286 };
287
288 Ok(())
289 }
290}