Skip to content
buffbot88Public

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

🤖 Jupiter Lend Liquidation Bot (Solana)

A beginner-friendly MEV liquidation bot for Jupiter Lend on Solana, built in Rust.

⚠️ Important Disclaimer

This bot is for educational purposes only. MEV extraction carries risks:

  • You could lose funds if transactions fail
  • Competing bots may frontrun your transactions
  • Always test on devnet first
  • Never commit private keys to version control

🚀 Quick Start

Prerequisites

  1. Rust - Install from rustup.rs
  2. Solana CLI - Install from docs.solana.com
  3. A Solana wallet with SOL for live transaction fees

Setup

  1. Copy the example configuration:
    cp config.example.json config.json
  2. Edit config.json with your RPC endpoint and vaults.
  3. Keep simulation_mode set to true while testing.
  4. For live mode, provide a valid base58 private_key or SOLANA_PRIVATE_KEY environment variable. Never commit that key.
  5. Build and run:
    cargo build --release
    cargo run --release

📁 Project Structure

jupiter-liquidation-bot/
├── Cargo.toml
├── config.json             # Local config; keep private keys out of version control
├── config.example.json
├── src/
│   ├── main.rs             # Entry point and task orchestration
│   ├── config.rs           # Configuration and safety validation
│   ├── jupiter.rs          # Jupiter Lend interaction
│   ├── pyth.rs             # Price feeds
│   ├── monitor.rs          # Polling account monitoring
│   ├── ws_monitor.rs       # WebSocket account monitoring
│   ├── liquidation.rs      # Simulation and live execution
│   ├── jito.rs             # Jito bundle submission
│   └── types.rs             # Shared data structures
└── README.md

🔧 Configuration

Option Type Required Description Default
rpc_url string Yes Solana RPC endpoint Mainnet public RPC
ws_url string No WebSocket endpoint; polling is used when absent or after WS retries fail null
jito_block_engine_url string Live Jito endpoint for live bundle submission Mainnet Jito
private_key string Live Base58 wallet key; simulation can use a temporary signer null
simulation_mode boolean No Build, sign, and RPC-simulate without submitting to Jito true
min_health_factor float Yes Detection threshold from 0.0 to 1.0 1.0
target_health_factor float No Only send positions below this threshold to execution 0.95
max_liquidation_amount float Yes Maximum liquidation amount per opportunity 1.0
jito_tip_lamports integer Yes Live Jito tip in lamports 100000
paper_trade_path string No JSONL path for persisted delinquent observations data/paper_trades.jsonl
alerts object No Telegram/Discord alert configuration Disabled
health_check object No RPC and monitor health checks Enabled
vaults array Yes Jupiter Lend VaultConfig PDA addresses to monitor Required

vaults[].address is the on-chain VaultConfig PDA (seeds "vault_config" + vault_id little-endian, program jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi) — a 219-byte account, not the old fabricated addresses. Find real ones with solana program accounts jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi (pick the 219-byte accounts) or derive them from the vault ids. WebSocket monitoring derives the config from each position's vault_id automatically; a wrong entry only disables the polling fallback for that vault.

Simulation and live mode overrides

Configuration precedence is:

  1. --simulate or --live command-line flag
  2. SIMULATION_MODE=true|false environment variable
  3. simulation_mode in config.json

Simulation builds and signs the same transaction shape as live mode, calls Solana simulateTransaction, records separate simulation metrics, and never calls Jito or submits a transaction. Every configured Telegram or Discord simulation alert is prefixed with [SIMULATION].

Examples:

cargo run --release -- --simulate
SIMULATION_MODE=true cargo run --release
cargo run --release -- --live

Live mode refuses to start without a valid private key. Simulation mode may generate a temporary signer when no key is configured; this does not give that signer any authority over a submitted transaction because simulation never submits.

Monitored accounts and estimates

The dashboard exposes every successfully decoded position through /api/monitored and the monitored WebSocket event. Each row is keyed by owner plus vault and includes health, collateral, debt, liquidation thresholds, target eligibility, and estimated gross/cost/net reward values.

The monitored view uses a 120-second last-seen policy and emits monitored_removed when a position becomes stale. UNKNOWN means the position was decoded but valuation was unavailable; it is not treated as liquidation-eligible.

Rewards are explicitly estimates. Gross reward is the approximate liquidation bonus, estimated cost is base gas plus the configured Jito tip, and net reward is gross less that cost. In simulation mode, these values are not realized profit and no transaction is submitted.

Paper-trade history

Delinquent observations are deduplicated and persisted as JSONL at paper_trade_path. The dashboard exposes recent records through /api/paper-trades and the Paper-trade history panel; healthy positions are not recorded. The ledger is local measurement data and is excluded from version control by default.

Alerts

Telegram uses telegram_token and telegram_chat_id. Discord uses a bot token and channel ID:

"alerts": {
  "enabled": true,
  "telegram_token": "YOUR_TELEGRAM_BOT_TOKEN",
  "telegram_chat_id": "YOUR_TELEGRAM_CHAT_ID",
  "discord_bot_token": "YOUR_DISCORD_BOT_TOKEN",
  "discord_channel_id": "YOUR_DISCORD_CHANNEL_ID"
}

The Discord bot needs VIEW_CHANNEL and SEND_MESSAGES permissions in the target channel. Bot API failures are reported instead of being treated as successful delivery.

🎯 How It Works

The bot decodes the real Jupiter Lend Vaults program accounts (jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi):

  • Positions are 71-byte zero-copy accounts (PDA "position" + vault_id + position_id) containing vault_id, nft_id, position_mint, tick, supply_amount and dust_debt_amount. They store no wallet or vault address.
  • Vault configs are 219-byte zero-copy accounts (PDA "vault_config" + vault_id) holding the liquidation threshold/penalty and the supply/borrow token mints. The address entries in config.json must be these VaultConfig PDA addresses.
  • Debt is derived from the tick (debt = floor((supply + 1) × 1.0015^tick) + 1 + dust), mirroring on-chain accounting.

Flow:

  1. Monitor: Watch real Jupiter Lend positions over WebSocket or polling.
  2. Detect: Calculate health factors from real on-chain amounts and prices; retain fresh delinquent positions for the dashboard.
  3. Filter: Send only positions below target_health_factor to the executor.
  4. Simulate or execute: Simulation calls RPC simulation; live mode submits through Jito and confirms the signature.
  5. Track: Keep simulation and live metrics separate and expose monitoring mode through the API.

Health factor formula:

Health Factor = (Collateral Value × Liquidation Threshold) / Debt Value
  • < 1.0: delinquent and potentially liquidatable
  • >= 1.0: healthy

⚠️ Monitoring is real; liquidation submission is not yet. Building the on-chain liquidate instruction requires discovery of branch/tick/oracle-source and liquidity accounts, which is a follow-up. The executor therefore reports a clear "not implemented" error instead of simulating a fabricated transaction. Keep simulation_mode on.

🧪 Testing

Run the Rust checks from jupiter-liquidation-bot:

cargo fmt -- --check
cargo check
cargo test

🐛 Common Issues

Transaction simulation failed

  • Check the RPC endpoint and rate limits.
  • Verify vault addresses and account data.
  • Ensure the simulated signer and required token accounts are valid for the transaction.

Jito bundle rejected

  • Confirm live mode has a valid private key.
  • Check Jito Block Engine status and tip amount.
  • Verify the transaction format.

Dashboard reports polling instead of WebSocket

This is an explicit status, not necessarily an error. The monitor uses polling when no ws_url is configured or after WebSocket reconnection attempts are exhausted.

🔒 Security Best Practices

  1. Never commit private keys.
  2. Use a burner wallet with only necessary funds.
  3. Keep simulation enabled until the complete path is understood.
  4. Monitor alert credentials and Discord permissions.
  5. Use a dedicated RPC endpoint for reliable production monitoring.

📝 License

MIT License - Use at your own risk!

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages