Anchor V2 Proposal #4249
Replies: 4 comments 1 reply
-
Additional Questions
|
Beta Was this translation helpful? Give feedback.
-
|
Move metadata, etc.... out of anchor into packages |
Beta Was this translation helpful? Give feedback.
-
|
consider codama for idl due to custom account types |
Beta Was this translation helpful? Give feedback.
-
/* lib.rs */
use anchor_lang::prelude::*;
use anchor_lang::legacy::BorshAccount;
use anchor_spl::token::{Interface, Token, TokenAccount, cpi::Transfer};
pub unsafe extern "C" fn entrypoint(mut input: *mut u8) -> u64 {
// asm handler for posting oracle update
core::arch::asm!( ... );
prop_amm::entry(input);
}
// alternatively..
// pub fn custom_entry(
// program_id: &Address,
// accounts: &[AccountView],
// data: &[u8],
// ) -> solana_program::entrypoint::ProgramResult {
// match data[..8] {
// POST_ORACLE_UPDATE => post_oracle_update(program_id, accounts, &data[8..]),
// _ => entry(program_id, accounts, data), // fallback to Anchor
// }
//
// prop_amm::entry(program_id, accounts, data)
// }
#[derive(Accounts)]
pub struct Swap {
#[account(mut, signer)]
pub swapper: UncheckedAccount,
#[account(mut, seeds = [b"market", market.id.as_ref()], bump = market.bump,
has_one = base_vault, has_one = quote_vault)]
pub market: BorshAccount<Market>, // legacy borsh path
pub metadata: Account<Metadata>, // zerocopy by default
#[account(mut)] pub base_vault: Account<TokenAccount>,
#[account(mut)] pub quote_vault: Account<TokenAccount>,
#[account(mut)] pub swapper_base: Account<TokenAccount>,
#[account(mut)] pub swapper_quote: Account<TokenAccount>,
pub system_program: Program<System>,
pub token_program: Interface<Token>,
}
#[program]
pub mod prop_amm {
use super::*;
pub fn swap(ctx: Context<Swap>, amount_in: u64) -> Result<()> {
let now = Clock::get()?.slot;
require!(now - ctx.accounts.market.last_oracle_slot <= 5, ErrorCode::StaleOracle);
let out = amount_in * ctx.accounts.market.oracle_price / 1_000_000;
// CPI: pinnochio style!
Transfer {
from: &ctx.accounts.swapper_base,
to: &ctx.accounts.base_vault,
authority: &ctx.accounts.swapper,
amount: amount_in,
}.invoke(&ctx.accounts.token_program)?;
// PDA-signed CPI
let bump = [ctx.accounts.market.bump];
let id = ctx.accounts.market.id;
let seeds: &[&[u8]] = &[b"market", id.as_ref(), &bump];
Transfer {
from: &ctx.accounts.quote_vault,
to: &ctx.accounts.swapper_quote,
authority: &ctx.accounts.market,
amount: out,
}.invoke_signed(&ctx.accounts.token_program, &[seeds])?;
// view is refreshed post-CPI
ctx.accounts.market.base_reserve += amount_in;
ctx.accounts.market.quote_reserve -= out;
emit!(Swapped { market: *ctx.accounts.market.key(), amount_in, out });
Ok(())
}
}
#[event]
pub struct Swapped {
pub market: Pubkey,
pub amount_in: u64,
pub out: u64,
}
/* anchor_lang/legacy.rs — shipped as anchor::legacy::BorshAccount */
// Example impl for custom account
impl<T> AnchorAccount for BorshAccount<T>
where
T: BorshSerialize + BorshDeserialize + Discriminator,
{
type Data = T;
fn load(view: AccountView) -> Result<Self> {
let data = view.data();
require!(data.len() >= 8, ErrorCode::AccountDiscriminatorNotFound);
require!(
&data[..8] == T::DISCRIMINATOR,
ErrorCode::DiscriminatorMismatch
);
let data = T::try_deserialize(&mut &data[8..])?;
Ok(Self { view, data })
}
fn account(&self) -> &AccountView {
&self.view
}
fn try_data(&self) -> Result<&T> {
Ok(&self.data)
}
fn try_data_mut(&mut self) -> Result<&mut T> {
Ok(&mut self.data)
}
fn exit(&self) -> Result<()> {
let mut buf = self.view.try_borrow_mut_data()?;
require!(buf.len() >= 8, ErrorCode::AccountDidNotSerialize);
self.data.serialize(&mut &mut buf[8..])?;
Ok(())
}
}
impl<T> Deref for BorshAccount<T>
where
T: BorshSerialize + BorshDeserialize,
{
type Target = T;
fn deref(&self) -> &T {
&self.data
}
}
impl<T> DerefMut for BorshAccount<T>
where
T: BorshSerialize + BorshDeserialize,
{
fn deref_mut(&mut self) -> &mut T {
&mut self.data
}
}This is what we're currently considering for the syntax changes for Anchor V2 (Including how to implement a Custom Account. Important to note that custom accounts don't necessarily need deref/derefMut implemented, the constraints work by just Implementing AnchorAccount (name subject to change) We'll also support old Anchor V1 CPIs as well as the new Anchor V2 pinocchio style CPI. The only thing that we're not fond of in the current syntax is Program and Interface. We don't think it's great for interface to move ids to the constraint, for things like token. So it would be nice to get feedback for this. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Anchor v2
Notes
Design Goals
User-facing changes:
Language:
Account<'info, T>->Account<T>.Context<'a, 'b, 'c, 'info, T>→Context<T>. Falls out naturally from the Pinocchio migration;AccountViewhas no lifetime parameter.Account,AccountLoader,LazyAccount,InterfaceAccount,Signer,Programall collapse into oneAccount<T>. zero-copy by default inheriting from Pinocchio.AccountChecks, where it implsAccount<System>which would have the id check onAccountView.AccountInfo.declare_id!optionalFor example:
Tooling:
Mantainer-facing changes:
#[derive(Accounts)]is to be reworked and broken into per-constraint-family modules with a single shared evaluation interface instead of the current spaghetti.#[account]helper attribute into#[deserialize],#[validate],#[cleanup]I want to particularly emphasise the big changes here being:
Also adding two niche examples here to showcase how doing custom stuff will look like in Anchor, specifically addressing concerns during the 11th February Anchor community meetup:
Migrating Anchor V1 to V2
implementing custom serde for an account
There's some specific details here not really shown like how does
Account<System>orAccount<()>work or how omitting#[account]looks like. We're still looking into this and this will probably change, but currently we're considering something like:Beta Was this translation helpful? Give feedback.
All reactions