lang: Add some more docs
This commit is contained in:
parent
a903d48e1f
commit
82d7e5ae2b
|
@ -5,6 +5,143 @@ use heck::SnakeCase;
|
|||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// The `#[interface]` attribute allows one to define an external program
|
||||
/// dependency, without having any knowledge about the program, other than
|
||||
/// the fact that it implements the given trait.
|
||||
///
|
||||
/// Additionally, the attribute generates a client that can be used to perform
|
||||
/// CPI to these external dependencies.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// In the following example, we have a counter program, where the count
|
||||
/// can only be set if the configured external program authorizes it.
|
||||
///
|
||||
/// ## Defining an `#[interface]`
|
||||
///
|
||||
/// First we define the program that depends on an external interface.
|
||||
///
|
||||
/// ```ignore
|
||||
/// #![feature(proc_macro_hygiene)]
|
||||
///
|
||||
/// use anchor_lang::prelude::*;
|
||||
///
|
||||
/// #[interface]
|
||||
/// pub trait Auth<'info, T: Accounts<'info>> {
|
||||
/// fn is_authorized(ctx: Context<T>, current: u64, new: u64) -> ProgramResult;
|
||||
/// }
|
||||
///
|
||||
/// #[program]
|
||||
/// pub mod counter {
|
||||
/// use super::*;
|
||||
///
|
||||
/// #[state]
|
||||
/// pub struct Counter {
|
||||
/// pub count: u64,
|
||||
/// pub auth_program: Pubkey,
|
||||
/// }
|
||||
///
|
||||
/// impl Counter {
|
||||
/// pub fn new(_ctx: Context<Empty>, auth_program: Pubkey) -> Result<Self> {
|
||||
/// Ok(Self {
|
||||
/// count: 0,
|
||||
/// auth_program,
|
||||
/// })
|
||||
/// }
|
||||
///
|
||||
/// #[access_control(SetCount::accounts(&self, &ctx))]
|
||||
/// pub fn set_count(&mut self, ctx: Context<SetCount>, new_count: u64) -> Result<()> {
|
||||
/// // Ask the auth program if we should approve the transaction.
|
||||
/// let cpi_program = ctx.accounts.auth_program.clone();
|
||||
/// let cpi_ctx = CpiContext::new(cpi_program, Empty {});
|
||||
///
|
||||
/// // This is the client generated by the `#[interface]` attribute.
|
||||
/// auth::is_authorized(cpi_ctx, self.count, new_count)?;
|
||||
///
|
||||
/// // Approved, so update.
|
||||
/// self.count = new_count;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct Empty {}
|
||||
///
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct SetCount<'info> {
|
||||
/// auth_program: AccountInfo<'info>,
|
||||
/// }
|
||||
///
|
||||
/// impl<'info> SetCount<'info> {
|
||||
/// pub fn accounts(counter: &Counter, ctx: &Context<SetCount>) -> Result<()> {
|
||||
/// if ctx.accounts.auth_program.key != &counter.auth_program {
|
||||
/// return Err(ErrorCode::InvalidAuthProgram.into());
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[error]
|
||||
/// pub enum ErrorCode {
|
||||
/// #[msg("Invalid auth program.")]
|
||||
/// InvalidAuthProgram,
|
||||
/// }
|
||||
///```
|
||||
///
|
||||
/// ## Defining an implementation
|
||||
///
|
||||
/// Now we define the program that implements the interface, which the above
|
||||
/// program will call.
|
||||
///
|
||||
/// ```ignore
|
||||
/// #![feature(proc_macro_hygiene)]
|
||||
///
|
||||
/// use anchor_lang::prelude::*;
|
||||
/// use counter::Auth;
|
||||
///
|
||||
/// #[program]
|
||||
/// pub mod counter_auth {
|
||||
/// use super::*;
|
||||
///
|
||||
/// #[state]
|
||||
/// pub struct CounterAuth {}
|
||||
///
|
||||
/// // TODO: remove this impl block after addressing
|
||||
/// // https://github.com/project-serum/anchor/issues/71.
|
||||
/// impl CounterAuth {
|
||||
/// pub fn new(_ctx: Context<Empty>) -> Result<Self, ProgramError> {
|
||||
/// Ok(Self {})
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl<'info> Auth<'info, Empty> for CounterAuth {
|
||||
/// fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> ProgramResult {
|
||||
/// if current % 2 == 0 {
|
||||
/// if new % 2 == 0 {
|
||||
/// return Err(ProgramError::Custom(50)); // Arbitrary error code.
|
||||
/// }
|
||||
/// } else {
|
||||
/// if new % 2 == 1 {
|
||||
/// return Err(ProgramError::Custom(60)); // Arbitrary error code.
|
||||
/// }
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// #[derive(Accounts)]
|
||||
/// pub struct Empty {}
|
||||
/// ```
|
||||
///
|
||||
/// # Returning Values Across CPI
|
||||
///
|
||||
/// The caller above uses a `Result` to act as a boolean. However, in order
|
||||
/// for this feature to be maximally useful, we need a way to return values from
|
||||
/// interfaces. For now, one can do this by writing to a shared account, e.g.,
|
||||
/// with the SPL's [Shared Memory Program](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory).
|
||||
/// In the future, Anchor will add the ability to return values across CPI
|
||||
/// without having to worry about the details of shared memory accounts.
|
||||
#[proc_macro_attribute]
|
||||
pub fn interface(
|
||||
_args: proc_macro::TokenStream,
|
||||
|
|
|
@ -3,6 +3,8 @@ extern crate proc_macro;
|
|||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// The `#[state]` attribute defines the program's state struct, i.e., the
|
||||
/// program's global account singleton giving the program the illusion of state.
|
||||
#[proc_macro_attribute]
|
||||
pub fn state(
|
||||
_args: proc_macro::TokenStream,
|
||||
|
|
|
@ -2,12 +2,13 @@ use crate::{Accounts, Sysvar};
|
|||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::sysvar::rent::Rent;
|
||||
|
||||
// The Ctor accounts that can be used to create any account within the program
|
||||
// itself (instead of creating the account on the client).
|
||||
//
|
||||
// This is used to create accounts at deterministic addresses, as a function of
|
||||
// nothing but a program ID--for example, to create state global program
|
||||
// structs and program IDL accounts.
|
||||
/// The Ctor accounts that can be used to create any account within the program
|
||||
/// itself (instead of creating the account on the client).
|
||||
///
|
||||
/// This is used to create accounts at deterministic addresses, as a function of
|
||||
/// nothing but a program ID--for example, to create state global program
|
||||
/// structs and program IDL accounts. It's currently used **internally** within
|
||||
/// the Anchor `#[program]` codegen.
|
||||
#[derive(Accounts)]
|
||||
pub struct Ctor<'info> {
|
||||
// Payer of the transaction.
|
||||
|
|
|
@ -54,7 +54,7 @@ pub use anchor_attribute_interface::interface;
|
|||
pub use anchor_attribute_program::program;
|
||||
pub use anchor_attribute_state::state;
|
||||
pub use anchor_derive_accounts::Accounts;
|
||||
/// Default serialization format for anchor instructions and accounts.
|
||||
/// Borsh is the default serialization format for instructions and accounts.
|
||||
pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize};
|
||||
pub use error::Error;
|
||||
pub use solana_program;
|
||||
|
|
Loading…
Reference in New Issue