Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: rest of accounts reference #1231

Merged
2 changes: 1 addition & 1 deletion lang/attribute/account/src/lib.rs
Expand Up @@ -56,7 +56,7 @@ mod id;
/// To facilitate this, all fields in an account must be constrained to be
/// "plain old data", i.e., they must implement
/// [`Pod`](../bytemuck/trait.Pod.html). Please review the
/// [`safety`](file:///home/armaniferrante/Documents/code/src/github.com/project-serum/anchor/target/doc/bytemuck/trait.Pod.html#safety)
/// [`safety`](../bytemuck/trait.Pod.html#safety)
/// section before using.
#[proc_macro_attribute]
pub fn account(
Expand Down
115 changes: 114 additions & 1 deletion lang/src/accounts/account.rs
Expand Up @@ -13,6 +13,13 @@ use std::ops::{Deref, DerefMut};
/// Wrapper around [`AccountInfo`](crate::solana_program::account_info::AccountInfo)
/// that verifies program ownership and deserializes underlying data into a Rust type.
///
/// # Table of Contents
/// - [Basic Functionality](#basic-functionality)
/// - [Using Account with non-anchor types](#using-account-with-non-anchor-types)
/// - [Out of the box wrapper types](#out-of-the-box-wrapper-types)
///
/// # Basic Functionality
///
/// Account checks that `Account.info.owner == T::owner()`.
/// This means that the data type that Accounts wraps around (`=T`) needs to
/// implement the [Owner trait](crate::Owner).
Expand Down Expand Up @@ -79,7 +86,7 @@ use std::ops::{Deref, DerefMut};
/// functions `#[account]` generates. See the example below for the code you have
/// to write.
///
/// The mint wrapper type Anchor provides out of the box for the token program ([source](https://github.com/project-serum/anchor/blob/master/spl/src/token.rs))
/// The mint wrapper type that Anchor provides out of the box for the token program ([source](https://github.com/project-serum/anchor/blob/master/spl/src/token.rs))
/// ```ignore
/// #[derive(Clone)]
/// pub struct Mint(spl_token::state::Mint);
Expand Down Expand Up @@ -121,6 +128,96 @@ use std::ops::{Deref, DerefMut};
/// }
/// }
/// ```
///
/// ## Out of the box wrapper types
///
/// ### Accessing BPFUpgradeableLoader Data
///
/// Anchor provides wrapper types to access data stored in programs owned by the BPFUpgradeableLoader
/// such as the upgrade authority. If you're interested in the data of a program account, you can use
/// ```ignore
/// Account<'info, BpfUpgradeableLoaderState>
/// ```
/// and then match on its contents inside your instruction function.
///
/// Alternatively, you can use
/// ```ignore
/// Account<'info, ProgramData>
/// ```
/// to let anchor do the matching for you and return the ProgramData variant of BpfUpgradeableLoaderState.
///
/// # Example
/// ```ignore
/// use anchor_lang::prelude::*;
/// use crate::program::MyProgram;
///
/// declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e");
///
/// #[program]
/// pub mod my_program {
/// use super::*;
///
/// pub fn set_initial_admin(
/// ctx: Context<SetInitialAdmin>,
/// admin_key: Pubkey
/// ) -> ProgramResult {
/// ctx.accounts.admin_settings.admin_key = admin_key;
/// Ok(())
/// }
///
/// pub fn set_admin(...){...}
///
/// pub fn set_settings(...){...}
/// }
///
/// #[account]
/// #[derive(Default, Debug)]
/// pub struct AdminSettings {
/// admin_key: Pubkey
/// }
///
/// #[derive(Accounts)]
/// pub struct SetInitialAdmin<'info> {
/// #[account(init, payer = authority, seeds = [b"admin"], bump)]
/// pub admin_settings: Account<'info, AdminSettings>,
/// #[account(mut)]
/// pub authority: Signer<'info>,
/// #[account(constraint = program.programdata_address() == Some(program_data.key()))]
/// pub program: Program<'info, MyProgram>,
/// #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
/// pub program_data: Account<'info, ProgramData>,
/// pub system_program: Program<'info, System>,
/// }
/// ```
///
/// This example solves a problem you may face if your program has admin settings: How do you set the
/// admin key for restricted functionality after deployment? Setting the admin key itself should
/// be a restricted action but how do you restrict it without having set an admin key?
/// You're stuck in a loop.
/// One solution is to use the upgrade authority of the program as the initial
/// (or permanent) admin key.
///
/// ### SPL Types
///
/// Anchor provides wrapper types to access accounts owned by the token program. Use
/// ```ignore
/// use anchor_spl::token::TokenAccount;
///
/// #[derive(Accounts)]
/// pub struct Example {
/// pub my_acc: Account<'info, TokenAccount>
/// }
/// ```
/// to access token accounts and
/// ```ignore
/// use anchor_spl::token::Mint;
///
/// #[derive(Accounts)]
/// pub struct Example {
/// pub my_acc: Account<'info, Mint>
/// }
/// ```
/// to access mint accounts.
#[derive(Clone)]
pub struct Account<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> {
account: T,
Expand Down Expand Up @@ -186,6 +283,22 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Account<'a, T
self.account
}

/// Sets the inner account.
///
/// Instead of this:
/// ```ignore
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> ProgramResult {
/// (*ctx.accounts.user_to_create).name = new_user.name;
/// (*ctx.accounts.user_to_create).age = new_user.age;
/// (*ctx.accounts.user_to_create).address = new_user.address;
/// }
/// ```
/// You can do this:
/// ```ignore
/// pub fn new_user(ctx: Context<CreateUser>, new_user:User) -> ProgramResult {
/// ctx.accounts.user_to_create.set_inner(new_user);
/// }
/// ```
pub fn set_inner(&mut self, inner: T) {
self.account = inner;
}
Expand Down
4 changes: 4 additions & 0 deletions lang/src/accounts/account_info.rs
@@ -1,3 +1,7 @@
//! AccountInfo can be used as a type but
//! [Unchecked Account](crate::accounts::unchecked_account::UncheckedAccount)
//! should be used instead.

use crate::error::ErrorCode;
use crate::{Accounts, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas};
use solana_program::account_info::AccountInfo;
Expand Down
15 changes: 15 additions & 0 deletions lang/src/accounts/boxed.rs
@@ -1,3 +1,18 @@
//! Box<T> type to save stack space.
//!
//! Sometimes accounts are too large for the stack,
//! leading to stack violations.
//!
//! Boxing the account can help.
//!
//! # Example
//! ```ignore
//! #[derive(Accounts)]
//! pub struct Example {
//! pub my_acc: Box<Account<'info, MyData>>
//! }
//! ```

use crate::{Accounts, AccountsClose, AccountsExit, ToAccountInfos, ToAccountMetas};
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
Expand Down
80 changes: 73 additions & 7 deletions lang/src/accounts/loader_account.rs
@@ -1,3 +1,5 @@
//! Type facilitating on demand zero copy deserialization.

use crate::error::ErrorCode;
use crate::{
Accounts, AccountsClose, AccountsExit, Key, Owner, ToAccountInfo, ToAccountInfos,
Expand All @@ -15,17 +17,81 @@ use std::io::Write;
use std::marker::PhantomData;
use std::ops::DerefMut;

/// Account AccountLoader facilitating on demand zero copy deserialization.
/// Type facilitating on demand zero copy deserialization.
///
/// Note that using accounts in this way is distinctly different from using,
/// for example, the [`Account`](./struct.Account.html). Namely,
/// one must call `load`, `load_mut`, or `load_init`, before reading or writing
/// to the account. For more details on zero-copy-deserialization, see the
/// one must call
/// - `load_init` after initializing an account (this will ignore the missing
/// account discriminator that gets added only after the user's instruction code)
/// - `load` when the account is not mutable
/// - `load_mut` when the account is mutable
///
/// For more details on zero-copy-deserialization, see the
/// [`account`](./attr.account.html) attribute.
/// <p style=";padding:0.75em;border: 1px solid #ee6868">
/// <strong>⚠️ </strong> When using this type it's important to be mindful
/// of any calls to the <code>load</code> functions so as not to
/// induce a <code>RefCell</code> panic, especially when sharing accounts across CPI
/// boundaries. When in doubt, one should make sure all refs resulting from
/// a call to a <code>load</code> function are dropped before CPI.
/// This can be done explicitly by calling <code>drop(my_var)</code> or implicitly
/// by wrapping the code using the <code>Ref</code> in braces <code>{..}</code> or
/// moving it into its own function.
/// </p>
///
/// # Example
/// ```ignore
/// use anchor_lang::prelude::*;
///
/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
///
/// #[program]
/// pub mod bar {
/// use super::*;
///
/// pub fn create_bar(ctx: Context<CreateBar>, data: u64) -> ProgramResult {
/// let bar = &mut ctx.accounts.bar.load_init()?;
/// bar.authority = ctx.accounts.authority.key();
/// bar.data = data;
/// Ok(())
/// }
///
/// pub fn update_bar(ctx: Context<UpdateBar>, data: u64) -> ProgramResult {
/// (*ctx.accounts.bar.load_mut()?).data = data;
/// Ok(())
/// }
/// }
///
/// #[account(zero_copy)]
/// #[derive(Default)]
/// pub struct Bar {
/// authority: Pubkey,
/// data: u64
/// }
///
/// #[derive(Accounts)]
/// pub struct CreateBar<'info> {
/// #[account(
/// init,
/// payer = authority
/// )]
/// bar: AccountLoader<'info, Bar>,
/// #[account(mut)]
/// authority: Signer<'info>,
/// system_program: AccountInfo<'info>,
/// }
///
/// When using it's important to be mindful of any calls to `load` so as not to
/// induce a `RefCell` panic, especially when sharing accounts across CPI
/// boundaries. When in doubt, one should make sure all refs resulting from a
/// call to `load` are dropped before CPI.
/// #[derive(Accounts)]
/// pub struct UpdateBar<'info> {
/// #[account(
/// mut,
/// has_one = authority,
/// )]
/// pub bar: AccountLoader<'info, Bar>,
/// pub authority: Signer<'info>,
/// }
/// ```
#[derive(Clone)]
pub struct AccountLoader<'info, T: ZeroCopy + Owner> {
acc_info: AccountInfo<'info>,
Expand Down
49 changes: 48 additions & 1 deletion lang/src/accounts/program.rs
@@ -1,3 +1,5 @@
//! Type validating that the account is the given Program

use crate::error::ErrorCode;
use crate::*;
use solana_program::account_info::AccountInfo;
Expand All @@ -9,7 +11,52 @@ use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;

/// Account container that checks ownership on deserialization.
/// Type validating that the account is the given Program
///
/// The type has a `programdata_address` property that will be set
/// if the program is owned by the [`BPFUpgradeableLoader`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/index.html)
/// and will contain the `programdata_address` property of the `Program` variant of the [`UpgradeableLoaderState`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html) enum.
///
/// Checks:
///
/// - `Account.info.key == Program`
/// - `Account.info.executable == true`
///
/// # Example
/// ```ignore
///
/// #[program]
/// mod my_program {
/// fn set_admin_settings(...){...}
/// }
///
/// #[account]
/// #[derive(Default)]
/// pub struct AdminSettings {
/// ...
/// }
///
/// #[derive(Accounts)]
/// pub struct SetAdminSettings<'info> {
/// #[account(mut, seeds = [b"admin"], bump)]
/// pub admin_settings: Account<'info, AdminSettings>,
/// #[account(constraint = program.programdata_address() == Some(program_data.key()))]
/// pub program: Program<'info, MyProgram>,
/// #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))]
/// pub program_data: Account<'info, ProgramData>,
/// pub authority: Signer<'info>,
/// }
/// ```
/// The given program has a function with which the upgrade authority can set admin settings.
///
/// The required constraints are as follows:
///
/// - `program` is the account of the program itself.
/// Its constraint checks that `program_data` is the account that contains the program's upgrade authority.
/// Implicitly, this checks that `program` is a BPFUpgradeable program (`program.programdata_address()`
/// will be `None` if it's not).
/// - `program_data`'s constraint checks that its upgrade authority is the `authority` account.
/// - Finally, `authority` needs to sign the transaction.
#[derive(Clone)]
pub struct Program<'info, T: Id + Clone> {
info: AccountInfo<'info>,
Expand Down
25 changes: 25 additions & 0 deletions lang/src/accounts/signer.rs
@@ -1,3 +1,4 @@
//! Type validating that the account signed the transaction
use crate::error::ErrorCode;
use crate::*;
use solana_program::account_info::AccountInfo;
Expand All @@ -10,6 +11,30 @@ use std::ops::Deref;
/// Type validating that the account signed the transaction. No other ownership
/// or type checks are done. If this is used, one should not try to access the
/// underlying account data.
///
/// Checks:
///
/// - `Signer.info.is_signer == true`
///
/// # Example
/// ```ignore
/// #[account]
/// #[derive(Default)]
/// pub struct MyData {
/// pub data: u64
/// }
///
/// #[derive(Accounts)]
/// pub struct Example<'info> {
/// #[account(init, payer = payer)]
/// pub my_acc: Account<'info, MyData>,
/// #[account(mut)]
/// pub payer: Signer<'info>,
/// pub system_program: Program<'info, System>
/// }
/// ```
///
/// When creating an account with `init`, the `payer` needs to sign the transaction.
#[derive(Debug, Clone)]
pub struct Signer<'info> {
info: AccountInfo<'info>,
Expand Down
7 changes: 7 additions & 0 deletions lang/src/accounts/system_account.rs
@@ -1,3 +1,5 @@
//! Type validating that the account is owned by the system program

use crate::error::ErrorCode;
use crate::*;
use solana_program::account_info::AccountInfo;
Expand All @@ -8,6 +10,11 @@ use solana_program::pubkey::Pubkey;
use solana_program::system_program;
use std::ops::Deref;

/// Type validating that the account is owned by the system program
///
/// Checks:
///
/// - `SystemAccount.info.owner == SystemProgram`
#[derive(Debug, Clone)]
pub struct SystemAccount<'info> {
info: AccountInfo<'info>,
Expand Down