Liquidation bots monitor accounts and liquidate undercollateralized positions to earn fees.
Overview
Liquidation bots:
- Monitor all user accounts
- Identify accounts below maintenance margin
- Execute liquidations to earn fees
- Help maintain protocol solvency
Basic Liquidation Bot
import {
DriftClient,
BulkAccountLoader,
User,
MarketType,
SpotBalanceType,
convertToNumber,
QUOTE_PRECISION,
BN,
} from '@drift-labs/sdk';
import { PublicKey } from '@solana/web3.js';
class LiquidationBot {
private driftClient: DriftClient;
private userMap: Map<string, User> = new Map();
constructor(driftClient: DriftClient) {
this.driftClient = driftClient;
}
async run() {
console.log('Liquidation bot starting...');
// Load all user accounts
await this.loadUserAccounts();
// Check for liquidations every 10 seconds
setInterval(async () => {
await this.checkLiquidations();
}, 10000);
}
async loadUserAccounts() {
console.log('Loading user accounts...');
// Get all user account public keys
const userAccounts = await this.driftClient.program.account.user.all();
console.log(`Found ${userAccounts.length} user accounts`);
// Create User instances for monitoring
for (const account of userAccounts.slice(0, 100)) { // Limit for example
const user = new User({
driftClient: this.driftClient,
userAccountPublicKey: account.publicKey,
accountSubscription: {
type: 'polling',
accountLoader: this.driftClient.accountSubscriber.accountLoader,
},
});
await user.subscribe();
this.userMap.set(account.publicKey.toBase58(), user);
}
console.log(`Monitoring ${this.userMap.size} accounts`);
}
async checkLiquidations() {
console.log('\nChecking for liquidations...');
for (const [pubkey, user] of this.userMap) {
try {
await user.fetchAccounts();
// Check if account can be liquidated
const canBeLiquidated = user.canBeLiquidated();
if (canBeLiquidated) {
console.log(`\n🚨 Found liquidatable account: ${pubkey}`);
// Get account details
const totalCollateral = user.getTotalCollateral();
const marginRequirement = user.getMaintenanceMarginRequirement();
const health = user.getHealth();
console.log(' Collateral:', convertToNumber(totalCollateral, QUOTE_PRECISION));
console.log(' Margin req:', convertToNumber(marginRequirement, QUOTE_PRECISION));
console.log(' Health:', health.toString());
// Attempt liquidation
await this.liquidateUser(user);
}
} catch (error) {
console.error(`Error checking ${pubkey}:`, error.message);
}
}
}
async liquidateUser(user: User) {
const userAccount = user.getUserAccount();
// Liquidate perp positions
const perpPositions = user.getActivePerpPositions();
for (const position of perpPositions) {
try {
console.log(` Liquidating perp position ${position.marketIndex}...`);
const txSig = await this.driftClient.liquidatePerp(
userAccount.authority,
userAccount,
position.marketIndex,
position.baseAssetAmount.abs() // Max amount
);
console.log(` Liquidated perp: ${txSig}`);
} catch (error) {
console.error(` Failed to liquidate perp:`, error.message);
}
}
// Liquidate spot positions
const spotPositions = user.getActiveSpotPositions();
for (const position of spotPositions) {
if (position.balanceType === SpotBalanceType.BORROW) {
try {
console.log(` Liquidating spot borrow ${position.marketIndex}...`);
const txSig = await this.driftClient.liquidateBorrowForPerpPnl(
userAccount.authority,
userAccount,
position.marketIndex,
0, // USDC market
user.getTokenAmount(position.marketIndex).abs()
);
console.log(` Liquidated spot: ${txSig}`);
} catch (error) {
console.error(` Failed to liquidate spot:`, error.message);
}
}
}
}
}
// Run the bot
async function main() {
// Initialize DriftClient (see guides/initialization for setup)
const driftClient = /* initialize your DriftClient */ ;
const bot = new LiquidationBot(driftClient);
await bot.run();
}
main().catch(console.error);
Key Concepts
Accounts are liquidatable when:
- Total collateral < Maintenance margin requirement
- Account health < 0
Liquidators earn a percentage of the liquidated position as a fee.
The protocol uses partial liquidations to minimize impact.
When liquidations result in bad debt, the insurance fund covers losses.
Production Considerations
Efficient Account Monitoring
// Use WebSocket or gRPC for real-time updates
const driftClient = new DriftClient({
// ...
accountSubscription: {
type: 'websocket',
},
});
// Listen for account updates
driftClient.eventEmitter.on('userAccountUpdate', async (account) => {
// Check if liquidatable
});
Priority Fees
Liquidations are competitive. Use priority fees to win:
import { ComputeBudgetProgram } from '@solana/web3.js';
// Add priority fee
const priorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 100_000,
});
Risk Management
- Collateral: Maintain sufficient collateral for liquidations
- Gas costs: Monitor profitability vs gas costs
- Competition: Be prepared for failed transactions
Running liquidation bots requires capital and technical expertise. Test thoroughly on devnet first.
Next Steps
Keeper Bots Repo
Production keeper implementations
Liquidation Concepts
Understanding liquidations