use hopper_runtime::{error::ProgramError, AccountView, Address, ProgramResult, Ref, RefMut};
pub struct Unresolved;
pub struct Resolved;
pub struct Validated;
pub struct Executed;
pub struct PhasedFrame<'a, P> {
program_id: &'a Address,
accounts: &'a [AccountView],
ix_data: &'a [u8],
mutable_borrows: u64,
_phase: core::marker::PhantomData<P>,
}
impl<'a> PhasedFrame<'a, Unresolved> {
#[inline(always)]
pub fn new(
program_id: &'a Address,
accounts: &'a [AccountView],
ix_data: &'a [u8],
) -> Result<Self, ProgramError> {
if accounts.len() > crate::frame::MAX_FRAME_ACCOUNTS {
return Err(ProgramError::InvalidArgument);
}
Ok(Self {
program_id,
accounts,
ix_data,
mutable_borrows: 0,
_phase: core::marker::PhantomData,
})
}
#[inline]
pub fn resolve<T, F>(
self,
min_accounts: usize,
f: F,
) -> Result<ResolvedFrame<'a, T>, ProgramError>
where
F: FnOnce(&'a [AccountView], &'a Address) -> Result<T, ProgramError>,
{
if self.accounts.len() < min_accounts {
return Err(ProgramError::NotEnoughAccountKeys);
}
let resolved = f(self.accounts, self.program_id)?;
Ok(ResolvedFrame {
program_id: self.program_id,
accounts: self.accounts,
ix_data: self.ix_data,
mutable_borrows: self.mutable_borrows,
resolved,
})
}
}
pub struct ResolvedFrame<'a, T> {
pub(crate) program_id: &'a Address,
pub(crate) accounts: &'a [AccountView],
pub(crate) ix_data: &'a [u8],
pub(crate) mutable_borrows: u64,
pub(crate) resolved: T,
}
impl<'a, T> ResolvedFrame<'a, T> {
#[inline(always)]
pub fn program_id(&self) -> &Address {
self.program_id
}
#[inline(always)]
pub fn ix_data(&self) -> &[u8] {
self.ix_data
}
#[inline(always)]
pub fn accounts(&self) -> &T {
&self.resolved
}
#[inline]
pub fn validate<F>(self, f: F) -> Result<ValidatedFrame<'a, T>, ProgramError>
where
F: FnOnce(&T, &Address) -> ProgramResult,
{
f(&self.resolved, self.program_id)?;
Ok(ValidatedFrame {
program_id: self.program_id,
accounts: self.accounts,
ix_data: self.ix_data,
mutable_borrows: self.mutable_borrows,
resolved: self.resolved,
})
}
}
pub struct ValidatedFrame<'a, T> {
pub(crate) program_id: &'a Address,
pub(crate) accounts: &'a [AccountView],
pub(crate) ix_data: &'a [u8],
pub(crate) mutable_borrows: u64,
pub(crate) resolved: T,
}
impl<'a, T> ValidatedFrame<'a, T> {
#[inline(always)]
pub fn program_id(&self) -> &Address {
self.program_id
}
#[inline(always)]
pub fn ix_data(&self) -> &[u8] {
self.ix_data
}
#[inline(always)]
pub fn accounts(&self) -> &T {
&self.resolved
}
#[inline]
pub fn execute<R, F>(mut self, f: F) -> Result<R, ProgramError>
where
F: FnOnce(&mut ExecutionContext<'a, '_, T>) -> Result<R, ProgramError>,
{
let mut ctx = ExecutionContext {
program_id: self.program_id,
accounts: self.accounts,
ix_data: self.ix_data,
mutable_borrows: &mut self.mutable_borrows,
resolved: &self.resolved,
};
f(&mut ctx)
}
}
pub struct ExecutionContext<'a, 'f, T> {
pub(crate) program_id: &'a Address,
pub(crate) accounts: &'a [AccountView],
pub(crate) ix_data: &'a [u8],
pub(crate) mutable_borrows: &'f mut u64,
pub(crate) resolved: &'f T,
}
impl<'a, 'f, T> ExecutionContext<'a, 'f, T> {
#[inline(always)]
pub fn program_id(&self) -> &'a Address {
self.program_id
}
#[inline(always)]
pub fn ix_data(&self) -> &'a [u8] {
self.ix_data
}
#[inline(always)]
pub fn resolved(&self) -> &T {
self.resolved
}
#[inline]
pub fn borrow_mut(&mut self, index: usize) -> Result<RefMut<'a, [u8]>, ProgramError> {
if index >= self.accounts.len() {
return Err(ProgramError::NotEnoughAccountKeys);
}
let bit = 1u64 << (index as u32);
if *self.mutable_borrows & bit != 0 {
return Err(ProgramError::AccountBorrowFailed);
}
*self.mutable_borrows |= bit;
match self.accounts[index].try_borrow_mut() {
Ok(data) => Ok(data),
Err(error) => {
*self.mutable_borrows &= !bit;
Err(error)
}
}
}
#[inline(always)]
pub fn borrow(&self, index: usize) -> Result<Ref<'a, [u8]>, ProgramError> {
if index >= self.accounts.len() {
return Err(ProgramError::NotEnoughAccountKeys);
}
self.accounts[index].try_borrow()
}
#[inline(always)]
pub fn account(&self, index: usize) -> Result<&'a AccountView, ProgramError> {
self.accounts
.get(index)
.ok_or(ProgramError::NotEnoughAccountKeys)
}
}