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]
11pub struct VaultTransaction {
12 pub multisig: Pubkey,
14 pub creator: Pubkey,
16 pub index: u64,
18 pub bump: u8,
20 pub vault_index: u8,
22 pub vault_bump: u8,
24 pub ephemeral_signer_bumps: Vec<u8>,
32 pub message: VaultTransactionMessage,
34}
35
36impl VaultTransaction {
37 pub fn size(ephemeral_signers_length: u8, transaction_message: &[u8]) -> Result<usize> {
38 let transaction_message: VaultTransactionMessage =
39 TransactionMessage::deserialize(&mut &transaction_message[..])?.try_into()?;
40 let message_size = get_instance_packed_len(&transaction_message).unwrap_or_default();
41
42 Ok(
43 8 + 32 + 32 + 8 + 1 + 1 + 1 + (4 + usize::from(ephemeral_signers_length)) + message_size, )
53 }
54}
55
56#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
57pub struct VaultTransactionMessage {
58 pub num_signers: u8,
60 pub num_writable_signers: u8,
62 pub num_writable_non_signers: u8,
64 pub account_keys: Vec<Pubkey>,
75 pub instructions: Vec<MultisigCompiledInstruction>,
77 pub address_table_lookups: Vec<MultisigMessageAddressTableLookup>,
80}
81
82impl VaultTransactionMessage {
83 pub fn num_all_account_keys(&self) -> usize {
85 let num_account_keys_from_lookups = self
86 .address_table_lookups
87 .iter()
88 .map(|lookup| lookup.writable_indexes.len() + lookup.readonly_indexes.len())
89 .sum::<usize>();
90
91 self.account_keys.len() + num_account_keys_from_lookups
92 }
93
94 pub fn is_static_writable_index(&self, key_index: usize) -> bool {
96 let num_account_keys = self.account_keys.len();
97 let num_signers = usize::from(self.num_signers);
98 let num_writable_signers = usize::from(self.num_writable_signers);
99 let num_writable_non_signers = usize::from(self.num_writable_non_signers);
100
101 if key_index >= num_account_keys {
102 return false;
104 }
105
106 if key_index < num_writable_signers {
107 return true;
109 }
110
111 if key_index >= num_signers {
112 let index_into_non_signers = key_index.saturating_sub(num_signers);
114 return index_into_non_signers < num_writable_non_signers;
116 }
117
118 false
119 }
120
121 pub fn is_signer_index(&self, key_index: usize) -> bool {
123 key_index < usize::from(self.num_signers)
124 }
125}
126
127impl TryFrom<TransactionMessage> for VaultTransactionMessage {
128 type Error = Error;
129
130 fn try_from(message: TransactionMessage) -> Result<Self> {
131 let account_keys: Vec<Pubkey> = message.account_keys.into();
132 let instructions: Vec<CompiledInstruction> = message.instructions.into();
133 let instructions: Vec<MultisigCompiledInstruction> = instructions
134 .into_iter()
135 .map(MultisigCompiledInstruction::from)
136 .collect();
137 let address_table_lookups: Vec<MessageAddressTableLookup> =
138 message.address_table_lookups.into();
139
140 let num_all_account_keys = account_keys.len()
141 + address_table_lookups
142 .iter()
143 .map(|lookup| lookup.writable_indexes.len() + lookup.readonly_indexes.len())
144 .sum::<usize>();
145
146 require!(
147 usize::from(message.num_signers) <= account_keys.len(),
148 MultisigError::InvalidTransactionMessage
149 );
150 require!(
151 message.num_writable_signers <= message.num_signers,
152 MultisigError::InvalidTransactionMessage
153 );
154 require!(
155 usize::from(message.num_writable_non_signers)
156 <= account_keys
157 .len()
158 .saturating_sub(usize::from(message.num_signers)),
159 MultisigError::InvalidTransactionMessage
160 );
161
162 for instruction in &instructions {
164 require!(
165 usize::from(instruction.program_id_index) < num_all_account_keys,
166 MultisigError::InvalidTransactionMessage
167 );
168
169 for account_index in &instruction.account_indexes {
170 require!(
171 usize::from(*account_index) < num_all_account_keys,
172 MultisigError::InvalidTransactionMessage
173 );
174 }
175 }
176
177 Ok(Self {
178 num_signers: message.num_signers,
179 num_writable_signers: message.num_writable_signers,
180 num_writable_non_signers: message.num_writable_non_signers,
181 account_keys,
182 instructions,
183 address_table_lookups: address_table_lookups
184 .into_iter()
185 .map(MultisigMessageAddressTableLookup::from)
186 .collect(),
187 })
188 }
189}
190
191#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
194pub struct MultisigCompiledInstruction {
195 pub program_id_index: u8,
196 pub account_indexes: Vec<u8>,
198 pub data: Vec<u8>,
200}
201
202impl From<CompiledInstruction> for MultisigCompiledInstruction {
203 fn from(compiled_instruction: CompiledInstruction) -> Self {
204 Self {
205 program_id_index: compiled_instruction.program_id_index,
206 account_indexes: compiled_instruction.account_indexes.into(),
207 data: compiled_instruction.data.into(),
208 }
209 }
210}
211
212#[derive(AnchorSerialize, AnchorDeserialize, Clone)]
215pub struct MultisigMessageAddressTableLookup {
216 pub account_key: Pubkey,
218 pub writable_indexes: Vec<u8>,
220 pub readonly_indexes: Vec<u8>,
222}
223
224impl From<MessageAddressTableLookup> for MultisigMessageAddressTableLookup {
225 fn from(m: MessageAddressTableLookup) -> Self {
226 Self {
227 account_key: m.account_key,
228 writable_indexes: m.writable_indexes.into(),
229 readonly_indexes: m.readonly_indexes.into(),
230 }
231 }
232}