anchor/lang/src/context.rs

244 lines
7.5 KiB
Rust

//! Data structures that are used to provide non-argument inputs to program endpoints
use crate::{Accounts, ToAccountInfos, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
use std::collections::BTreeMap;
use std::fmt;
/// Provides non-argument inputs to the program.
///
/// # Example
/// ```ignore
/// pub fn set_data(ctx: Context<SetData>, age: u64, other_data: u32) -> Result<()> {
/// // Set account data like this
/// (*ctx.accounts.my_account).age = age;
/// (*ctx.accounts.my_account).other_data = other_data;
/// // or like this
/// let my_account = &mut ctx.account.my_account;
/// my_account.age = age;
/// my_account.other_data = other_data;
/// Ok(())
/// }
/// ```
pub struct Context<'a, 'b, 'c, 'info, T> {
/// Currently executing program id.
pub program_id: &'a Pubkey,
/// Deserialized accounts.
pub accounts: &'b mut T,
/// Remaining accounts given but not deserialized or validated.
/// Be very careful when using this directly.
pub remaining_accounts: &'c [AccountInfo<'info>],
/// Bump seeds found during constraint validation. This is provided as a
/// convenience so that handlers don't have to recalculate bump seeds or
/// pass them in as arguments.
pub bumps: BTreeMap<String, u8>,
}
impl<'a, 'b, 'c, 'info, T: fmt::Debug> fmt::Debug for Context<'a, 'b, 'c, 'info, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Context")
.field("program_id", &self.program_id)
.field("accounts", &self.accounts)
.field("remaining_accounts", &self.remaining_accounts)
.field("bumps", &self.bumps)
.finish()
}
}
impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> {
pub fn new(
program_id: &'a Pubkey,
accounts: &'b mut T,
remaining_accounts: &'c [AccountInfo<'info>],
bumps: BTreeMap<String, u8>,
) -> Self {
Self {
program_id,
accounts,
remaining_accounts,
bumps,
}
}
}
/// Context specifying non-argument inputs for cross-program-invocations.
///
/// # Example with and without PDA signature
/// ```ignore
/// // Callee Program
///
/// use anchor_lang::prelude::*;
///
/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
///
/// #[program]
/// pub mod callee {
/// use super::*;
/// pub fn init(ctx: Context<Init>) -> Result<()> {
/// (*ctx.accounts.data).authority = ctx.accounts.authority.key();
/// Ok(())
/// }
///
/// pub fn set_data(ctx: Context<SetData>, data: u64) -> Result<()> {
/// (*ctx.accounts.data_acc).data = data;
/// Ok(())
/// }
/// }
///
/// #[account]
/// #[derive(Default)]
/// pub struct Data {
/// data: u64,
/// authority: Pubkey,
/// }
///
/// #[derive(Accounts)]
/// pub struct Init<'info> {
/// #[account(init, payer = payer)]
/// pub data: Account<'info, Data>,
/// pub payer: Signer<'info>,
/// pub authority: UncheckedAccount<'info>,
/// pub system_program: Program<'info, System>
/// }
///
/// #[derive(Accounts)]
/// pub struct SetData<'info> {
/// #[account(mut, has_one = authority)]
/// pub data_acc: Account<'info, Data>,
/// pub authority: Signer<'info>,
/// }
///
/// // Caller Program
///
/// use anchor_lang::prelude::*;
/// use callee::{self, program::Callee};
///
/// declare_id!("Sxg7dBh5VLT8S1o6BqncZCPq9nhHHukjfVd6ohQJeAk");
///
/// #[program]
/// pub mod caller {
/// use super::*;
/// pub fn do_cpi(ctx: Context<DoCpi>, data: u64) -> Result<()> {
/// let callee_id = ctx.accounts.callee.to_account_info();
/// let callee_accounts = callee::cpi::accounts::SetData {
/// data_acc: ctx.accounts.data_acc.to_account_info(),
/// authority: ctx.accounts.callee_authority.to_account_info(),
/// };
/// let cpi_ctx = CpiContext::new(callee_id, callee_accounts);
/// callee::cpi::set_data(cpi_ctx, data)
/// }
///
/// pub fn do_cpi_with_pda_authority(ctx: Context<DoCpiWithPDAAuthority>, bump: u8, data: u64) -> Result<()> {
/// let seeds = &[&[b"example_seed", bytemuck::bytes_of(&bump)][..]];
/// let callee_id = ctx.accounts.callee.to_account_info();
/// let callee_accounts = callee::cpi::accounts::SetData {
/// data_acc: ctx.accounts.data_acc.to_account_info(),
/// authority: ctx.accounts.callee_authority.to_account_info(),
/// };
/// let cpi_ctx = CpiContext::new_with_signer(callee_id, callee_accounts, seeds);
/// callee::cpi::set_data(cpi_ctx, data)
/// }
/// }
///
/// // We can use "UncheckedAccount"s here because
/// // the callee program does the checks.
/// // We use "mut" so the autogenerated clients know
/// // that this account should be mutable.
/// #[derive(Accounts)]
/// pub struct DoCpi<'info> {
/// #[account(mut)]
/// pub data_acc: UncheckedAccount<'info>,
/// pub callee_authority: UncheckedAccount<'info>,
/// pub callee: Program<'info, Callee>,
/// }
///
/// #[derive(Accounts)]
/// pub struct DoCpiWithPDAAuthority<'info> {
/// #[account(mut)]
/// pub data_acc: UncheckedAccount<'info>,
/// pub callee_authority: UncheckedAccount<'info>,
/// pub callee: Program<'info, Callee>,
/// }
/// ```
pub struct CpiContext<'a, 'b, 'c, 'info, T>
where
T: ToAccountMetas + ToAccountInfos<'info>,
{
pub accounts: T,
pub remaining_accounts: Vec<AccountInfo<'info>>,
pub program: AccountInfo<'info>,
pub signer_seeds: &'a [&'b [&'c [u8]]],
}
impl<'a, 'b, 'c, 'info, T> CpiContext<'a, 'b, 'c, 'info, T>
where
T: ToAccountMetas + ToAccountInfos<'info>,
{
pub fn new(program: AccountInfo<'info>, accounts: T) -> Self {
Self {
accounts,
program,
remaining_accounts: Vec::new(),
signer_seeds: &[],
}
}
#[must_use]
pub fn new_with_signer(
program: AccountInfo<'info>,
accounts: T,
signer_seeds: &'a [&'b [&'c [u8]]],
) -> Self {
Self {
accounts,
program,
signer_seeds,
remaining_accounts: Vec::new(),
}
}
#[must_use]
pub fn with_signer(mut self, signer_seeds: &'a [&'b [&'c [u8]]]) -> Self {
self.signer_seeds = signer_seeds;
self
}
#[must_use]
pub fn with_remaining_accounts(mut self, ra: Vec<AccountInfo<'info>>) -> Self {
self.remaining_accounts = ra;
self
}
}
impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountInfos<'info>
for CpiContext<'_, '_, '_, 'info, T>
{
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
let mut infos = self.accounts.to_account_infos();
infos.extend_from_slice(&self.remaining_accounts);
infos.push(self.program.clone());
infos
}
}
impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountMetas
for CpiContext<'_, '_, '_, 'info, T>
{
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
let mut metas = self.accounts.to_account_metas(is_signer);
metas.append(
&mut self
.remaining_accounts
.iter()
.map(|acc| match acc.is_writable {
false => AccountMeta::new_readonly(*acc.key, acc.is_signer),
true => AccountMeta::new(*acc.key, acc.is_signer),
})
.collect(),
);
metas
}
}