Attribute Macro anchor_lang::interface

source · []
#[interface]
Expand description

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.

#![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.

#![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. In the future, Anchor will add the ability to return values across CPI without having to worry about the details of shared memory accounts.