Skip to content

codiiman/Solana-Perp-Dex-Smart-Contract

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Solana Perpetual DEX Smart Contract

Anchor Solana Rust License

A production-grade Solana smart contract for a perpetual futures DEX, inspired by Drift Protocol V2. Built with Anchor framework and designed for high-performance DeFi trading.

πŸ“‹ Overview

This project implements a complete perpetual futures decentralized exchange (DEX) on Solana, supporting:

  • Multiple Markets: Permissioned creation of perpetual markets with configurable parameters
  • Leverage Trading: Long/short positions with up to 50x leverage (configurable per market)
  • Order Types: Limit and market orders with partial fills
  • vAMM: Virtual Automated Market Maker for price discovery and liquidity
  • Funding Rates: Periodic funding payments based on premium/discount to oracle price
  • Liquidations: Permissionless liquidation of undercollateralized positions
  • Insurance Fund: Accumulates fees to cover bad debt from liquidations
  • Oracle Integration: Ready for Pyth Network price feeds
  • Isolated Margin: Each position has isolated collateral and risk

✨ Features

Core Functionality

  • βœ… Market Management: Create and configure perpetual markets
  • βœ… User Accounts: Initialize user accounts and manage collateral
  • βœ… Order Placement: Place limit and market orders
  • βœ… Order Execution: Fill orders via vAMM with price impact calculation
  • βœ… Position Management: Open, update, and close positions with leverage
  • βœ… Funding Mechanism: Hourly funding rate updates and payment settlement
  • βœ… Liquidation System: Permissionless liquidation with liquidator rewards
  • βœ… Insurance Fund: Fee accumulation and bad debt coverage
  • βœ… PNL Calculation: Real-time unrealized and realized PNL tracking
  • βœ… Margin Health: Health factor calculation for liquidation checks

Security Features

  • βœ… Access Control: Admin-only market creation and configuration
  • βœ… Oracle Validation: Staleness and deviation checks
  • βœ… Overflow Protection: Safe math operations throughout
  • βœ… CEI Pattern: Checks-Effects-Interactions pattern for state updates
  • βœ… Reentrancy Protection: Anchor's built-in account validation
  • βœ… Input Validation: Comprehensive parameter validation

πŸ—οΈ Architecture

Account Structure

ProtocolState (Global)
β”œβ”€β”€ admin: Pubkey
β”œβ”€β”€ insurance_fund: Pubkey
β”œβ”€β”€ fee rates, liquidation params, funding params
└── oracle parameters

PerpMarket (Per Market)
β”œβ”€β”€ market_id, base/quote assets
β”œβ”€β”€ oracle: Pubkey
β”œβ”€β”€ max_leverage, fee rates
β”œβ”€β”€ funding_rate, premium_index
β”œβ”€β”€ vAMM reserves (base, quote, k)
└── funding schedule

User (Per User)
β”œβ”€β”€ owner: Pubkey
β”œβ”€β”€ collateral: u64
β”œβ”€β”€ position_count, order_count
└── last_interaction

Position (Per User/Market)
β”œβ”€β”€ user, market
β”œβ”€β”€ side (long/short), size
β”œβ”€β”€ entry_price, leverage
β”œβ”€β”€ collateral, unrealized_pnl
└── cumulative_funding_payment

Order (Per User/Order)
β”œβ”€β”€ user, market, order_id
β”œβ”€β”€ side, order_type, size
β”œβ”€β”€ filled_size, price
└── status (open/filled/cancelled)

InsuranceFund (Global)
β”œβ”€β”€ balance: u64
β”œβ”€β”€ total_fees_collected
└── total_bad_debt_covered

Instruction Flow

1. Initialize Protocol
   └─> Create ProtocolState, InsuranceFund

2. Create Market
   └─> Admin creates PerpMarket with vAMM initialization

3. User Onboarding
   β”œβ”€> Initialize User account
   └─> Deposit Collateral

4. Trading Flow
   β”œβ”€> Place Order (limit/market)
   β”œβ”€> Fill Order (via vAMM)
   β”œβ”€> Open Position (from filled order)
   └─> Update Position PNL

5. Funding Flow
   β”œβ”€> Update Funding Rate (keeper)
   └─> Settle Funding Payment (user)

6. Liquidation Flow
   β”œβ”€> Check Position Health
   β”œβ”€> Liquidate Position (permissionless)
   └─> Pay Liquidator Reward (from insurance fund)

7. Position Management
   β”œβ”€> Update Position PNL
   └─> Close Position (partial/full)

πŸ”’ Key Algorithms

Funding Rate Calculation

The funding rate is calculated based on the premium index (deviation of mark price from oracle price):

premium_index = (mark_price - oracle_price) * PRECISION / oracle_price
funding_rate = clamp(premium_index * sensitivity, -max_rate, max_rate)
  • Positive funding rate: Longs pay shorts (mark > oracle)
  • Negative funding rate: Shorts pay longs (mark < oracle)
  • Default sensitivity: 10% (0.1 * PRECISION)
  • Default max rate: 1% per hour (0.01 * PRECISION)

Funding Payment

Funding payments are calculated per position:

payment = funding_rate * position_size * time_elapsed / funding_interval
  • For longs: payment = rate * size * time / interval
  • For shorts: payment = -rate * size * time / interval
  • Payments are deducted/added to collateral

Margin Health Factor

Health factor determines if a position is liquidatable:

health = (collateral + unrealized_pnl) / margin_requirement
  • Health < 1.0: Position is undercollateralized
  • Health < 0.8 (default): Position is liquidatable
  • Margin requirement: size * entry_price / leverage

vAMM Constant Product

The virtual AMM uses the constant product formula:

x * y = k

For buying base (input quote, output base):

new_reserve_y = reserve_y + input_quote
new_reserve_x = k / new_reserve_y
output_base = reserve_x - new_reserve_x

For selling base (input base, output quote):

new_reserve_x = reserve_x + input_base
new_reserve_y = k / new_reserve_x
output_quote = reserve_y - new_reserve_y

Unrealized PNL

PNL is calculated based on price movement:

For long: pnl = (current_price - entry_price) * size / PRECISION
For short: pnl = (entry_price - current_price) * size / PRECISION

Liquidation Bonus

Liquidators receive a bonus from the insurance fund:

bonus = liquidated_size * liquidation_price * bonus_rate / PRECISION^2
  • Default bonus rate: 5% (0.05 * PRECISION)
  • Bad debt is also covered by the insurance fund

πŸš€ Installation

Prerequisites

Setup

  1. Clone the repository

    git clone <repository-url>
    cd Solana-Perp-Dex-Smart-Contract-1
  2. Install dependencies

    anchor build
  3. Run tests (when test suite is added)

    anchor test

πŸ“¦ Build & Deploy

Build

# Build the program
anchor build

# Build with optimizations
anchor build --release

Deploy to Localnet

# Start local validator
solana-test-validator

# Deploy (in another terminal)
anchor deploy

Deploy to Devnet

# Set cluster
solana config set --url devnet

# Airdrop SOL
solana airdrop 2 $(solana address)

# Deploy
anchor deploy --provider.cluster devnet

Deploy to Mainnet

⚠️ Warning: Only deploy to mainnet after comprehensive audits and testing.

# Set cluster
solana config set --url mainnet-beta

# Deploy
anchor deploy --provider.cluster mainnet-beta

πŸ’» Usage Examples

Initialize Protocol

// Anchor client example
let protocol_state = Pubkey::find_program_address(
    &[b"protocol_state"],
    &program_id,
).0;

let insurance_fund = Pubkey::find_program_address(
    &[b"insurance_fund"],
    &program_id,
).0;

let tx = program
    .request()
    .accounts(InitializeProtocol {
        admin: admin.pubkey(),
        protocol_state,
        insurance_fund_token_account,
        insurance_fund,
        quote_mint,
        system_program: anchor_lang::system_program::ID,
        token_program: anchor_spl::token::ID,
        rent: anchor_lang::sysvar::rent::ID,
    })
    .signers(&[&admin])
    .send();

Create Market

let market = Pubkey::find_program_address(
    &[b"perp_market", &market_id.to_le_bytes()],
    &program_id,
).0;

let tx = program
    .request()
    .accounts(CreatePerpMarket {
        admin: admin.pubkey(),
        protocol_state,
        oracle: pyth_oracle_pubkey,
        market,
        system_program: anchor_lang::system_program::ID,
    })
    .args(
        market_id,
        "SOL".to_string(),
        "USDC".to_string(),
        20u8, // 20x max leverage
        1_000_000_000u64, // 1000 SOL initial base reserve
        50_000_000_000u64, // 50000 USDC initial quote reserve
    )
    .signers(&[&admin])
    .send();

Initialize User & Deposit

let user_account = Pubkey::find_program_address(
    &[b"user", user.pubkey().as_ref()],
    &program_id,
).0;

// Initialize user
program
    .request()
    .accounts(InitializeUser {
        user: user.pubkey(),
        user_account,
        system_program: anchor_lang::system_program::ID,
    })
    .signers(&[&user])
    .send();

// Deposit collateral
program
    .request()
    .accounts(DepositCollateral {
        user: user.pubkey(),
        user_account,
        user_token_account: user_usdc_account,
        collateral_vault: protocol_vault,
        token_program: anchor_spl::token::ID,
    })
    .args(10_000_000u64) // 10 USDC
    .signers(&[&user])
    .send();

Place & Fill Order

let order = Pubkey::find_program_address(
    &[b"order", user.pubkey().as_ref(), &order_id.to_le_bytes()],
    &program_id,
).0;

// Place limit order
program
    .request()
    .accounts(PlaceOrder {
        user: user.pubkey(),
        user_account,
        market,
        order,
        system_program: anchor_lang::system_program::ID,
    })
    .args(
        order_id,
        0u8, // long
        1u8, // limit
        1_000_000u64, // 1 SOL
        50_000_000u64, // 50 USDC per SOL
    )
    .signers(&[&user])
    .send();

// Fill order
program
    .request()
    .accounts(FillOrder {
        filler: filler.pubkey(),
        user_account,
        market,
        order,
        oracle: pyth_oracle,
        system_program: anchor_lang::system_program::ID,
    })
    .args(1_000_000u64) // fill 1 SOL
    .signers(&[&filler])
    .send();

Open Position

let position = Pubkey::find_program_address(
    &[b"position", user.pubkey().as_ref(), market.as_ref()],
    &program_id,
).0;

program
    .request()
    .accounts(OpenPosition {
        user: user.pubkey(),
        user_account,
        market,
        oracle: pyth_oracle,
        position,
        system_program: anchor_lang::system_program::ID,
    })
    .args(
        1_000_000u64, // 1 SOL
        50_000_000u64, // 50 USDC entry price
        10u8, // 10x leverage
        0u8, // long
    )
    .signers(&[&user])
    .send();

Update Funding Rate

// Called by keeper bot
let oracle_price = get_pyth_price(&pyth_oracle)?;

program
    .request()
    .accounts(UpdateFundingRate {
        updater: keeper.pubkey(),
        market,
        oracle: pyth_oracle,
    })
    .args(oracle_price)
    .signers(&[&keeper])
    .send();

Liquidate Position

let current_price = get_pyth_price(&pyth_oracle)?;

program
    .request()
    .accounts(LiquidatePosition {
        liquidator: liquidator.pubkey(),
        user_account,
        market,
        position,
        insurance_fund,
        insurance_fund_token_account,
        liquidator_token_account,
        oracle: pyth_oracle,
        protocol_state,
        token_program: anchor_spl::token::ID,
    })
    .args(
        position.size, // liquidate full position
        current_price,
    )
    .signers(&[&liquidator])
    .send();

πŸ§ͺ Testing

Unit Tests

Unit tests for math utilities and state validation:

cargo test

Integration Tests

Integration tests using Anchor's test framework:

anchor test

Test Coverage

Key test scenarios:

  • βœ… Market creation and configuration
  • βœ… User account initialization and collateral management
  • βœ… Order placement, filling, and cancellation
  • βœ… Position opening, PNL updates, and closing
  • βœ… Funding rate calculation and payment settlement
  • βœ… Liquidation scenarios (various health factors)
  • βœ… Insurance fund deposits and usage
  • βœ… Edge cases (overflow, underflow, division by zero)
  • βœ… Access control and authorization
  • βœ… Oracle staleness and deviation checks

πŸ”’ Security Considerations

⚠️ Important Disclaimers

  1. Educational Purpose: This codebase is designed for educational purposes and as a reference implementation. It should NOT be used in production without comprehensive security audits.

  2. No Warranty: The code is provided "as is" without any warranty. Use at your own risk.

  3. Audit Required: Before any mainnet deployment, the contract must undergo:

    • Professional security audits
    • Formal verification where possible
    • Extensive testing on testnets
    • Bug bounty programs

Known Limitations

  • Oracle Integration: Oracle price feeds are stubbed. Full Pyth integration requires additional CPI calls and account validation.
  • Orderbook: Currently only vAMM is implemented. Full orderbook matching would require additional data structures.
  • Partial Liquidations: Liquidation logic supports partial liquidations but may need refinement for edge cases.
  • Gas Optimization: Some operations may be optimized further for production use.

Security Best Practices

  • βœ… All math operations use checked arithmetic
  • βœ… Input validation on all user inputs
  • βœ… Access control checks on admin functions
  • βœ… Oracle staleness and deviation validation
  • βœ… CEI pattern for state updates
  • βœ… Comprehensive error handling

Recommendations for Production

  1. Oracle Integration: Implement full Pyth Network integration with proper account validation
  2. Circuit Breakers: Add circuit breakers for extreme market conditions
  3. Rate Limiting: Implement rate limiting for critical operations
  4. Monitoring: Add comprehensive logging and monitoring
  5. Upgradeability: Consider upgradeable program architecture
  6. Multi-sig: Use multi-sig for admin operations
  7. Insurance: Consider additional insurance mechanisms

πŸ“š Documentation

Code Structure

programs/solana-perp-dex-smart-contract/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ lib.rs              # Main program entry point
β”‚   β”œβ”€β”€ state.rs            # Account structs (ProtocolState, PerpMarket, User, Position, Order, InsuranceFund)
β”‚   β”œβ”€β”€ errors.rs           # Custom error codes
β”‚   β”œβ”€β”€ events.rs           # Event definitions
β”‚   β”œβ”€β”€ math.rs             # Math utilities (funding, PNL, margin, vAMM)
β”‚   └── instructions/       # Instruction implementations
β”‚       β”œβ”€β”€ mod.rs
β”‚       β”œβ”€β”€ initialize.rs   # Protocol initialization
β”‚       β”œβ”€β”€ market.rs       # Market creation and configuration
β”‚       β”œβ”€β”€ user.rs         # User account and collateral management
β”‚       β”œβ”€β”€ order.rs        # Order placement, filling, cancellation
β”‚       β”œβ”€β”€ position.rs     # Position management
β”‚       β”œβ”€β”€ funding.rs      # Funding rate updates and settlement
β”‚       β”œβ”€β”€ liquidation.rs  # Position liquidation
β”‚       └── insurance.rs    # Insurance fund management
β”œβ”€β”€ Cargo.toml              # Dependencies
└── Anchor.toml             # Anchor configuration

Key Constants

  • PRECISION: 1_000_000 (1e6) - Fixed-point precision for all calculations
  • Default Maker Fee: 0.1% (1_000 / PRECISION)
  • Default Taker Fee: 0.2% (2_000 / PRECISION)
  • Default Liquidation Threshold: 80% (800_000 / PRECISION)
  • Default Liquidation Bonus: 5% (50_000 / PRECISION)
  • Default Funding Interval: 3600 seconds (1 hour)
  • Default Max Funding Rate: 1% per hour (10_000 / PRECISION)
  • Default Max Oracle Staleness: 60 seconds
  • Default Max Oracle Deviation: 5% (50_000 / PRECISION)

🀝 Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Code Style

  • Follow Rust formatting (cargo fmt)
  • Run clippy (cargo clippy)
  • Add comments for complex logic
  • Write tests for new features

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

πŸ“ž Contact

About

Solana perpetual dex smart contract fork, drift clone

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors