Skip to content

Commit 1924abc

Browse files
committed
feat(sdk-rs): add client::vault_transaction_execute
1 parent acd8d4f commit 1924abc

8 files changed

Lines changed: 301 additions & 33 deletions

File tree

Cargo.lock

Lines changed: 21 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/rs/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ keywords = ["solana", "multisig", "anchor", "squads", "cpi"]
1414
name = "squads_multisig"
1515

1616
[dependencies]
17+
futures = { version = "0.3.28" , features = ["async-await", "alloc"] }
1718
squads-multisig-program = { path = "../../programs/squads_multisig_program", features =["cpi", "no-entrypoint"], version = "0.2.0" }
1819
solana-client = "1.14.16"
20+
solana-address-lookup-table-program = "1.14.16"
1921
thiserror = "1.0.48"
2022

2123
[features]

sdk/rs/src/client.rs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub use squads_multisig_program::accounts::ProposalCreate as ProposalCreateAccou
77
pub use squads_multisig_program::accounts::ProposalVote as ProposalVoteAccounts;
88
pub use squads_multisig_program::accounts::SpendingLimitUse as SpendingLimitUseAccounts;
99
pub use squads_multisig_program::accounts::VaultTransactionCreate as VaultTransactionCreateAccounts;
10+
pub use squads_multisig_program::accounts::VaultTransactionExecute as VaultTransactionExecuteAccounts;
1011
use squads_multisig_program::anchor_lang::AnchorSerialize;
1112
pub use squads_multisig_program::instruction::ConfigTransactionCreate as ConfigTransactionCreateData;
1213
pub use squads_multisig_program::instruction::ConfigTransactionExecute as ConfigTransactionExecuteData;
@@ -15,13 +16,14 @@ pub use squads_multisig_program::instruction::ProposalApprove as ProposalApprove
1516
pub use squads_multisig_program::instruction::ProposalCreate as ProposalCreateData;
1617
pub use squads_multisig_program::instruction::SpendingLimitUse as SpendingLimitUseData;
1718
pub use squads_multisig_program::instruction::VaultTransactionCreate as VaultTransactionCreateData;
19+
pub use squads_multisig_program::instruction::VaultTransactionExecute as VaultTransactionExecuteData;
1820
pub use squads_multisig_program::instructions::ConfigTransactionCreateArgs;
1921
pub use squads_multisig_program::instructions::MultisigCreateArgs;
2022
pub use squads_multisig_program::instructions::ProposalCreateArgs;
2123
pub use squads_multisig_program::instructions::ProposalVoteArgs;
2224
pub use squads_multisig_program::instructions::SpendingLimitUseArgs;
2325
pub use squads_multisig_program::instructions::VaultTransactionCreateArgs;
24-
use squads_multisig_program::TransactionMessage;
26+
use squads_multisig_program::{TransactionMessage, VaultTransaction};
2527

2628
use crate::anchor_lang::prelude::Pubkey;
2729
use crate::anchor_lang::AccountDeserialize;
@@ -30,9 +32,11 @@ use crate::anchor_lang::{
3032
};
3133
use crate::error::ClientError;
3234
use crate::pda::get_vault_pda;
35+
use crate::solana_program::address_lookup_table_account::AddressLookupTableAccount;
3336
use crate::solana_program::instruction::AccountMeta;
3437
use crate::state::{Multisig, SpendingLimit};
35-
use crate::vault_transaction_message::VaultTransactionMessageExt;
38+
39+
use crate::vault_transaction::{AccountsForExecute, Error, VaultTransactionMessageExt};
3640
use crate::ClientResult;
3741

3842
/// Gets a `Multisig` account from the chain.
@@ -324,7 +328,6 @@ pub fn spending_limit_use(
324328
/// vault_transaction_create,
325329
/// };
326330
/// use squads_multisig::pda::get_vault_pda;
327-
/// use squads_multisig::vault_transaction_message::VaultTransactionMessageExt;
328331
/// use squads_multisig_program::TransactionMessage;
329332
///
330333
/// let multisig = Pubkey::new_unique();
@@ -375,3 +378,71 @@ pub fn vault_transaction_create(
375378
program_id: program_id.unwrap_or(squads_multisig_program::ID),
376379
}
377380
}
381+
382+
/// Executes a vault transaction.
383+
/// Example:
384+
/// ```
385+
/// use solana_client::nonblocking::rpc_client::RpcClient;
386+
/// use squads_multisig::anchor_lang::AnchorSerialize;
387+
/// use squads_multisig::solana_program::pubkey::Pubkey;
388+
/// use squads_multisig::solana_program::{system_instruction, system_program};
389+
/// use squads_multisig::client::{
390+
/// VaultTransactionExecuteAccounts,
391+
/// vault_transaction_execute
392+
/// };
393+
///
394+
/// let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string());
395+
///
396+
/// let future = vault_transaction_execute(
397+
/// &rpc_client,
398+
/// VaultTransactionExecuteAccounts {
399+
/// multisig: Pubkey::new_unique(),
400+
/// transaction: Pubkey::new_unique(),
401+
/// member: Pubkey::new_unique(),
402+
/// proposal: Pubkey::new_unique(),
403+
/// },
404+
/// 0,
405+
/// None,
406+
/// );
407+
///
408+
/// // TODO: await the `future`.
409+
/// ```
410+
pub async fn vault_transaction_execute(
411+
rpc_client: &RpcClient,
412+
accounts: VaultTransactionExecuteAccounts,
413+
vault_index: u8,
414+
program_id: Option<Pubkey>,
415+
) -> ClientResult<(Instruction, Vec<AddressLookupTableAccount>)> {
416+
let vault_pda = get_vault_pda(&accounts.multisig, vault_index, None).0;
417+
418+
let transaction_account_data = rpc_client.get_account_data(&accounts.transaction).await?;
419+
let transaction_account =
420+
VaultTransaction::try_deserialize(&mut transaction_account_data.as_slice())
421+
.map_err(|_| ClientError::DeserializationError)?;
422+
423+
let accounts_for_execute = AccountsForExecute::load(
424+
rpc_client,
425+
&vault_pda,
426+
&accounts.transaction,
427+
&transaction_account.message,
428+
&transaction_account.ephemeral_signer_bumps,
429+
&program_id.unwrap_or(squads_multisig_program::ID),
430+
)
431+
.await
432+
.map_err(|err| match err {
433+
Error::FailedToLoadAccount => ClientError::AccountNotFound,
434+
})?;
435+
436+
let mut accounts = accounts.to_account_metas(Some(false));
437+
// Append the accounts required for executing the inner instructions.
438+
accounts.extend(accounts_for_execute.account_metas.into_iter());
439+
440+
Ok((
441+
Instruction {
442+
accounts,
443+
data: VaultTransactionExecuteData {}.data(),
444+
program_id: program_id.unwrap_or(squads_multisig_program::ID),
445+
},
446+
accounts_for_execute.lookup_table_accounts,
447+
))
448+
}

sdk/rs/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub use squads_multisig_program::anchor_lang::solana_program;
77

88
pub mod client;
99
pub mod pda;
10-
pub mod vault_transaction_message;
10+
pub mod vault_transaction;
1111

1212
pub mod error {
1313
use thiserror::Error;
@@ -16,9 +16,10 @@ pub mod error {
1616
pub enum ClientError {
1717
#[error(transparent)]
1818
Client(#[from] solana_client::client_error::ClientError),
19-
2019
#[error("Failed to deserialize account data")]
2120
DeserializationError,
21+
#[error("Failed to load account")]
22+
AccountNotFound,
2223
}
2324
}
2425

sdk/rs/src/pda.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use squads_multisig_program::{
2-
SEED_MULTISIG, SEED_PREFIX, SEED_PROPOSAL, SEED_SPENDING_LIMIT, SEED_TRANSACTION, SEED_VAULT,
2+
SEED_EPHEMERAL_SIGNER, SEED_MULTISIG, SEED_PREFIX, SEED_PROPOSAL, SEED_SPENDING_LIMIT,
3+
SEED_TRANSACTION, SEED_VAULT,
34
};
45

56
use crate::solana_program::pubkey::Pubkey;
@@ -75,3 +76,19 @@ pub fn get_spending_limit_pda(
7576
program_id.unwrap_or(&squads_multisig_program::ID),
7677
)
7778
}
79+
80+
pub fn get_ephemeral_signer_pda(
81+
transaction_pda: &Pubkey,
82+
ephemeral_signer_index: u8,
83+
program_id: Option<&Pubkey>,
84+
) -> (Pubkey, u8) {
85+
Pubkey::find_program_address(
86+
&[
87+
SEED_PREFIX,
88+
&transaction_pda.to_bytes(),
89+
SEED_EPHEMERAL_SIGNER,
90+
&ephemeral_signer_index.to_le_bytes(),
91+
],
92+
program_id.unwrap_or(&squads_multisig_program::ID),
93+
)
94+
}

sdk/rs/src/vault_transaction_message/compiled_keys.rs renamed to sdk/rs/src/vault_transaction/compiled_keys.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,20 @@ impl CompiledKeys {
7373
.chain(
7474
key_meta_map
7575
.iter()
76-
.filter_map(|(key, meta)| (meta.is_signer && meta.is_writable).then(|| *key)),
76+
.filter_map(|(key, meta)| (meta.is_signer && meta.is_writable).then_some(*key)),
7777
)
7878
.collect();
7979
let readonly_signer_keys: Vec<Pubkey> = key_meta_map
8080
.iter()
81-
.filter_map(|(key, meta)| (meta.is_signer && !meta.is_writable).then(|| *key))
81+
.filter_map(|(key, meta)| (meta.is_signer && !meta.is_writable).then_some(*key))
8282
.collect();
8383
let writable_non_signer_keys: Vec<Pubkey> = key_meta_map
8484
.iter()
85-
.filter_map(|(key, meta)| (!meta.is_signer && meta.is_writable).then(|| *key))
85+
.filter_map(|(key, meta)| (!meta.is_signer && meta.is_writable).then_some(*key))
8686
.collect();
8787
let readonly_non_signer_keys: Vec<Pubkey> = key_meta_map
8888
.iter()
89-
.filter_map(|(key, meta)| (!meta.is_signer && !meta.is_writable).then(|| *key))
89+
.filter_map(|(key, meta)| (!meta.is_signer && !meta.is_writable).then_some(*key))
9090
.collect();
9191

9292
let signers_len = writable_signer_keys
@@ -153,7 +153,7 @@ impl CompiledKeys {
153153
for search_key in self
154154
.key_meta_map
155155
.iter()
156-
.filter_map(|(key, meta)| key_meta_filter(meta).then(|| key))
156+
.filter_map(|(key, meta)| key_meta_filter(meta).then_some(key))
157157
{
158158
for (key_index, key) in lookup_table_addresses.iter().enumerate() {
159159
if key == search_key {

0 commit comments

Comments
 (0)