Attribute Macro anchor_attribute_interface::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.
use anchor_lang::prelude::*;
#[interface]
pub trait Auth<'info, T: Accounts<'info>> {
fn is_authorized(ctx: Context<T>, current: u64, new: u64) -> anchor_lang::Result<()>;
}
#[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(error!(ErrorCode::InvalidAuthProgram));
}
Ok(())
}
}
#[error_code]
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.
use anchor_lang::prelude::*;
use counter::Auth;
#[program]
pub mod counter_auth {
use super::*;
#[state]
pub struct CounterAuth;
impl<'info> Auth<'info, Empty> for CounterAuth {
fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> Result<()> {
if current % 2 == 0 {
if new % 2 == 0 {
return Err(ProgramError::Custom(50).into()); // Arbitrary error code.
}
} else {
if new % 2 == 1 {
return Err(ProgramError::Custom(60).into()); // 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.