squads_multisig/client.rs
1use solana_client::nonblocking::rpc_client::RpcClient;
2
3pub use squads_multisig_program::accounts::BatchAccountsClose as BatchAccountsCloseAccounts;
4pub use squads_multisig_program::accounts::ConfigTransactionAccountsClose as ConfigTransactionAccountsCloseAccounts;
5pub use squads_multisig_program::accounts::ConfigTransactionCreate as ConfigTransactionCreateAccounts;
6pub use squads_multisig_program::accounts::ConfigTransactionExecute as ConfigTransactionExecuteAccounts;
7pub use squads_multisig_program::accounts::MultisigCreateV2 as MultisigCreateAccountsV2;
8pub use squads_multisig_program::accounts::ProposalCreate as ProposalCreateAccounts;
9pub use squads_multisig_program::accounts::ProposalVote as ProposalVoteAccounts;
10pub use squads_multisig_program::accounts::SpendingLimitUse as SpendingLimitUseAccounts;
11pub use squads_multisig_program::accounts::VaultBatchTransactionAccountClose as VaultBatchTransactionAccountCloseAccounts;
12pub use squads_multisig_program::accounts::VaultTransactionAccountsClose as VaultTransactionAccountsCloseAccounts;
13pub use squads_multisig_program::accounts::VaultTransactionCreate as VaultTransactionCreateAccounts;
14pub use squads_multisig_program::accounts::VaultTransactionExecute as VaultTransactionExecuteAccounts;
15use squads_multisig_program::anchor_lang::AnchorSerialize;
16pub use squads_multisig_program::instruction::ConfigTransactionAccountsClose as ConfigTransactionAccountsCloseData;
17pub use squads_multisig_program::instruction::ConfigTransactionCreate as ConfigTransactionCreateData;
18pub use squads_multisig_program::instruction::ConfigTransactionExecute as ConfigTransactionExecuteData;
19pub use squads_multisig_program::instruction::MultisigCreate as MultisigCreateData;
20pub use squads_multisig_program::instruction::MultisigCreateV2 as MultisigCreateDataV2;
21pub use squads_multisig_program::instruction::ProposalApprove as ProposalApproveData;
22pub use squads_multisig_program::instruction::ProposalCancel as ProposalCancelData;
23pub use squads_multisig_program::instruction::ProposalCreate as ProposalCreateData;
24pub use squads_multisig_program::instruction::SpendingLimitUse as SpendingLimitUseData;
25pub use squads_multisig_program::instruction::VaultTransactionAccountsClose as VaultTransactionAccountsCloseData;
26pub use squads_multisig_program::instruction::VaultTransactionCreate as VaultTransactionCreateData;
27pub use squads_multisig_program::instruction::VaultTransactionExecute as VaultTransactionExecuteData;
28pub use squads_multisig_program::instructions::ConfigTransactionCreateArgs;
29pub use squads_multisig_program::instructions::MultisigCreateArgsV2;
30pub use squads_multisig_program::instructions::ProposalCreateArgs;
31pub use squads_multisig_program::instructions::ProposalVoteArgs;
32pub use squads_multisig_program::instructions::SpendingLimitUseArgs;
33pub use squads_multisig_program::instructions::VaultTransactionCreateArgs;
34use squads_multisig_program::TransactionMessage;
35
36use crate::anchor_lang::prelude::Pubkey;
37use crate::anchor_lang::AccountDeserialize;
38use crate::anchor_lang::{
39 solana_program::instruction::Instruction, InstructionData, ToAccountMetas,
40};
41use crate::client::utils::IntoAccountMetas;
42use crate::error::ClientError;
43use crate::pda::get_vault_pda;
44use crate::solana_program::address_lookup_table_account::AddressLookupTableAccount;
45use crate::solana_program::instruction::AccountMeta;
46use crate::state::{Multisig, SpendingLimit};
47use crate::vault_transaction::{Error, VaultTransactionMessageExt};
48use crate::ClientResult;
49
50/// Gets a `Multisig` account from the chain.
51pub async fn get_multisig(rpc_client: &RpcClient, multisig_key: &Pubkey) -> ClientResult<Multisig> {
52 let multisig_account = rpc_client.get_account(multisig_key).await?;
53
54 let multisig = Multisig::try_deserialize(&mut multisig_account.data.as_slice())
55 .map_err(|_| ClientError::DeserializationError)?;
56
57 Ok(multisig)
58}
59
60/// Gets a `SpendingLimit` account from the chain.
61pub async fn get_spending_limit(
62 rpc_client: &RpcClient,
63 spending_limit_key: &Pubkey,
64) -> ClientResult<SpendingLimit> {
65 let spending_limit_account = rpc_client.get_account(spending_limit_key).await?;
66
67 let spending_limit =
68 SpendingLimit::try_deserialize(&mut spending_limit_account.data.as_slice())
69 .map_err(|_| ClientError::DeserializationError)?;
70
71 Ok(spending_limit)
72}
73
74/// Creates a new multisig config transaction.
75/// Example:
76/// ```
77/// use squads_multisig::anchor_lang::error::ComparedValues::Pubkeys;
78/// use squads_multisig::solana_program::pubkey::Pubkey;
79/// use squads_multisig::solana_program::system_program;
80/// use squads_multisig::state::{ConfigAction, Member, Permissions, Permission};
81/// use squads_multisig::client::{
82/// MultisigCreateAccountsV2,
83/// MultisigCreateArgsV2,
84/// multisig_create_v2
85/// };
86///
87/// let ix = multisig_create_v2(
88/// MultisigCreateAccountsV2 {
89/// program_config: Pubkey::new_unique(),
90/// treasury: Pubkey::new_unique(),
91/// multisig: Pubkey::new_unique(),
92/// create_key: Pubkey::new_unique(),
93/// creator: Pubkey::new_unique(),
94/// system_program: system_program::id(),
95/// },
96/// MultisigCreateArgsV2 {
97/// members: vec![
98/// Member {
99/// key: Pubkey::new_unique(),
100/// permissions: Permissions::from_vec(&[Permission::Initiate, Permission::Vote, Permission::Execute]),
101/// }
102/// ],
103/// threshold: 1,
104/// time_lock: 0,
105/// config_authority: None,
106/// rent_collector: None,
107/// memo: Some("Deploy my own Squad".to_string()),
108/// },
109/// Some(squads_multisig_program::ID)
110/// );
111/// ```
112///
113pub fn multisig_create_v2(
114 accounts: MultisigCreateAccountsV2,
115 args: MultisigCreateArgsV2,
116 program_id: Option<Pubkey>,
117) -> Instruction {
118 Instruction {
119 accounts: accounts.to_account_metas(Some(false)),
120 data: MultisigCreateDataV2 { args }.data(),
121 program_id: program_id.unwrap_or(squads_multisig_program::ID),
122 }
123}
124
125/// Creates a new multisig config transaction.
126/// Example:
127/// ```
128/// use squads_multisig::solana_program::pubkey::Pubkey;
129/// use squads_multisig::solana_program::system_program;
130/// use squads_multisig::state::ConfigAction;
131/// use squads_multisig::client::{
132/// ConfigTransactionCreateAccounts,
133/// ConfigTransactionCreateArgs,
134/// config_transaction_create
135/// };
136///
137/// let ix = config_transaction_create(
138/// ConfigTransactionCreateAccounts {
139/// multisig: Pubkey::new_unique(),
140/// creator: Pubkey::new_unique(),
141/// rent_payer: Pubkey::new_unique(),
142/// transaction: Pubkey::new_unique(),
143/// system_program: system_program::id(),
144/// },
145/// ConfigTransactionCreateArgs {
146/// actions: vec![ConfigAction::ChangeThreshold { new_threshold: 2 }],
147/// memo: None,
148/// },
149/// Some(squads_multisig_program::ID)
150/// );
151/// ```
152///
153pub fn config_transaction_create(
154 accounts: ConfigTransactionCreateAccounts,
155 args: ConfigTransactionCreateArgs,
156 program_id: Option<Pubkey>,
157) -> Instruction {
158 Instruction {
159 accounts: accounts.to_account_metas(Some(false)),
160 data: ConfigTransactionCreateData { args }.data(),
161 program_id: program_id.unwrap_or(squads_multisig_program::ID),
162 }
163}
164
165/// Executes a multisig config transaction.
166/// Example:
167/// ```
168/// use squads_multisig::solana_program::pubkey::Pubkey;
169/// use squads_multisig::solana_program::system_program;
170/// use squads_multisig::state::ConfigAction;
171/// use squads_multisig::client::{
172/// ConfigTransactionExecuteAccounts,
173/// config_transaction_execute
174/// };
175///
176/// let ix = config_transaction_execute(
177/// ConfigTransactionExecuteAccounts {
178/// multisig: Pubkey::new_unique(),
179/// member: Pubkey::new_unique(),
180/// proposal: Pubkey::new_unique(),
181/// transaction: Pubkey::new_unique(),
182/// rent_payer: None,
183/// system_program: None,
184/// },
185/// vec![],
186/// Some(squads_multisig_program::ID)
187/// );
188/// ```
189pub fn config_transaction_execute(
190 accounts: ConfigTransactionExecuteAccounts,
191 spending_limit_accounts: Vec<Pubkey>,
192 program_id: Option<Pubkey>,
193) -> Instruction {
194 let program_id = program_id.unwrap_or(squads_multisig_program::ID);
195
196 let account_metas = [
197 accounts.into_account_metas(program_id),
198 // Spending Limit accounts are optional and are passed as remaining_accounts
199 // if the Config Transaction adds or removes some.
200 spending_limit_accounts
201 .into_iter()
202 .map(|key| AccountMeta::new(key, false))
203 .collect(),
204 ]
205 .concat();
206
207 Instruction {
208 accounts: account_metas,
209 data: ConfigTransactionExecuteData.data(),
210 program_id,
211 }
212}
213
214/// Creates a new multisig proposal.
215/// Example:
216/// ```
217/// use squads_multisig::solana_program::pubkey::Pubkey;
218/// use squads_multisig::solana_program::system_program;
219/// use squads_multisig::state::ConfigAction;
220/// use squads_multisig::client::{
221/// ProposalCreateAccounts,
222/// ProposalCreateArgs,
223/// proposal_create
224/// };
225///
226/// let ix = proposal_create(
227/// ProposalCreateAccounts {
228/// multisig: Pubkey::new_unique(),
229/// creator: Pubkey::new_unique(),
230/// proposal: Pubkey::new_unique(),
231/// rent_payer: Pubkey::new_unique(),
232/// system_program: system_program::id(),
233/// },
234/// ProposalCreateArgs {
235/// transaction_index: 0,
236/// draft: false,
237/// },
238/// Some(squads_multisig_program::ID)
239/// );
240/// ```
241///
242pub fn proposal_create(
243 accounts: ProposalCreateAccounts,
244 args: ProposalCreateArgs,
245 program_id: Option<Pubkey>,
246) -> Instruction {
247 Instruction {
248 accounts: accounts.to_account_metas(Some(false)),
249 data: ProposalCreateData { args }.data(),
250 program_id: program_id.unwrap_or(squads_multisig_program::ID),
251 }
252}
253
254/// Votes "approve" on a multisig proposal.
255/// Example:
256/// ```
257/// use squads_multisig::solana_program::pubkey::Pubkey;
258/// use squads_multisig::client::{
259/// ProposalVoteAccounts,
260/// ProposalVoteArgs,
261/// proposal_approve,
262/// };
263///
264/// let ix = proposal_approve(
265/// ProposalVoteAccounts {
266/// multisig: Pubkey::new_unique(),
267/// proposal: Pubkey::new_unique(),
268/// member: Pubkey::new_unique(),
269/// },
270/// ProposalVoteArgs { memo: None },
271/// Some(squads_multisig_program::ID)
272/// );
273/// ```
274pub fn proposal_approve(
275 accounts: ProposalVoteAccounts,
276 args: ProposalVoteArgs,
277 program_id: Option<Pubkey>,
278) -> Instruction {
279 Instruction {
280 accounts: accounts.to_account_metas(Some(false)),
281 data: ProposalApproveData { args }.data(),
282 program_id: program_id.unwrap_or(squads_multisig_program::ID),
283 }
284}
285
286/// Votes "cancel" on a multisig proposal.
287/// Example:
288/// ```
289/// use squads_multisig::solana_program::pubkey::Pubkey;
290/// use squads_multisig::client::{
291/// ProposalVoteAccounts,
292/// ProposalVoteArgs,
293/// proposal_cancel,
294/// };
295///
296/// let ix = proposal_cancel(
297/// ProposalVoteAccounts {
298/// multisig: Pubkey::new_unique(),
299/// proposal: Pubkey::new_unique(),
300/// member: Pubkey::new_unique(),
301/// },
302/// ProposalVoteArgs { memo: None },
303/// Some(squads_multisig_program::ID)
304/// );
305/// ```
306pub fn proposal_cancel(
307 accounts: ProposalVoteAccounts,
308 args: ProposalVoteArgs,
309 program_id: Option<Pubkey>,
310) -> Instruction {
311 Instruction {
312 accounts: accounts.to_account_metas(Some(false)),
313 data: ProposalCancelData { args }.data(),
314 program_id: program_id.unwrap_or(squads_multisig_program::ID),
315 }
316}
317
318/// Use a Spending Limit to transfer tokens from a multisig vault to a destination account.
319/// Example:
320/// ```
321/// use squads_multisig::solana_program::pubkey::Pubkey;
322/// use squads_multisig::solana_program::system_program;
323/// use squads_multisig::solana_program::native_token::LAMPORTS_PER_SOL;
324/// use squads_multisig::client::{
325/// SpendingLimitUseAccounts,
326/// SpendingLimitUseArgs,
327/// spending_limit_use,
328/// };
329///
330/// let ix = spending_limit_use(
331/// SpendingLimitUseAccounts {
332/// multisig: Pubkey::new_unique(),
333/// member: Pubkey::new_unique(),
334/// spending_limit: Pubkey::new_unique(),
335/// vault: Pubkey::new_unique(),
336/// destination: Pubkey::new_unique(),
337/// system_program: Some(system_program::id()),
338/// mint: None,
339/// vault_token_account: None,
340/// destination_token_account: None,
341/// token_program: None,
342/// },
343/// SpendingLimitUseArgs {
344/// amount: 1 * LAMPORTS_PER_SOL,
345/// decimals: 9,
346/// memo: None
347/// },
348/// None,
349/// );
350/// ```
351///
352pub fn spending_limit_use(
353 accounts: SpendingLimitUseAccounts,
354 args: SpendingLimitUseArgs,
355 program_id: Option<Pubkey>,
356) -> Instruction {
357 let program_id = program_id.unwrap_or(squads_multisig_program::ID);
358
359 Instruction {
360 accounts: accounts.into_account_metas(program_id),
361 data: SpendingLimitUseData { args }.data(),
362 program_id,
363 }
364}
365
366/// Creates a new vault transaction.
367/// Example:
368/// ```
369/// use squads_multisig::anchor_lang::AnchorSerialize;
370/// use squads_multisig::solana_program::pubkey::Pubkey;
371/// use squads_multisig::solana_program::{system_instruction, system_program};
372/// use squads_multisig::client::{
373/// VaultTransactionCreateAccounts,
374/// VaultTransactionCreateArgs,
375/// vault_transaction_create,
376/// };
377/// use squads_multisig::pda::get_vault_pda;
378/// use squads_multisig::vault_transaction::VaultTransactionMessageExt;
379/// use squads_multisig_program::TransactionMessage;
380///
381/// let multisig = Pubkey::new_unique();
382/// let vault_index = 0;
383/// let vault_pda = get_vault_pda(&multisig, vault_index, None).0;
384///
385/// // Create a vault transaction that includes 1 instruction - SOL transfer from the default vault.
386/// let message = TransactionMessage::try_compile(
387/// &vault_pda,
388/// &[system_instruction::transfer(&vault_pda, &Pubkey::new_unique(), 1_000_000)],
389/// &[]
390/// ).unwrap();
391///
392/// let ix = vault_transaction_create(
393/// VaultTransactionCreateAccounts {
394/// multisig,
395/// transaction: Pubkey::new_unique(),
396/// creator: Pubkey::new_unique(),
397/// rent_payer: Pubkey::new_unique(),
398/// system_program: system_program::id(),
399/// },
400/// vault_index,
401/// 0,
402/// &message,
403/// None,
404/// None,
405/// );
406/// ```
407pub fn vault_transaction_create(
408 accounts: VaultTransactionCreateAccounts,
409 vault_index: u8,
410 num_ephemeral_signers: u8,
411 message: &TransactionMessage,
412 memo: Option<String>,
413 program_id: Option<Pubkey>,
414) -> Instruction {
415 let args = VaultTransactionCreateArgs {
416 vault_index,
417 ephemeral_signers: num_ephemeral_signers,
418 transaction_message: message.try_to_vec().unwrap(),
419 memo,
420 };
421
422 Instruction {
423 accounts: accounts.to_account_metas(Some(false)),
424 data: VaultTransactionCreateData { args }.data(),
425 program_id: program_id.unwrap_or(squads_multisig_program::ID),
426 }
427}
428
429/// Executes a vault transaction.
430/// Example:
431/// ```
432/// use squads_multisig::anchor_lang::AnchorSerialize;
433/// use squads_multisig::solana_program::pubkey::Pubkey;
434/// use squads_multisig::solana_program::{system_instruction, system_program};
435/// use squads_multisig::client::{
436/// VaultTransactionExecuteAccounts,
437/// vault_transaction_execute
438/// };
439/// use squads_multisig::pda::get_vault_pda;
440/// use squads_multisig::vault_transaction::VaultTransactionMessageExt;
441/// use squads_multisig_program::TransactionMessage;
442///
443/// let multisig = Pubkey::new_unique();
444/// let vault_index = 0;
445/// let vault_pda = get_vault_pda(&multisig, vault_index, None).0;
446///
447/// // Create a vault transaction that includes 1 instruction - SOL transfer from the default vault.
448/// let message = TransactionMessage::try_compile(
449/// &vault_pda,
450/// &[system_instruction::transfer(&vault_pda, &Pubkey::new_unique(), 1_000_000)],
451/// &[]
452/// ).unwrap();
453///
454/// let ix = vault_transaction_execute(
455/// VaultTransactionExecuteAccounts {
456/// multisig,
457/// transaction: Pubkey::new_unique(),
458/// member: Pubkey::new_unique(),
459/// proposal: Pubkey::new_unique(),
460/// },
461/// 0,
462/// 0,
463/// &message,
464/// &[],
465/// None,
466/// );
467///
468/// ```
469pub fn vault_transaction_execute(
470 accounts: VaultTransactionExecuteAccounts,
471 vault_index: u8,
472 num_ephemeral_signers: u8,
473 message: &TransactionMessage,
474 address_lookup_table_accounts: &[AddressLookupTableAccount],
475 program_id: Option<Pubkey>,
476) -> ClientResult<Instruction> {
477 let program_id = program_id.unwrap_or(squads_multisig_program::ID);
478
479 let vault_pda = get_vault_pda(&accounts.multisig, vault_index, Some(&program_id)).0;
480
481 let accounts_for_execute = message
482 .get_accounts_for_execute(
483 &vault_pda,
484 &accounts.transaction,
485 &address_lookup_table_accounts,
486 num_ephemeral_signers,
487 &program_id,
488 )
489 .map_err(|err| match err {
490 Error::InvalidAddressLookupTableAccount => {
491 ClientError::InvalidAddressLookupTableAccount
492 }
493 Error::InvalidTransactionMessage => ClientError::InvalidTransactionMessage,
494 })?;
495
496 let mut accounts = accounts.to_account_metas(Some(false));
497 // Append the accounts required for executing the inner instructions.
498 accounts.extend(accounts_for_execute.into_iter());
499
500 Ok(Instruction {
501 accounts,
502 data: VaultTransactionExecuteData {}.data(),
503 program_id,
504 })
505}
506
507/// Closes a `ConfigTransaction` and the corresponding `Proposal`.
508/// `transaction` can be closed if either:
509/// - the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.
510/// - the `proposal` is stale.
511///
512/// Example:
513/// ```
514/// use squads_multisig::solana_program::{pubkey::Pubkey, system_program};
515/// use squads_multisig::client::{
516/// ConfigTransactionAccountsCloseAccounts,
517/// config_transaction_accounts_close
518/// };
519///
520/// let ix = config_transaction_accounts_close(
521/// ConfigTransactionAccountsCloseAccounts {
522/// multisig: Pubkey::new_unique(),
523/// proposal: Pubkey::new_unique(),
524/// transaction: Pubkey::new_unique(),
525/// rent_collector: Pubkey::new_unique(),
526/// system_program: system_program::id(),
527/// },
528/// None,
529/// );
530pub fn config_transaction_accounts_close(
531 accounts: ConfigTransactionAccountsCloseAccounts,
532 program_id: Option<Pubkey>,
533) -> Instruction {
534 Instruction {
535 accounts: accounts.to_account_metas(Some(false)),
536 data: ConfigTransactionAccountsCloseData {}.data(),
537 program_id: program_id.unwrap_or(squads_multisig_program::ID),
538 }
539}
540
541/// Closes a `VaultTransaction` and the corresponding `Proposal`.
542/// `transaction` can be closed if either:
543/// - the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.
544/// - the `proposal` is stale and not `Approved`.
545///
546/// Example:
547/// ```
548/// use squads_multisig::solana_program::{pubkey::Pubkey, system_program};
549/// use squads_multisig::client::{
550/// VaultTransactionAccountsCloseAccounts,
551/// vault_transaction_accounts_close
552/// };
553///
554/// let ix = vault_transaction_accounts_close(
555/// VaultTransactionAccountsCloseAccounts {
556/// multisig: Pubkey::new_unique(),
557/// proposal: Pubkey::new_unique(),
558/// transaction: Pubkey::new_unique(),
559/// rent_collector: Pubkey::new_unique(),
560/// system_program: system_program::id(),
561/// },
562/// None,
563/// );
564/// ```
565pub fn vault_transaction_accounts_close(
566 accounts: VaultTransactionAccountsCloseAccounts,
567 program_id: Option<Pubkey>,
568) -> Instruction {
569 Instruction {
570 accounts: accounts.to_account_metas(Some(false)),
571 data: VaultTransactionAccountsCloseData {}.data(),
572 program_id: program_id.unwrap_or(squads_multisig_program::ID),
573 }
574}
575
576pub mod utils {
577 use squads_multisig_program::accounts::{ConfigTransactionExecute, SpendingLimitUse};
578
579 use crate::solana_program::instruction::AccountMeta;
580 use crate::solana_program::pubkey::Pubkey;
581
582 /// A fix for the auto derived anchor `ToAccountMetas` trait.
583 /// The anchor one works incorrectly with Option accounts when program ID is different from the canonical one.
584 pub trait IntoAccountMetas {
585 fn into_account_metas(self, program_id: Pubkey) -> Vec<AccountMeta>;
586 }
587
588 impl IntoAccountMetas for ConfigTransactionExecute {
589 fn into_account_metas(self, program_id: Pubkey) -> Vec<AccountMeta> {
590 vec![
591 AccountMeta::new(self.multisig, false),
592 AccountMeta::new_readonly(self.member, true),
593 AccountMeta::new(self.proposal, false),
594 AccountMeta::new(self.transaction, false),
595 if let Some(rent_payer) = self.rent_payer {
596 AccountMeta::new(rent_payer, true)
597 } else {
598 AccountMeta::new_readonly(program_id, false)
599 },
600 if let Some(system_program) = self.system_program {
601 AccountMeta::new_readonly(system_program, false)
602 } else {
603 AccountMeta::new_readonly(program_id, false)
604 },
605 ]
606 }
607 }
608
609 impl IntoAccountMetas for SpendingLimitUse {
610 fn into_account_metas(self, program_id: Pubkey) -> Vec<AccountMeta> {
611 vec![
612 AccountMeta::new_readonly(self.multisig, false),
613 AccountMeta::new_readonly(self.member, true),
614 AccountMeta::new(self.spending_limit, false),
615 AccountMeta::new(self.vault, false),
616 AccountMeta::new(self.destination, false),
617 if let Some(system_program) = self.system_program {
618 AccountMeta::new_readonly(system_program, false)
619 } else {
620 AccountMeta::new_readonly(program_id, false)
621 },
622 if let Some(mint) = self.mint {
623 AccountMeta::new_readonly(mint, false)
624 } else {
625 AccountMeta::new_readonly(program_id, false)
626 },
627 if let Some(vault_token_account) = self.vault_token_account {
628 AccountMeta::new(vault_token_account, false)
629 } else {
630 AccountMeta::new_readonly(program_id, false)
631 },
632 if let Some(destination_token_account) = self.destination_token_account {
633 AccountMeta::new(destination_token_account, false)
634 } else {
635 AccountMeta::new_readonly(program_id, false)
636 },
637 if let Some(token_program) = self.token_program {
638 AccountMeta::new_readonly(token_program, false)
639 } else {
640 AccountMeta::new_readonly(program_id, false)
641 },
642 ]
643 }
644 }
645
646 #[cfg(test)]
647 mod test {
648 use crate::anchor_lang::prelude::Pubkey;
649 use crate::anchor_lang::ToAccountMetas;
650 use crate::client::utils::IntoAccountMetas;
651 use squads_multisig_program::accounts::SpendingLimitUse;
652
653 #[test]
654 fn spending_limit_use_into_account_metas_matches_anchor_implementation() {
655 let accounts = SpendingLimitUse {
656 multisig: Pubkey::new_unique(),
657 member: Pubkey::new_unique(),
658 spending_limit: Pubkey::new_unique(),
659 vault: Pubkey::new_unique(),
660 destination: Pubkey::new_unique(),
661 system_program: Some(Pubkey::new_unique()),
662 mint: Some(Pubkey::new_unique()),
663 vault_token_account: Some(Pubkey::new_unique()),
664 destination_token_account: Some(Pubkey::new_unique()),
665 token_program: Some(Pubkey::new_unique()),
666 };
667
668 // When program_id is the canonical one our implementation should match the anchor one.
669 let anchor_metas = accounts.to_account_metas(Some(false));
670 let sdk_metas = accounts.into_account_metas(squads_multisig_program::ID);
671
672 assert_eq!(anchor_metas, sdk_metas);
673 }
674
675 #[test]
676 fn config_transaction_execute_into_account_metas_matches_anchor_implementation() {
677 let accounts = squads_multisig_program::accounts::ConfigTransactionExecute {
678 multisig: Pubkey::new_unique(),
679 member: Pubkey::new_unique(),
680 proposal: Pubkey::new_unique(),
681 transaction: Pubkey::new_unique(),
682 rent_payer: Some(Pubkey::new_unique()),
683 system_program: Some(Pubkey::new_unique()),
684 };
685
686 // When program_id is the canonical one our implementation should match the anchor one.
687 let anchor_metas = accounts.to_account_metas(Some(false));
688 let sdk_metas = accounts.into_account_metas(squads_multisig_program::ID);
689
690 assert_eq!(anchor_metas, sdk_metas);
691 }
692 }
693}