use anchor_lang::{
prelude::borsh::{BorshDeserialize, BorshSerialize},
AccountDeserialize,
};
use gmsol_solana_utils::solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{message::VersionedMessage, pubkey::Pubkey};
use crate::squads::{
small_vec::SmallVec,
squads_multisig_v4::{
accounts::Multisig,
types::{
MultisigCompiledInstruction, MultisigMessageAddressTableLookup, VaultTransactionMessage,
},
},
};
pub(super) async fn get_multisig(
rpc_client: &RpcClient,
multisig_key: &Pubkey,
) -> crate::Result<Multisig> {
let multisig_account = rpc_client
.get_account(multisig_key)
.await
.map_err(crate::Error::custom)?;
let multisig = Multisig::try_deserialize(&mut multisig_account.data.as_slice())?;
Ok(multisig)
}
impl VaultTransactionMessage {
pub fn is_static_writable_index(&self, key_index: usize) -> bool {
let num_account_keys = self.account_keys.len();
let num_signers = usize::from(self.num_signers);
let num_writable_signers = usize::from(self.num_writable_signers);
let num_writable_non_signers = usize::from(self.num_writable_non_signers);
if key_index >= num_account_keys {
return false;
}
if key_index < num_writable_signers {
return true;
}
if key_index >= num_signers {
let index_into_non_signers = key_index.saturating_sub(num_signers);
return index_into_non_signers < num_writable_non_signers;
}
false
}
pub fn is_signer_index(&self, key_index: usize) -> bool {
key_index < usize::from(self.num_signers)
}
}
impl TryFrom<TransactionMessage> for VaultTransactionMessage {
type Error = crate::Error;
fn try_from(msg: TransactionMessage) -> Result<Self, Self::Error> {
Ok(Self {
num_signers: msg.num_signers,
num_writable_signers: msg.num_writable_signers,
num_writable_non_signers: msg.num_writable_non_signers,
account_keys: msg.account_keys.into(),
instructions: Vec::<CompiledInstruction>::from(msg.instructions)
.into_iter()
.map(|ix| MultisigCompiledInstruction {
program_id_index: ix.program_id_index,
account_indexes: ix.account_indexes.into(),
data: ix.data.into(),
})
.collect(),
address_table_lookups: Vec::<MessageAddressTableLookup>::from(
msg.address_table_lookups,
)
.into_iter()
.map(|atl| MultisigMessageAddressTableLookup {
account_key: atl.account_key,
writable_indexes: atl.writable_indexes.into(),
readonly_indexes: atl.readonly_indexes.into(),
})
.collect(),
})
}
}
#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub struct TransactionMessage {
pub num_signers: u8,
pub num_writable_signers: u8,
pub num_writable_non_signers: u8,
pub account_keys: SmallVec<u8, Pubkey>,
pub instructions: SmallVec<u8, CompiledInstruction>,
pub address_table_lookups: SmallVec<u8, MessageAddressTableLookup>,
}
#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub struct CompiledInstruction {
pub program_id_index: u8,
pub account_indexes: SmallVec<u8, u8>,
pub data: SmallVec<u16, u8>,
}
#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub struct MessageAddressTableLookup {
pub account_key: Pubkey,
pub writable_indexes: SmallVec<u8, u8>,
pub readonly_indexes: SmallVec<u8, u8>,
}
pub(super) fn versioned_message_to_transaction_message(
message: &VersionedMessage,
) -> TransactionMessage {
match message {
VersionedMessage::Legacy(message) => {
let num_accounts = message.account_keys.len() as u8;
let num_signers = message.header.num_required_signatures;
let num_non_signers = num_accounts - num_signers;
let instructions = message
.instructions
.iter()
.map(|ix| CompiledInstruction {
program_id_index: ix.program_id_index,
account_indexes: ix.accounts.clone().into(),
data: ix.data.clone().into(),
})
.collect::<Vec<_>>();
TransactionMessage {
num_signers,
num_writable_signers: num_signers - message.header.num_readonly_signed_accounts,
num_writable_non_signers: num_non_signers
- message.header.num_readonly_unsigned_accounts,
account_keys: message.account_keys.clone().into(),
instructions: instructions.into(),
address_table_lookups: Vec::default().into(),
}
}
VersionedMessage::V0(message) => {
let num_accounts = message.account_keys.len() as u8;
let num_signers = message.header.num_required_signatures;
let num_non_signers = num_accounts - num_signers;
let instructions = message
.instructions
.iter()
.map(|ix| CompiledInstruction {
program_id_index: ix.program_id_index,
account_indexes: ix.accounts.clone().into(),
data: ix.data.clone().into(),
})
.collect::<Vec<_>>();
let address_table_lookups = message
.address_table_lookups
.iter()
.map(|atl| MessageAddressTableLookup {
account_key: atl.account_key,
writable_indexes: atl.writable_indexes.clone().into(),
readonly_indexes: atl.readonly_indexes.clone().into(),
})
.collect::<Vec<_>>();
TransactionMessage {
num_signers,
num_writable_signers: num_signers - message.header.num_readonly_signed_accounts,
num_writable_non_signers: num_non_signers
- message.header.num_readonly_unsigned_accounts,
account_keys: message.account_keys.clone().into(),
instructions: instructions.into(),
address_table_lookups: address_table_lookups.into(),
}
}
}
}