squads_multisig_program/state/
vault_transaction.rs1use anchor_lang::prelude::*;
2use anchor_lang::solana_program::borsh0_10::get_instance_packed_len;
3
4use crate::errors::*;
5use crate::instructions::{CompiledInstruction, MessageAddressTableLookup, TransactionMessage};
6
7#[account]
11#[derive(Default)]
12pub struct VaultTransaction {
13 pub multisig: Pubkey,
15 pub creator: Pubkey,
17 pub index: u64,
19 pub bump: u8,
21 pub vault_index: u8,
23 pub vault_bump: u8,
25 pub ephemeral_signer_bumps: Vec<u8>,
33 pub message: VaultTransactionMessage,
35}
36
37impl VaultTransaction {
38 pub fn size(ephemeral_signers_length: u8, transaction_message: &[u8]) -> Result<usize> {
39 let transaction_message: VaultTransactionMessage =
40 TransactionMessage::deserialize(&mut &transaction_message[..])?.try_into()?;
41 let message_size = get_instance_packed_len(&transaction_message).unwrap_or_default();
42
43 Ok(
44 8 + 32 + 32 + 8 + 1 + 1 + 1 + (4 + usize::from(ephemeral_signers_length)) + message_size, )
54 }
55 pub fn take(&mut self) -> VaultTransaction {
58 core::mem::take(self)
59 }
60}
61
62#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
63pub struct VaultTransactionMessage {
64 pub num_signers: u8,
66 pub num_writable_signers: u8,
68 pub num_writable_non_signers: u8,
70 pub account_keys: Vec<Pubkey>,
81 pub instructions: Vec<MultisigCompiledInstruction>,
83 pub address_table_lookups: Vec<MultisigMessageAddressTableLookup>,
86}
87
88impl VaultTransactionMessage {
89 pub fn num_all_account_keys(&self) -> usize {
91 let num_account_keys_from_lookups = self
92 .address_table_lookups
93 .iter()
94 .map(|lookup| lookup.writable_indexes.len() + lookup.readonly_indexes.len())
95 .sum::<usize>();
96
97 self.account_keys.len() + num_account_keys_from_lookups
98 }
99
100 pub fn is_static_writable_index(&self, key_index: usize) -> bool {
102 let num_account_keys = self.account_keys.len();
103 let num_signers = usize::from(self.num_signers);
104 let num_writable_signers = usize::from(self.num_writable_signers);
105 let num_writable_non_signers = usize::from(self.num_writable_non_signers);
106
107 if key_index >= num_account_keys {
108 return false;
110 }
111
112 if key_index < num_writable_signers {
113 return true;
115 }
116
117 if key_index >= num_signers {
118 let index_into_non_signers = key_index.saturating_sub(num_signers);
120 return index_into_non_signers < num_writable_non_signers;
122 }
123
124 false
125 }
126
127 pub fn is_signer_index(&self, key_index: usize) -> bool {
129 key_index < usize::from(self.num_signers)
130 }
131}
132
133impl TryFrom<TransactionMessage> for VaultTransactionMessage {
134 type Error = Error;
135
136 fn try_from(message: TransactionMessage) -> Result<Self> {
137 let account_keys: Vec<Pubkey> = message.account_keys.into();
138 let instructions: Vec<CompiledInstruction> = message.instructions.into();
139 let instructions: Vec<MultisigCompiledInstruction> = instructions
140 .into_iter()
141 .map(MultisigCompiledInstruction::from)
142 .collect();
143 let address_table_lookups: Vec<MessageAddressTableLookup> =
144 message.address_table_lookups.into();
145
146 let num_all_account_keys = account_keys.len()
147 + address_table_lookups
148 .iter()
149 .map(|lookup| lookup.writable_indexes.len() + lookup.readonly_indexes.len())
150 .sum::<usize>();
151
152 require!(
153 usize::from(message.num_signers) <= account_keys.len(),
154 MultisigError::InvalidTransactionMessage
155 );
156 require!(
157 message.num_writable_signers <= message.num_signers,
158 MultisigError::InvalidTransactionMessage
159 );
160 require!(
161 usize::from(message.num_writable_non_signers)
162 <= account_keys
163 .len()
164 .saturating_sub(usize::from(message.num_signers)),
165 MultisigError::InvalidTransactionMessage
166 );
167
168 for instruction in &instructions {
170 require!(
171 usize::from(instruction.program_id_index) < num_all_account_keys,
172 MultisigError::InvalidTransactionMessage
173 );
174
175 for account_index in &instruction.account_indexes {
176 require!(
177 usize::from(*account_index) < num_all_account_keys,
178 MultisigError::InvalidTransactionMessage
179 );
180 }
181 }
182
183 Ok(Self {
184 num_signers: message.num_signers,
185 num_writable_signers: message.num_writable_signers,
186 num_writable_non_signers: message.num_writable_non_signers,
187 account_keys,
188 instructions,
189 address_table_lookups: address_table_lookups
190 .into_iter()
191 .map(MultisigMessageAddressTableLookup::from)
192 .collect(),
193 })
194 }
195}
196
197#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
200pub struct MultisigCompiledInstruction {
201 pub program_id_index: u8,
202 pub account_indexes: Vec<u8>,
204 pub data: Vec<u8>,
206}
207
208impl From<CompiledInstruction> for MultisigCompiledInstruction {
209 fn from(compiled_instruction: CompiledInstruction) -> Self {
210 Self {
211 program_id_index: compiled_instruction.program_id_index,
212 account_indexes: compiled_instruction.account_indexes.into(),
213 data: compiled_instruction.data.into(),
214 }
215 }
216}
217
218#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
221pub struct MultisigMessageAddressTableLookup {
222 pub account_key: Pubkey,
224 pub writable_indexes: Vec<u8>,
226 pub readonly_indexes: Vec<u8>,
228}
229
230impl From<MessageAddressTableLookup> for MultisigMessageAddressTableLookup {
231 fn from(m: MessageAddressTableLookup) -> Self {
232 Self {
233 account_key: m.account_key,
234 writable_indexes: m.writable_indexes.into(),
235 readonly_indexes: m.readonly_indexes.into(),
236 }
237 }
238}