use std::collections::HashMap;
use crate::{
error::LightSdkError,
instruction::system_accounts::{get_light_system_account_metas, SystemAccountMetaConfig},
AccountMeta, Pubkey,
};
#[derive(Default, Debug)]
pub struct PackedAccounts {
pub pre_accounts: Vec<AccountMeta>,
system_accounts: Vec<AccountMeta>,
next_index: u8,
map: HashMap<Pubkey, (u8, AccountMeta)>,
system_accounts_set: bool,
}
impl PackedAccounts {
pub fn new_with_system_accounts(config: SystemAccountMetaConfig) -> crate::error::Result<Self> {
let mut remaining_accounts = PackedAccounts::default();
remaining_accounts.add_system_accounts(config)?;
Ok(remaining_accounts)
}
pub fn system_accounts_set(&self) -> bool {
self.system_accounts_set
}
pub fn add_pre_accounts_signer(&mut self, pubkey: Pubkey) {
self.pre_accounts.push(AccountMeta {
pubkey,
is_signer: true,
is_writable: false,
});
}
pub fn add_pre_accounts_signer_mut(&mut self, pubkey: Pubkey) {
self.pre_accounts.push(AccountMeta {
pubkey,
is_signer: true,
is_writable: true,
});
}
pub fn add_pre_accounts_meta(&mut self, account_meta: AccountMeta) {
self.pre_accounts.push(account_meta);
}
pub fn add_pre_accounts_metas(&mut self, account_metas: &[AccountMeta]) {
self.pre_accounts.extend_from_slice(account_metas);
}
pub fn add_system_accounts(
&mut self,
config: SystemAccountMetaConfig,
) -> crate::error::Result<()> {
self.system_accounts
.extend(get_light_system_account_metas(config));
Ok(())
}
#[cfg(feature = "v2")]
pub fn add_system_accounts_v2(
&mut self,
config: SystemAccountMetaConfig,
) -> crate::error::Result<()> {
self.system_accounts
.extend(crate::instruction::get_light_system_account_metas_v2(
config,
));
Ok(())
}
pub fn insert_or_get(&mut self, pubkey: Pubkey) -> u8 {
self.insert_or_get_config(pubkey, false, true)
}
pub fn insert_or_get_read_only(&mut self, pubkey: Pubkey) -> u8 {
self.insert_or_get_config(pubkey, false, false)
}
pub fn insert_or_get_config(
&mut self,
pubkey: Pubkey,
is_signer: bool,
is_writable: bool,
) -> u8 {
match self.map.get_mut(&pubkey) {
Some((index, entry)) => {
if !entry.is_writable {
entry.is_writable = is_writable;
}
if !entry.is_signer {
entry.is_signer = is_signer;
}
*index
}
None => {
let index = self.next_index;
self.next_index += 1;
self.map.insert(
pubkey,
(
index,
AccountMeta {
pubkey,
is_signer,
is_writable,
},
),
);
index
}
}
}
fn hash_set_accounts_to_metas(&self) -> Vec<AccountMeta> {
let mut packed_accounts = self.map.iter().collect::<Vec<_>>();
packed_accounts.sort_by(|a, b| a.1 .0.cmp(&b.1 .0));
let packed_accounts = packed_accounts
.iter()
.map(|(_, (_, k))| k.clone())
.collect::<Vec<AccountMeta>>();
packed_accounts
}
fn get_offsets(&self) -> (usize, usize) {
let system_accounts_start_offset = self.pre_accounts.len();
let packed_accounts_start_offset =
system_accounts_start_offset + self.system_accounts.len();
(system_accounts_start_offset, packed_accounts_start_offset)
}
pub fn to_account_metas(&self) -> (Vec<AccountMeta>, usize, usize) {
let packed_accounts = self.hash_set_accounts_to_metas();
let (system_accounts_start_offset, packed_accounts_start_offset) = self.get_offsets();
(
[
self.pre_accounts.clone(),
self.system_accounts.clone(),
packed_accounts,
]
.concat(),
system_accounts_start_offset,
packed_accounts_start_offset,
)
}
pub fn packed_pubkeys(&self) -> Vec<Pubkey> {
self.hash_set_accounts_to_metas()
.iter()
.map(|meta| meta.pubkey)
.collect()
}
pub fn add_custom_system_accounts<T: AccountMetasVec>(
&mut self,
accounts: T,
) -> crate::error::Result<()> {
accounts.get_account_metas_vec(self)
}
}
pub trait AccountMetasVec {
fn get_account_metas_vec(&self, accounts: &mut PackedAccounts) -> Result<(), LightSdkError>;
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_remaining_accounts() {
let mut remaining_accounts = PackedAccounts::default();
let pubkey_1 = Pubkey::new_unique();
let pubkey_2 = Pubkey::new_unique();
let pubkey_3 = Pubkey::new_unique();
let pubkey_4 = Pubkey::new_unique();
assert_eq!(remaining_accounts.insert_or_get(pubkey_1), 0);
assert_eq!(remaining_accounts.insert_or_get(pubkey_2), 1);
assert_eq!(remaining_accounts.insert_or_get(pubkey_3), 2);
assert_eq!(
remaining_accounts.to_account_metas().0.as_slice(),
&[
AccountMeta {
pubkey: pubkey_1,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: pubkey_2,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: pubkey_3,
is_signer: false,
is_writable: true,
}
]
);
assert_eq!(remaining_accounts.insert_or_get(pubkey_1), 0);
assert_eq!(remaining_accounts.insert_or_get(pubkey_2), 1);
assert_eq!(remaining_accounts.insert_or_get(pubkey_3), 2);
assert_eq!(
remaining_accounts.to_account_metas().0.as_slice(),
&[
AccountMeta {
pubkey: pubkey_1,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: pubkey_2,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: pubkey_3,
is_signer: false,
is_writable: true,
}
]
);
assert_eq!(remaining_accounts.insert_or_get(pubkey_4), 3);
assert_eq!(
remaining_accounts.to_account_metas().0.as_slice(),
&[
AccountMeta {
pubkey: pubkey_1,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: pubkey_2,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: pubkey_3,
is_signer: false,
is_writable: true,
},
AccountMeta {
pubkey: pubkey_4,
is_signer: false,
is_writable: true,
}
]
);
}
}