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.
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
- β 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
- β 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
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
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)
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 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
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
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
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
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
- Rust (latest stable)
- Solana CLI (v1.18.0+)
- Anchor (v0.30.1+)
- Node.js (v18+) for tests
-
Clone the repository
git clone <repository-url> cd Solana-Perp-Dex-Smart-Contract-1
-
Install dependencies
anchor build
-
Run tests (when test suite is added)
anchor test
# Build the program
anchor build
# Build with optimizations
anchor build --release# Start local validator
solana-test-validator
# Deploy (in another terminal)
anchor deploy# Set cluster
solana config set --url devnet
# Airdrop SOL
solana airdrop 2 $(solana address)
# Deploy
anchor deploy --provider.cluster devnet# Set cluster
solana config set --url mainnet-beta
# Deploy
anchor deploy --provider.cluster mainnet-beta// 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();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();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();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();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();// 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();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();Unit tests for math utilities and state validation:
cargo testIntegration tests using Anchor's test framework:
anchor testKey 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
-
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.
-
No Warranty: The code is provided "as is" without any warranty. Use at your own risk.
-
Audit Required: Before any mainnet deployment, the contract must undergo:
- Professional security audits
- Formal verification where possible
- Extensive testing on testnets
- Bug bounty programs
- 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.
- β 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
- Oracle Integration: Implement full Pyth Network integration with proper account validation
- Circuit Breakers: Add circuit breakers for extreme market conditions
- Rate Limiting: Implement rate limiting for critical operations
- Monitoring: Add comprehensive logging and monitoring
- Upgradeability: Consider upgradeable program architecture
- Multi-sig: Use multi-sig for admin operations
- Insurance: Consider additional insurance mechanisms
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
- 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:
3600seconds (1 hour) - Default Max Funding Rate:
1%per hour (10_000 / PRECISION) - Default Max Oracle Staleness:
60seconds - Default Max Oracle Deviation:
5%(50_000 / PRECISION)
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow Rust formatting (
cargo fmt) - Run clippy (
cargo clippy) - Add comments for complex logic
- Write tests for new features
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by Drift Protocol V2
- Built with Anchor Framework
- Designed for the Solana Blockchain
- telegram: https://t.me/codiiman
- twitter: https://x.com/codiiman_