#[account]
#[derive(InitSpace)]
pub struct Pool {
pub base_mint: Pubkey,
pub quote_mint: Pubkey,
pub fee_rate: u16,
pub protocol_fee_rate: u16,
pub tick_spacing: i32,
pub current_tick_index: i32,
pub liquidity: u128,
pub sqrt_price: u128,
pub fee_growth_global_0: u128,
pub fee_growth_global_1: u128,
pub protocol_fees_0: u64,
pub protocol_fees_1: u64,
pub bump: u8,
}
let (pool_pda, pool_bump) = Pubkey::find_program_address(
&[
b"pool",
base_mint.as_ref(),
quote_mint.as_ref(),
],
program_id
);
## Instruction Handlers Deep Dive
### 1. `initialize_pool` - Pool Creation
This instruction creates a new liquidity pool with specified parameters:
```rust
#[derive(Accounts)]
pub struct InitializePool<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(
init,
payer = payer,
space = 8 + Pool::INIT_SPACE,
seeds = [
b"pool",
base_mint.key().as_ref(),
quote_mint.key().as_ref()
],
bump
)]
pub pool: Account<'info, Pool>,
pub base_mint: Account<'info, Mint>,
pub quote_mint: Account<'info, Mint>,
pub system_program: Program<'info, System>,
}
pub fn initialize_pool(
ctx: Context<InitializePool>,
fee_rate: u16,
tick_spacing: i32,
initial_sqrt_price: u128,
) -> Result<()> {
let pool = &mut ctx.accounts.pool;
require!(fee_rate <= MAX_FEE_RATE, ErrorCode::InvalidFeeRate);
require!(tick_spacing > 0, ErrorCode::InvalidTickSpacing);
pool.base_mint = ctx.accounts.base_mint.key();
pool.quote_mint = ctx.accounts.quote_mint.key();
pool.fee_rate = fee_rate;
pool.tick_spacing = tick_spacing;
pool.sqrt_price = initial_sqrt_price;
pool.current_tick_index = calculate_tick_from_sqrt_price(initial_sqrt_price);
pool.bump = ctx.bumps.pool;
Ok(())
}
### 2. `create_position` - Concentrated Liquidity Position
Creates a position with liquidity concentrated between specified ticks:
```rust
#[derive(Accounts)]
#[instruction(position_id: u64)]
pub struct CreatePosition<'info> {
#[account(mut)]
pub owner: Signer<'info>,
#[account(
seeds = [
b"pool",
pool.base_mint.as_ref(),
pool.quote_mint.as_ref()
],
bump = pool.bump
)]
pub pool: Account<'info, Pool>,
#[account(
init,
payer = owner,
space = 8 + Position::INIT_SPACE,
seeds = [
b"position",
pool.key().as_ref(),
owner.key().as_ref(),
&position_id.to_le_bytes()
],
bump
)]
pub position: Account<'info, Position>,
#[account(mut)]
pub token_account_a: Account<'info, TokenAccount>,
#[account(mut)]
pub token_account_b: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
}
#[account]
#[derive(InitSpace)]
pub struct Position {
pub pool: Pubkey,
pub owner: Pubkey,
pub liquidity: u128,
pub tick_lower: i32,
pub tick_upper: i32,
pub fee_growth_inside_0_last: u128,
pub fee_growth_inside_1_last: u128,
pub tokens_owed_0: u64,
pub tokens_owed_1: u64,
pub bump: u8,
}
pub fn create_position(
ctx: Context<CreatePosition>,
position_id: u64,
tick_lower: i32,
tick_upper: i32,
liquidity_delta: u128,
) -> Result<()> {
let pool = &mut ctx.accounts.pool;
let position = &mut ctx.accounts.position;
require!(tick_lower < tick_upper, ErrorCode::InvalidTickRange);
require!(tick_lower % pool.tick_spacing == 0, ErrorCode::TickNotSpaced);
require!(tick_upper % pool.tick_spacing == 0, ErrorCode::TickNotSpaced);
let (amount_a, amount_b) = calculate_liquidity_amounts(
pool.sqrt_price,
tick_lower,
tick_upper,
liquidity_delta
);
transfer_tokens_in(
&ctx.accounts.token_account_a,
&ctx.accounts.token_account_b,
amount_a,
amount_b,
&ctx.accounts.token_program,
&ctx.accounts.owner
)?;
position.pool = ctx.accounts.pool.key();
position.owner = ctx.accounts.owner.key();
position.liquidity = liquidity_delta;
position.tick_lower = tick_lower;
position.tick_upper = tick_upper;
position.bump = ctx.bumps.position;
update_ticks_liquidity(pool, tick_lower, tick_upper, liquidity_delta, true)?;
Ok(())
}
### 3. `place_limit_order` - Orderbook Integration
Creates a limit order that sits on the order book until matched:
```rust
#[derive(Accounts)]
#[instruction(order_id: u64)]
pub struct PlaceLimitOrder<'info> {
#[account(mut)]
pub owner: Signer<'info>,
#[account(
seeds = [
b"pool",
pool.base_mint.as_ref(),
pool.quote_mint.as_ref()
],
bump = pool.bump
)]
pub pool: Account<'info, Pool>,
#[account(
init,
payer = owner,
space = 8 + LimitOrder::INIT_SPACE,
seeds = [
b"order",
pool.key().as_ref(),
owner.key().as_ref(),
&order_id.to_le_bytes()
],
bump
)]
pub order: Account<'info, LimitOrder>,
#[account(
mut,
token::mint = pool.base_mint,
token::authority = owner
)]
pub user_token_account: Account<'info, TokenAccount>,
#[account(
init,
payer = owner,
token::mint = pool.base_mint,
token::authority = order,
seeds = [
b"order_vault",
order.key().as_ref()
],
bump
)]
pub order_vault: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
}
#[account]
#[derive(InitSpace)]
pub struct LimitOrder {
pub pool: Pubkey,
pub owner: Pubkey,
pub order_id: u64,
pub tick: i32,
pub amount: u64,
pub is_bid: bool,
pub filled_amount: u64,
pub status: OrderStatus,
pub bump: u8,
}
pub fn place_limit_order(
ctx: Context<PlaceLimitOrder>,
order_id: u64,
tick: i32,
amount: u64,
is_bid: bool,
) -> Result<()> {
let order = &mut ctx.accounts.order;
let pool = &ctx.accounts.pool;
require!(tick % pool.tick_spacing == 0, ErrorCode::TickNotSpaced);
let transfer_ctx = CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.user_token_account.to_account_info(),
to: ctx.accounts.order_vault.to_account_info(),
authority: ctx.accounts.owner.to_account_info(),
}
);
transfer(transfer_ctx, amount)?;
order.pool = ctx.accounts.pool.key();
order.owner = ctx.accounts.owner.key();
order.order_id = order_id;
order.tick = tick;
order.amount = amount;
order.is_bid = is_bid;
order.filled_amount = 0;
order.status = OrderStatus::Open;
order.bump = ctx.bumps.order;
emit!(OrderPlaced {
pool: ctx.accounts.pool.key(),
owner: ctx.accounts.owner.key(),
order_id,
tick,
amount,
is_bid,
timestamp: Clock::get()?.unix_timestamp,
});
Ok(())
}
### 4. `swap` - Execution with Order Matching
Executes a swap, potentially matching against limit orders:
```rust
pub fn swap(
ctx: Context<Swap>,
amount: u64,
sqrt_price_limit: u128,
is_exact_input: bool,
) -> Result<()> {
let pool = &mut ctx.accounts.pool;
let (amount_in, amount_out, sqrt_price_new, liquidity) =
compute_swap_step(
pool.sqrt_price,
sqrt_price_limit,
pool.liquidity,
amount,
pool.fee_rate,
is_exact_input
)?;
let matched_orders = find_matching_orders(
pool,
pool.current_tick_index,
get_tick_from_sqrt_price(sqrt_price_new),
!is_exact_input
);
let mut total_matched = 0;
for order_info in matched_orders {
let order_account = &ctx.remaining_accounts[order_info.index];
let mut order = Account::<LimitOrder>::try_from(order_account)?;
let match_amount = min(order.amount - order.filled_amount, amount_out - total_matched);
execute_against_limit_order(
&mut order,
match_amount,
&ctx.accounts.token_program,
&ctx.accounts.user_token_account,
&ctx.accounts.order_vaults[order_info.vault_index]
)?;
total_matched += match_amount;
if total_matched >= amount_out {
break;
}
}
pool.sqrt_price = sqrt_price_new;
pool.liquidity = liquidity;
let protocol_fee = amount_in
.checked_mul(pool.protocol_fee_rate as u64)
.unwrap()
.checked_div(10_000)
.unwrap();
if is_exact_input {
pool.protocol_fees_0 = pool.protocol_fees_0
.checked_add(protocol_fee)
.unwrap();
} else {
pool.protocol_fees_1 = pool.protocol_fees_1
.checked_add(protocol_fee)
.unwrap();
}
Ok(())
}
### 5. `leverage_position` - Leveraged Liquidity
Enables leveraged liquidity provision with up to 5x multiplier:
```rust
pub fn leverage_position(
ctx: Context<LeveragePosition>,
leverage_id: u64,
position_key: Pubkey,
leverage_multiplier: u8,
collateral_amount: u64,
) -> Result<()> {
require!(leverage_multiplier >= 1, ErrorCode::InvalidLeverage);
require!(leverage_multiplier <= MAX_LEVERAGE, ErrorCode::ExceedsMaxLeverage);
let position = &ctx.accounts.position;
let leveraged_position = &mut ctx.accounts.leveraged_position;
let total_liquidity_value = calculate_position_value(position, ctx.accounts.pool.sqrt_price);
let collateral_value = calculate_token_value(collateral_amount, ctx.accounts.pool.sqrt_price);
let max_borrow_value = collateral_value
.checked_mul(leverage_multiplier as u64)
.unwrap()
.checked_sub(collateral_value)
.unwrap();
leveraged_position.position = position_key;
leveraged_position.owner = ctx.accounts.owner.key();
leveraged_position.leverage_multiplier = leverage_multiplier;
leveraged_position.collateral_amount = collateral_amount;
leveraged_position.borrowed_amount_0 = calculate_borrow_amount_0(total_liquidity_value, max_borrow_value);
leveraged_position.borrowed_amount_1 = calculate_borrow_amount_1(total_liquidity_value, max_borrow_value);
leveraged_position.bump = ctx.bumps.leveraged_position;
let health_ratio = calculate_health_ratio(leveraged_position, ctx.accounts.pool.sqrt_price);
require!(health_ratio > MIN_HEALTH_RATIO, ErrorCode::InsufficientCollateral);
Ok(())
}
## Mathematical Formulas
### Concentrated Liquidity Calculations
The amount of token X and Y required for a liquidity position between ticks $t_L$ and $t_U$ is given by:
$$
\Delta x = \Delta L \cdot \left( \frac{1}{\sqrt{P}} - \frac{1}{\sqrt{P_U}} \right)
$$
$$
\Delta y = \Delta L \cdot \left( \sqrt{P} - \sqrt{P_L} \right)
$$
Where:
- $\Delta L$ is the liquidity delta
- $\sqrt{P}$ is the current square root price
- $\sqrt{P_L}, \sqrt{P_U}$ are square root prices at lower and upper ticks
### Swap Computation
For a swap with fee $f$ (in basis points):
Effective amount in after fees:
$$
\Delta x_{eff} = \Delta x \cdot \left(1 - \frac{f}{10^4}\right)
$$
Output amount:
$$
\Delta y = \frac{y \cdot \Delta x_{eff}}{x + \Delta x_{eff}}
$$
### Leverage Health Ratio
$$
\text{Health Ratio} = \frac{\text{Position Value}}{\text{Borrowed Value} \cdot \text{Liquidation Threshold}}
$$
Positions are liquidated when:
$$
\text{Health Ratio} < 1
$$
## Solana & Anchor Best Practices
### 1. Account Validation Patterns
Always validate accounts using Anchor's type system:
```rust
#[account(
constraint = token_account.mint == pool.base_mint,
constraint = token_account.owner == owner.key()
)]
pub token_account: Account<'info, TokenAccount>,
### 2. Compute Unit Optimization
Use iteration limits and batch processing for order matching:
```rust
const MAX_ORDERS_PER_SWAP: usize = 10;
for i in 0..min(remaining_orders.len(), MAX_ORDERS_PER_SWAP) {
if compute_units_remaining() < SAFE_COMPUTE_LIMIT {
break;
}
}
### 3. Token-2022 Compatibility
Handle transfer fees by checking received amounts:
```rust
let balance_before = token_account.amount;
transfer(transfer_ctx, amount)?;
let balance_after = token_account.reload()?.amount;
let received_amount = balance_after.checked_sub(balance_before).unwrap();
### 4. Event Emission for Indexers
Emit structured events for easy off-chain processing:
```rust
#[event]
pub struct SwapEvent {
pub pool: Pubkey,
pub trader: Pubkey,
pub amount_in: u64,
pub amount_out: u64,
pub sqrt_price_before: u128,
pub sqrt_price_after: u128,
pub liquidity: u128,
pub timestamp: i64,
}
## Security Considerations
### 1. Access Control
All critical operations use PDA-based authority:
```rust
#[account(
seeds = [b"config"],
bump = config.bump,
constraint = config.admin == admin.key()
)]
pub config: Account<'info, ProtocolConfig>,
### 2. Input Validation
Validate all user inputs with appropriate bounds:
```rust
require!(tick_lower < tick_upper, ErrorCode::InvalidTickRange);
require!(fee_rate <= MAX_FEE_RATE, ErrorCode::InvalidFeeRate);
require!(amount > 0, ErrorCode::ZeroAmount);
### 3. Arithmetic Safety
Use checked arithmetic to prevent overflows:
```rust
let total = amount_a
.checked_add(amount_b)
.ok_or(ErrorCode::ArithmeticOverflow)?;
### 4. Reentrancy Protection
Solana's transaction model prevents reentrancy, but validate cross-program interactions:
```rust
require!(
token_account_a.mint == pool.base_mint &&
token_account_b.mint == pool.quote_mint,
ErrorCode::InvalidTokenAccount
);
### 5. Oracle Manipulation Protection
Use time-weighted prices for sensitive operations:
```rust
let price = calculate_time_weighted_price(
pool.sqrt_price_history,
Clock::get()?.unix_timestamp
);
## How to Use This Contract
### Building and Deploying
```bash
# Build the program
anchor build
# Deploy to devnet
anchor deploy --provider.cluster devnet
# Verify deployment
solana program show --programs
### Example TypeScript Client
```typescript
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { DefiTunaAmm } from "../target/types/defi_tuna_amm";
async function createPosition() {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.DefiTunaAmm as Program<DefiTunaAmm>;
const [poolPda] = anchor.web3.PublicKey.findProgramAddressSync(
[
Buffer.from("pool"),
baseMint.toBuffer(),
quoteMint.toBuffer()
],
program.programId
);
const positionId = new anchor.BN(Date.now());
const [positionPda] = anchor.web3.PublicKey.findProgramAddressSync(
[
Buffer.from("position"),
poolPda.toBuffer(),
provider.wallet.publicKey.toBuffer(),
positionId.toArrayLike(Buffer, "le", 8)
],
program.programId
);
const tx = await program.methods
.createPosition(
positionId,
-6000,
6000,
new anchor.BN(1000000)
)
.accounts({
pool: poolPda,
position: positionPda,
owner: provider.wallet.publicKey,
tokenAccountA: tokenAccountA,
tokenAccountB: tokenAccountB,
})
.rpc();
console.log("Transaction signature:", tx);
}
### Required Pre-Instructions
For complex operations like leveraged positions, you may need to:
1. Create associated token accounts
2. Approve token transfers
3. Initialize required PDAs
4. Fund accounts with minimum rent
## Extending the Contract
### Adding New Instructions
1. Define new account structs in `#[derive(Accounts)]`
2. Implement handler function with proper validation
3. Add to the `lib.rs` module exports
4. Update IDL generation
### Customization Points
- **Fee Models**: Modify `compute_swap_step` for dynamic fees
- **Order Types**: Extend `LimitOrder` for different order types (FOK, IOC)
- **Leverage Models**: Add new collateral types or liquidation mechanisms
- **Oracle Integration**: Incorporate Pyth or Switchboard for price feeds
### Testing Strategies
```rust
#[tokio::test]
async fn test_swap_with_limit_order_match() {
let mut test = ProgramTest::new(
"defi_tuna_amm",
id(),
processor!(processor::Processor::process)
);
test.add_account(mint_pubkey, mint_account);
let (mut banks_client, payer, recent_blockhash) = test.start().await;
let place_order_ix = Instruction {
program_id: id(),
accounts: place_order_accounts,
data: place_order_data,
};
let swap_ix = Instruction {
program_id: id(),
accounts: swap_accounts,
data: swap_data,
};
let transaction = Transaction::new_signed_with_payer(
&[place_order_ix, swap_ix],
Some(&payer.pubkey()),
&[&payer],
recent_blockhash
);
banks_client.process_transaction(transaction).await.unwrap();
}
## Conclusion
The DefiTuna AMM smart contract demonstrates advanced Anchor patterns for building sophisticated DeFi protocols on Solana. By combining concentrated liquidity, limit orders, and leverage in a single program, it showcases how to manage complex state relationships while maintaining security and efficiency.
Key takeaways for developers:
1. Use PDA hierarchies for secure ownership and access control
2. Implement mathematical operations with overflow protection
3. Design for composability with other Solana programs
4. Emit comprehensive events for off-chain indexing
5. Optimize for compute units in iteration-heavy operations
This contract serves as a foundation for building next-generation AMMs that bridge the gap between traditional order books and automated market makers.