Skip to content

Commit

Permalink
feat(sdk-rs): add client::vault_transaction_execute
Browse files Browse the repository at this point in the history
  • Loading branch information
vovacodes committed Oct 19, 2023
1 parent acd8d4f commit 1924abc
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 33 deletions.
40 changes: 21 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions sdk/rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ keywords = ["solana", "multisig", "anchor", "squads", "cpi"]
name = "squads_multisig"

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

[features]
Expand Down
77 changes: 74 additions & 3 deletions sdk/rs/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub use squads_multisig_program::accounts::ProposalCreate as ProposalCreateAccou
pub use squads_multisig_program::accounts::ProposalVote as ProposalVoteAccounts;
pub use squads_multisig_program::accounts::SpendingLimitUse as SpendingLimitUseAccounts;
pub use squads_multisig_program::accounts::VaultTransactionCreate as VaultTransactionCreateAccounts;
pub use squads_multisig_program::accounts::VaultTransactionExecute as VaultTransactionExecuteAccounts;
use squads_multisig_program::anchor_lang::AnchorSerialize;
pub use squads_multisig_program::instruction::ConfigTransactionCreate as ConfigTransactionCreateData;
pub use squads_multisig_program::instruction::ConfigTransactionExecute as ConfigTransactionExecuteData;
Expand All @@ -15,13 +16,14 @@ pub use squads_multisig_program::instruction::ProposalApprove as ProposalApprove
pub use squads_multisig_program::instruction::ProposalCreate as ProposalCreateData;
pub use squads_multisig_program::instruction::SpendingLimitUse as SpendingLimitUseData;
pub use squads_multisig_program::instruction::VaultTransactionCreate as VaultTransactionCreateData;
pub use squads_multisig_program::instruction::VaultTransactionExecute as VaultTransactionExecuteData;
pub use squads_multisig_program::instructions::ConfigTransactionCreateArgs;
pub use squads_multisig_program::instructions::MultisigCreateArgs;
pub use squads_multisig_program::instructions::ProposalCreateArgs;
pub use squads_multisig_program::instructions::ProposalVoteArgs;
pub use squads_multisig_program::instructions::SpendingLimitUseArgs;
pub use squads_multisig_program::instructions::VaultTransactionCreateArgs;
use squads_multisig_program::TransactionMessage;
use squads_multisig_program::{TransactionMessage, VaultTransaction};

use crate::anchor_lang::prelude::Pubkey;
use crate::anchor_lang::AccountDeserialize;
Expand All @@ -30,9 +32,11 @@ use crate::anchor_lang::{
};
use crate::error::ClientError;
use crate::pda::get_vault_pda;
use crate::solana_program::address_lookup_table_account::AddressLookupTableAccount;
use crate::solana_program::instruction::AccountMeta;
use crate::state::{Multisig, SpendingLimit};
use crate::vault_transaction_message::VaultTransactionMessageExt;

use crate::vault_transaction::{AccountsForExecute, Error, VaultTransactionMessageExt};
use crate::ClientResult;

/// Gets a `Multisig` account from the chain.
Expand Down Expand Up @@ -324,7 +328,6 @@ pub fn spending_limit_use(
/// vault_transaction_create,
/// };
/// use squads_multisig::pda::get_vault_pda;
/// use squads_multisig::vault_transaction_message::VaultTransactionMessageExt;
/// use squads_multisig_program::TransactionMessage;
///
/// let multisig = Pubkey::new_unique();
Expand Down Expand Up @@ -375,3 +378,71 @@ pub fn vault_transaction_create(
program_id: program_id.unwrap_or(squads_multisig_program::ID),
}
}

/// Executes a vault transaction.
/// Example:
/// ```
/// use solana_client::nonblocking::rpc_client::RpcClient;
/// use squads_multisig::anchor_lang::AnchorSerialize;
/// use squads_multisig::solana_program::pubkey::Pubkey;
/// use squads_multisig::solana_program::{system_instruction, system_program};
/// use squads_multisig::client::{
/// VaultTransactionExecuteAccounts,
/// vault_transaction_execute
/// };
///
/// let rpc_client = RpcClient::new("https://api.devnet.solana.com".to_string());
///
/// let future = vault_transaction_execute(
/// &rpc_client,
/// VaultTransactionExecuteAccounts {
/// multisig: Pubkey::new_unique(),
/// transaction: Pubkey::new_unique(),
/// member: Pubkey::new_unique(),
/// proposal: Pubkey::new_unique(),
/// },
/// 0,
/// None,
/// );
///
/// // TODO: await the `future`.
/// ```
pub async fn vault_transaction_execute(
rpc_client: &RpcClient,
accounts: VaultTransactionExecuteAccounts,
vault_index: u8,
program_id: Option<Pubkey>,
) -> ClientResult<(Instruction, Vec<AddressLookupTableAccount>)> {
let vault_pda = get_vault_pda(&accounts.multisig, vault_index, None).0;

let transaction_account_data = rpc_client.get_account_data(&accounts.transaction).await?;
let transaction_account =
VaultTransaction::try_deserialize(&mut transaction_account_data.as_slice())
.map_err(|_| ClientError::DeserializationError)?;

let accounts_for_execute = AccountsForExecute::load(
rpc_client,
&vault_pda,
&accounts.transaction,
&transaction_account.message,
&transaction_account.ephemeral_signer_bumps,
&program_id.unwrap_or(squads_multisig_program::ID),
)
.await
.map_err(|err| match err {
Error::FailedToLoadAccount => ClientError::AccountNotFound,
})?;

let mut accounts = accounts.to_account_metas(Some(false));
// Append the accounts required for executing the inner instructions.
accounts.extend(accounts_for_execute.account_metas.into_iter());

Ok((
Instruction {
accounts,
data: VaultTransactionExecuteData {}.data(),
program_id: program_id.unwrap_or(squads_multisig_program::ID),
},
accounts_for_execute.lookup_table_accounts,
))
}
5 changes: 3 additions & 2 deletions sdk/rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub use squads_multisig_program::anchor_lang::solana_program;

pub mod client;
pub mod pda;
pub mod vault_transaction_message;
pub mod vault_transaction;

pub mod error {
use thiserror::Error;
Expand All @@ -16,9 +16,10 @@ pub mod error {
pub enum ClientError {
#[error(transparent)]
Client(#[from] solana_client::client_error::ClientError),

#[error("Failed to deserialize account data")]
DeserializationError,
#[error("Failed to load account")]
AccountNotFound,
}
}

Expand Down
19 changes: 18 additions & 1 deletion sdk/rs/src/pda.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use squads_multisig_program::{
SEED_MULTISIG, SEED_PREFIX, SEED_PROPOSAL, SEED_SPENDING_LIMIT, SEED_TRANSACTION, SEED_VAULT,
SEED_EPHEMERAL_SIGNER, SEED_MULTISIG, SEED_PREFIX, SEED_PROPOSAL, SEED_SPENDING_LIMIT,
SEED_TRANSACTION, SEED_VAULT,
};

use crate::solana_program::pubkey::Pubkey;
Expand Down Expand Up @@ -75,3 +76,19 @@ pub fn get_spending_limit_pda(
program_id.unwrap_or(&squads_multisig_program::ID),
)
}

pub fn get_ephemeral_signer_pda(
transaction_pda: &Pubkey,
ephemeral_signer_index: u8,
program_id: Option<&Pubkey>,
) -> (Pubkey, u8) {
Pubkey::find_program_address(
&[
SEED_PREFIX,
&transaction_pda.to_bytes(),
SEED_EPHEMERAL_SIGNER,
&ephemeral_signer_index.to_le_bytes(),
],
program_id.unwrap_or(&squads_multisig_program::ID),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,20 @@ impl CompiledKeys {
.chain(
key_meta_map
.iter()
.filter_map(|(key, meta)| (meta.is_signer && meta.is_writable).then(|| *key)),
.filter_map(|(key, meta)| (meta.is_signer && meta.is_writable).then_some(*key)),
)
.collect();
let readonly_signer_keys: Vec<Pubkey> = key_meta_map
.iter()
.filter_map(|(key, meta)| (meta.is_signer && !meta.is_writable).then(|| *key))
.filter_map(|(key, meta)| (meta.is_signer && !meta.is_writable).then_some(*key))
.collect();
let writable_non_signer_keys: Vec<Pubkey> = key_meta_map
.iter()
.filter_map(|(key, meta)| (!meta.is_signer && meta.is_writable).then(|| *key))
.filter_map(|(key, meta)| (!meta.is_signer && meta.is_writable).then_some(*key))
.collect();
let readonly_non_signer_keys: Vec<Pubkey> = key_meta_map
.iter()
.filter_map(|(key, meta)| (!meta.is_signer && !meta.is_writable).then(|| *key))
.filter_map(|(key, meta)| (!meta.is_signer && !meta.is_writable).then_some(*key))
.collect();

let signers_len = writable_signer_keys
Expand Down Expand Up @@ -153,7 +153,7 @@ impl CompiledKeys {
for search_key in self
.key_meta_map
.iter()
.filter_map(|(key, meta)| key_meta_filter(meta).then(|| key))
.filter_map(|(key, meta)| key_meta_filter(meta).then_some(key))
{
for (key_index, key) in lookup_table_addresses.iter().enumerate() {
if key == search_key {
Expand Down
Loading

0 comments on commit 1924abc

Please sign in to comment.