Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
On Ethereum, composability means one contract calls another contract's function. On Solana, composability means one program reads and writes accounts that another program owns. The account-based model is more explicit — you literally pass every account a transaction will touch — but it enables aggregators like Jupiter to route through 10+ DEXs in a single transaction with no token approvals.
Cross-program invocation (CPI) reading state from a Token Program account.
use anchor_lang::prelude::*;
use anchor_spl::token::{self, Token, TokenAccount, Transfer};
#[derive(Accounts)]
pub struct Swap<'info> {
#[account(mut)]
pub user_source: Account<'info, TokenAccount>,
#[account(mut)]
pub user_dest: Account<'info, TokenAccount>,
#[account(mut)]
pub pool_source: Account<'info, TokenAccount>,
#[account(mut)]
pub pool_dest: Account<'info, TokenAccount>,
pub pool_authority: Signer<'info>,
pub token_program: Program<'info, Token>,
}
pub fn swap(ctx: Context<Swap>, amount_in: u64, min_out: u64) -> Result<()> {
// Transfer user -> pool (user signs)
let cpi_in = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.user_source.to_account_info(),
to: ctx.accounts.pool_source.to_account_info(),
authority: ctx.accounts.user_source.to_account_info(),
},
);
token::transfer(cpi_in, amount_in)?;
// Compute amount_out using x*y=k pricing on pool reserves
let reserve_in = ctx.accounts.pool_source.amount;
let reserve_out = ctx.accounts.pool_dest.amount;
let amount_out = (reserve_out * amount_in) / (reserve_in + amount_in);
require!(amount_out >= min_out, SwapError::Slippage);
// Transfer pool -> user (program-derived authority signs)
let cpi_out = CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.pool_dest.to_account_info(),
to: ctx.accounts.user_dest.to_account_info(),
authority: ctx.accounts.pool_authority.to_account_info(),
},
&[],
);
token::transfer(cpi_out, amount_out)?;
Ok(())
}
#[error_code]
pub enum SwapError { Slippage }