A decentralized lending protocol on Solana built with Anchor. Supports SOL and USDC deposits and borrows using share-based accounting with real-time oracle price feeds and automated liquidation mechanisms.
A production-ready decentralized lending protocol built on Solana using the Anchor framework. The protocol enables users to deposit SOL and USDC as collateral and borrow assets with real-time interest accrual. It implements a sophisticated share-based accounting system with automated liquidation capabilities, integrated with Pyth Network for secure price feeds.
- Share-Based Accounting: Advanced share-based system where deposits and borrows are converted to shares that automatically track interest accrual over time
- Dual Asset Support: Native support for SOL and USDC with separate liquidity pools and banks for each asset
- Oracle Integration: Integrated with Pyth Network for real-time, secure price feeds to calculate accurate collateral values and enable safe borrowing
- Collateral-Based Borrowing: Users can borrow assets based on their deposited collateral with configurable Loan-to-Value (LTV) ratios and liquidation thresholds
- Automated Liquidations: Built-in liquidation mechanism with configurable thresholds, bonuses, and close factors to protect protocol solvency
- Compound Interest: Exponential interest accrual on both deposits and borrows using continuous compounding
- Program Derived Addresses (PDAs): Utilizes PDAs for deterministic account addresses and secure program-controlled accounts
- Type-Safe Development: Built with Anchor 0.30.1 for type-safe Solana program development and improved developer experience
The Bank account manages the state for each supported asset (SOL and USDC). Each bank maintains:
authority: The program authority controlling the bankmint_address: The token mint address this bank managestotal_deposits: Total amount of tokens deposited (increases with interest accrual)total_deposits_share: Total deposit shares issued (stays constant, enables interest tracking)total_borrowed: Total amount of tokens borrowed (increases with interest accrual)total_borrowed_share: Total borrow shares issued (stays constant, enables interest tracking)liquidation_threshold: Loan-to-value ratio at which liquidation can be triggeredliquidation_bonus: Percentage bonus paid to liquidators as an incentiveliquidation_close_factor: Maximum percentage of collateral that can be liquidated in a single transactionmax_ltv: Maximum loan-to-value ratio allowed for borrowing against collateralinterest_rate: Interest rate used for exponential interest accrual on deposits and borrowslast_updated: Timestamp of the last state update
The User account tracks individual user positions and balances:
owner: The user's wallet address (owner of the account)deposited_sol/deposited_sol_share: SOL deposit amounts and corresponding sharesborrowed_sol/borrowed_sol_share: SOL borrow amounts and corresponding sharesdeposited_usdc/deposited_usdc_share: USDC deposit amounts and corresponding sharesborrowed_usdc/borrowed_usdc_share: USDC borrow amounts and corresponding sharesusdc_mint_address: USDC mint address for the userlast_updated: Timestamp of last deposit/withdraw updatelast_update_borrow: Timestamp of last borrow/repay update
Initializes a new bank for a specific token mint (SOL or USDC). This instruction:
- Creates a PDA
Bankaccount derived from the mint address - Creates a PDA treasury token account for holding user deposits
- Sets initial parameters including liquidation thresholds, LTV ratios, and interest rates
Initializes a user account for tracking deposits and borrows. This instruction:
- Creates a PDA
Useraccount derived from the user's wallet address - Sets up the user's USDC mint address for cross-asset operations
Allows users to deposit SOL or USDC into the protocol. This instruction:
- Transfers tokens from the user's token account to the bank's treasury account
- Calculates and assigns shares based on the current exchange rate
- Updates the user's deposit balances and shares
- Updates the bank's total deposits
- Uses share-based accounting where the first deposit sets the initial exchange rate (1:1)
Allows users to withdraw their deposited SOL or USDC. This instruction:
- Accrues interest on deposits using exponential growth based on elapsed time and interest rate
- Calculates the current value per share after interest accrual
- Validates that the user has sufficient balance (including accrued interest)
- Calculates shares to withdraw based on the current exchange rate
- Transfers tokens from the bank's treasury account back to the user's token account using PDA signing
- Updates the user's deposit balances and shares (decreases)
- Updates the bank's total deposits and shares (decreases)
- Creates the user's token account if it doesn't exist (init_if_needed)
Allows users to borrow SOL or USDC against their collateral. This instruction:
- Retrieves real-time price feeds from Pyth Network oracle
- Calculates total collateral value using accrued interest on deposits and current market prices
- Validates that the borrow amount doesn't exceed the liquidation threshold (LTV limits)
- Calculates and assigns borrow shares based on the current exchange rate
- Transfers tokens from the bank's treasury account to the user's token account using PDA signing
- Updates the user's borrow balances and shares (increases)
- Updates the bank's total borrowed and borrowed shares (increases)
- Initializes the borrow pool if this is the first borrow (sets exchange rate to 1:1)
- Creates the user's token account if it doesn't exist (init_if_needed)
Allows users to repay their borrowed SOL or USDC. This instruction:
- Accrues interest on the total borrowed amount using exponential growth based on elapsed time and interest rate
- Calculates the current value per share after interest accrual
- Validates that the repay amount doesn't exceed the user's total debt (including accrued interest)
- Transfers tokens from the user's token account back to the bank's treasury account
- Calculates shares to remove based on the current exchange rate
- Updates the user's borrow balances and shares (decreases)
- Updates the bank's total borrowed and borrowed shares (decreases)
- Uses share-based accounting to ensure accurate interest calculation
Allows liquidators to liquidate undercollateralized positions. This instruction:
- Retrieves real-time price feeds from Pyth Network for both collateral and borrowed assets
- Calculates current collateral value (with accrued interest) and current debt value (with accrued interest)
- Computes health factor to verify the position is undercollateralized
- Validates that liquidation is necessary (health factor < 1.0)
- Calculates liquidation amount based on close factor
- Transfers borrowed tokens from liquidator to the bank (repaying part of the debt)
- Transfers collateral tokens from the bank to the liquidator (including liquidation bonus)
- Uses PDA signing for secure treasury account transfers
- Creates liquidator token accounts if they don't exist (init_if_needed)
- Protects protocol solvency by ensuring undercollateralized positions are liquidated
ws_lending/
βββ programs/
β βββ ws_lending/
β βββ src/
β βββ lib.rs # Main program entry point and instruction handlers
β βββ errors.rs # Custom error definitions (InsufficientBalance, OverBorrowedLimit, etc.)
β βββ constants.rs # Constants (Pyth feed IDs, max price age)
β βββ states/
β β βββ mod.rs
β β βββ states.rs # Account state definitions (Bank, User)
β βββ instructions/
β βββ mod.rs
β βββ admin.rs # Admin instructions (initialize_bank, init_user)
β βββ deposit.rs # Deposit instruction
β βββ withdraw.rs # Withdraw instruction
β βββ borrow.rs # Borrow instruction
β βββ repay.rs # Repay instruction
β βββ liquidate.rs # Liquidation instruction
βββ tests/
β βββ ws_lending.ts # Test suite
βββ Anchor.toml # Anchor configuration
βββ Cargo.toml # Rust workspace configuration
βββ package.json # Node.js dependencies
- Rust (latest stable version)
- Solana CLI (v1.18 or higher)
- Anchor (v0.30.1)
- Yarn (v3.1.1 or higher)
- Node.js (v16 or higher)
- Anchor Lang/SPL: Core framework for Solana program development providing type safety and developer tooling
- Pyth Solana Receiver SDK: Oracle integration for real-time, secure price feeds from Pyth Network
- Clone the repository:
git clone <your-repo-url>
cd ws_lending- Install dependencies:
yarn install- Build the program:
anchor buildanchor buildanchor test# Start a local validator
solana-test-validator
# In another terminal, deploy
anchor deploy# Set cluster to devnet
solana config set --url devnet
# Deploy
anchor deployThe program is configured via Anchor.toml:
- Program ID:
FDpT1vmBWwJvEf7RbDAy1STwUs4AUEXraB6wEnj5bVRd - Cluster: Configured for
localnet(change todevnetormainnetas needed) - Wallet: Defaults to
~/.config/solana/id.json
The protocol uses a sophisticated share-based accounting system that enables efficient interest accrual without updating individual user accounts on every block:
- Exchange Rate Calculation:
total_deposits / total_deposits_shareortotal_borrowed / total_borrowed_share - On Deposit/Borrow: Users receive shares proportional to their deposit/borrow amount at the current exchange rate
- Interest Accrual:
total_depositsortotal_borrowedincreases exponentially with time while shares remain constant - On Withdrawal/Repay: Shares are converted back to tokens using the current exchange rate, which includes all accrued interest
This design allows interest to accrue continuously to depositors and borrowers without requiring constant on-chain updates to individual user accounts, reducing computational costs and improving efficiency.
Interest accrues using continuous compounding with the formula:
new_value = original_value * e^(interest_rate * time_difference)
This ensures that interest compounds continuously, providing fair and accurate interest calculations for both depositors and borrowers.
Deposit Example:
- Initial state:
total_deposits = 1000,total_deposits_share = 1000(exchange rate = 1.0) - User deposits 100 tokens: receives 100 shares. New state:
total_deposits = 1100,total_deposits_share = 1100 - Interest accrues over time:
total_deposits = 1200, shares stay at 1100 (exchange rate = 1.09) - User withdraws: 100 shares convert to ~109 tokens (100 * 1200 / 1100), earning ~9% interest
Borrow and Liquidation Example:
- User deposits $1000 worth of SOL as collateral
- User borrows $700 worth of USDC (70% LTV)
- SOL price drops, collateral value becomes $600
- Health factor drops below 1.0 (under-collateralized)
- Liquidator repays part of the debt and receives collateral plus bonus
This protocol includes multiple security mechanisms:
- Oracle Price Feeds: Uses Pyth Network for secure, real-time price data with staleness checks
- Liquidation Thresholds: Configurable LTV limits prevent over-borrowing
- Share-Based Accounting: Prevents rounding errors and ensures accurate interest calculations
- PDA-Based Accounts: Deterministic addresses provide security and predictability
- Input Validation: Comprehensive checks for balances, limits, and conditions
Important Notes:
- Always verify oracle price feed freshness before relying on prices
- Liquidation parameters should be carefully calibrated based on market volatility
- Interest rates should be set based on market conditions and risk assessments
- Regular security audits are recommended before production deployment
ISC
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Built as part of a Solana bootcamp, demonstrating best practices in Anchor framework development and DeFi protocol implementation on Solana.
