use crate::{
account_set::{
account::discriminant::AccountDiscriminant,
modifiers::{HasOwnerProgram, HasSeeds, OwnerProgramDiscriminant},
ClientAccountSet,
},
instruction::InstructionDiscriminant,
prelude::*,
unsize::{init::UnsizedInit, FromOwned},
ErrorCode,
};
use borsh::{object_length, BorshSerialize};
use bytemuck::bytes_of;
use solana_instruction::Instruction as SolanaInstruction;
#[doc(hidden)]
pub fn star_frame_instruction_data<S, I>(data: &I) -> Result<Vec<u8>>
where
S: InstructionSet,
I: InstructionDiscriminant<S> + BorshSerialize,
{
let data_len = std::mem::size_of::<S::Discriminant>() + object_length(data)?;
let mut ix_data = Vec::with_capacity(data_len);
ix_data.extend_from_slice(bytes_of(&I::DISCRIMINANT));
BorshSerialize::serialize(data, &mut ix_data)?;
Ok(ix_data)
}
pub trait MakeInstruction: StarFrameProgram {
fn instruction<I, A>(data: &I, accounts: A::ClientAccounts) -> Result<SolanaInstruction>
where
I: StarFrameInstruction<Accounts<'static, 'static> = A>
+ InstructionDiscriminant<Self::InstructionSet>
+ BorshSerialize,
A: ClientAccountSet,
{
let mut metas = Vec::with_capacity(A::MIN_LEN);
A::extend_account_metas(&Self::ID, &accounts, &mut metas);
let data = star_frame_instruction_data::<Self::InstructionSet, I>(data)?;
Ok(SolanaInstruction {
program_id: Self::ID,
accounts: metas,
data,
})
}
}
impl<T> MakeInstruction for T where T: StarFrameProgram + ?Sized {}
pub trait FindProgramAddress: HasSeeds + HasOwnerProgram {
fn find_program_address(seeds: &Self::Seeds) -> (Pubkey, u8) {
Pubkey::find_program_address(&seeds.seeds(), &Self::OwnerProgram::ID)
}
fn create_program_address(seeds: &Self::Seeds, bump: u8) -> Result<Pubkey> {
let mut seeds = seeds.seeds();
let bump = &[bump];
seeds.push(bump);
Ok(Pubkey::create_program_address(
&seeds,
&Self::OwnerProgram::ID,
)?)
}
}
impl<T> FindProgramAddress for T where T: HasSeeds + HasOwnerProgram {}
pub trait DeserializeType: UnsizedType {
fn deserialize_type(data: &[u8]) -> Result<Self::Owned> {
Self::owned(data)
}
}
impl<T: UnsizedType + ?Sized> DeserializeType for T {}
pub trait SerializeType: UnsizedType {
fn serialize_type(owned: Self::Owned) -> Result<Vec<u8>>
where
Self: FromOwned,
{
let byte_size = Self::byte_size(&owned);
let mut bytes = vec![0u8; byte_size];
Self::from_owned(owned, &mut bytes.as_mut_slice())?;
Ok(bytes)
}
fn serialize_type_from_init<I>(init_arg: I) -> Result<Vec<u8>>
where
Self: UnsizedInit<I>,
{
let mut bytes = vec![0u8; <Self as UnsizedInit<I>>::INIT_BYTES];
let data = &mut &mut bytes[..];
<Self as UnsizedInit<I>>::init(data, init_arg)?;
Ok(bytes)
}
}
impl<T> SerializeType for T where T: UnsizedType + ?Sized {}
#[inline]
fn check_discriminant<T: ProgramAccount + ?Sized>(data: &[u8]) -> Result<()> {
let discriminant_bytes = data.get(0..size_of_val(&T::DISCRIMINANT)).ok_or_else(|| {
error!(
ErrorCode::DiscriminantMismatch,
"Not enough bytes for the discriminant"
)
})?;
let expected_discriminant = &T::DISCRIMINANT;
ensure_eq!(
discriminant_bytes,
bytes_of(expected_discriminant),
ErrorCode::DiscriminantMismatch,
);
Ok(())
}
pub trait DeserializeAccount: UnsizedType + ProgramAccount {
fn deserialize_account(data: &[u8]) -> Result<Self::Owned> {
check_discriminant::<Self>(data)
.ctx("Failed to validate the discriminant in DeserializeAccount")?;
<AccountDiscriminant<Self> as DeserializeType>::deserialize_type(data)
}
}
impl<T> DeserializeAccount for T where T: UnsizedType + ProgramAccount + ?Sized {}
pub trait DeserializeBorshAccount: BorshDeserialize + ProgramAccount {
fn deserialize_account(data: &[u8]) -> Result<Self> {
check_discriminant::<Self>(data)
.ctx("Failed to validate the discriminant in DeserializeBorshAccount")?;
let data = &data[size_of::<OwnerProgramDiscriminant<Self>>()..];
BorshDeserialize::try_from_slice(data).map_err(Into::into)
}
}
impl<T> DeserializeBorshAccount for T where T: BorshDeserialize + ProgramAccount {}
pub trait SerializeAccount: UnsizedType + ProgramAccount {
#[inline]
fn serialize_account(owned: Self::Owned) -> Result<Vec<u8>>
where
Self: FromOwned,
{
<AccountDiscriminant<Self> as SerializeType>::serialize_type(owned)
}
#[inline]
fn serialize_account_from_init<I>(init_arg: I) -> Result<Vec<u8>>
where
AccountDiscriminant<Self>: UnsizedInit<I>,
{
<AccountDiscriminant<Self> as SerializeType>::serialize_type_from_init(init_arg)
}
}
impl<T> SerializeAccount for T where T: UnsizedType + ProgramAccount + ?Sized {}
pub trait SerializeBorshAccount: BorshSerialize + ProgramAccount {
fn serialize_account(data: &Self) -> Result<Vec<u8>> {
let mut bytes =
vec![0u8; size_of::<OwnerProgramDiscriminant<Self>>() + object_length(data)?];
let (discriminant_bytes, mut data_bytes) =
bytes.split_at_mut(size_of::<OwnerProgramDiscriminant<Self>>());
discriminant_bytes.copy_from_slice(bytes_of(&Self::DISCRIMINANT));
BorshSerialize::serialize(data, &mut data_bytes)?;
Ok(bytes)
}
}
impl<T> SerializeBorshAccount for T where T: BorshSerialize + ProgramAccount + ?Sized {}