npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

torch-liquidation-bot

v10.7.1

Published

autonomous vault-based liquidation keeper for Torch Market lending on Solana using torchsdk

Readme

torch-liquidation-bot

Vault-based liquidation keeper for Torch Market on Solana. Generates an agent keypair in-process — no user wallet required. All operations route through a Torch Vault. Built on torchsdk v10.7.1.

Install

npm install torch-liquidation-bot

Quick Start

# 1. start the bot — it prints its agent wallet on startup
VAULT_CREATOR=<your-vault-creator-pubkey> SOLANA_RPC_URL=<rpc> npx torch-liquidation-bot

# 2. link the printed agent wallet to your vault (one-time, from your authority wallet)
#    the bot prints the exact instructions if the wallet is not yet linked

# 3. restart the bot — it will begin scanning and liquidating

What It Does

Every migrated token on Torch has a built-in lending market. Borrowers lock tokens as collateral and borrow SOL. When a position's LTV exceeds the 65% liquidation threshold, anyone can liquidate it and collect a 10% collateral bonus.

This bot:

  1. Generates a disposable Keypair in-process (no private key leaves the process; optional SOLANA_PRIVATE_KEY overrides)
  2. Verifies the vault exists and the agent wallet is linked
  3. Scans migrated tokens with getAllLoanPositions() — one RPC call per token, positions pre-sorted liquidatable-first
  4. Executes buildLiquidateTransaction() for each liquidatable position, routing bonus tokens into the vault
  5. Confirms via confirmTransaction() and records metrics
  6. Repeats on a configurable interval

All value flows through the vault. The agent wallet is a stateless controller that holds only gas SOL.

Off-chain health visibility (torchsdk v10.7.1+): the SDK projects accrued interest forward to the current slot, so positions that have drifted past the liquidation threshold via interest accrual alone — without any on-chain instruction touching them — show up as health=liquidatable immediately. No need for someone else to poke the loan first.

Configuration

| Variable | Required | Default | Description | |----------|----------|---------|-------------| | SOLANA_RPC_URL | yes | — | Solana RPC endpoint (fallback: RPC_URL) | | VAULT_CREATOR | yes | — | Vault creator pubkey (identifies which vault to use) | | SOLANA_PRIVATE_KEY | no | — | Disposable controller keypair (base58 or JSON byte array). If omitted, generates fresh keypair on startup | | SCAN_INTERVAL_MS | no | 30000 | Milliseconds between scan cycles (min 5000) | | SCAN_LIMIT | no | 50 | Max tokens scanned per cycle (0 = unlimited) | | MIN_AGENT_BALANCE_SOL | no | 0.01 | Pause liquidations when agent's gas balance drops below this | | LOG_LEVEL | no | info | debug, info, warn, error | | LOG_FORMAT | no | text | text (human-readable) or json (structured, one record per line) |

Vault Setup

User (hardware wallet) → creates vault, deposits SOL
                       → links bot's agent wallet
Bot  (disposable)      → scans for liquidatable positions
                       → executes liquidations using vault funds
                       → all proceeds return to vault
User                   → withdraws from vault (authority only)

The agent wallet needs minimal SOL for gas (~0.01 SOL default, configurable via MIN_AGENT_BALANCE_SOL). All liquidation value flows through the vault. If the agent balance drops below the threshold, the bot pauses that cycle and logs a warning rather than failing transactions mid-flight.

Operational Features

  • Graceful shutdown. SIGINT / SIGTERM abort the current scan cleanly — the bot finishes the in-flight RPC call, skips remaining tokens in the cycle, and exits with graceful shutdown complete + code 0. Safe for systemctl stop, container orchestrators, etc.
  • Retry with exponential backoff. Every RPC call is wrapped in a 3-attempt retry with 1s / 2s / 4s delays and tracked via stats.rpcRetries. Transient Solana RPC failures don't kill a scan cycle.
  • 30s timeout on every SDK call. A hung RPC can't wedge the bot; the timeout throws and the retry logic kicks in.
  • Balance-pause check. Pre-flight check before each scan cycle — if the agent's balance dips below MIN_AGENT_BALANCE_SOL, the cycle is skipped with an error log.
  • Structured JSON logging. LOG_FORMAT=json emits one JSON record per line — pipe straight into Vector, Datadog, Loki, etc.
  • Runtime stats. Every cycle logs cumulative counters: cycles, liquidations, failures, rpc_retries, uptime_sec, lastError.

Programmatic Usage

If you want the scanning loop embedded in your own service instead of running the bot binary:

import { Connection, Keypair } from '@solana/web3.js'
import {
  getTokens,
  getAllLoanPositions,
  getVault,
  getVaultForWallet,
  buildLiquidateTransaction,
  confirmTransaction,
} from 'torchsdk'

const connection = new Connection('<rpc>', 'confirmed')
const agent = Keypair.generate()

// verify vault and link (one-time)
const vaultCreator = '<vault-creator-pubkey>'
const vault = await getVault(connection, vaultCreator)
if (!vault) throw new Error('vault not found')

const link = await getVaultForWallet(connection, agent.publicKey.toBase58())
if (!link) throw new Error('agent wallet not linked to vault')

// scan and liquidate
const { tokens } = await getTokens(connection, {
  status: 'migrated',
  sort: 'volume',
  limit: 50,
})

for (const token of tokens) {
  const { positions } = await getAllLoanPositions(connection, token.mint)

  for (const pos of positions) {
    // positions are pre-sorted liquidatable → at_risk → healthy.
    // health is already projected to the current slot — no need to call accrue_interest first.
    if (pos.health !== 'liquidatable') break

    const { transaction } = await buildLiquidateTransaction(connection, {
      mint: token.mint,
      liquidator: agent.publicKey.toBase58(),
      borrower: pos.borrower,
      vault: vaultCreator,
    })
    transaction.sign([agent])
    const sig = await connection.sendRawTransaction(transaction.serialize())
    await confirmTransaction(connection, sig, agent.publicKey.toBase58())
  }
}

Architecture

packages/bot/src/
├── constants.ts  — retry/timeout constants, log level/format tables
├── types.ts      — BotConfig, BotStats, ScanContext, Logger, LogLevel, LogFormat
├── config.ts     — loadConfig() + env-var validation
├── utils.ts      — withTimeout, withRetry, createLogger, sol/bpsToPercent formatters
└── index.ts      — scanAndLiquidate + main() with graceful shutdown

Testing

Requires Surfpool running a mainnet fork:

surfpool start --network mainnet --no-tui
pnpm build
pnpm test

The e2e covers the full flow: create token → bond → migrate → open loan → time-travel past threshold → scan → liquidate via vault → verify cleanup → balance-pause check → config validation → subprocess SIGTERM shutdown.

Security

  • Agent keypair generated in-process with Keypair.generate() (or loaded from optional SOLANA_PRIVATE_KEY)
  • Vault model: agent is a stateless controller; all value stays in the vault
  • Authority can unlink the agent wallet instantly via buildUnlinkWalletTransaction()
  • All SDK calls wrapped with a 30-second timeout + 3-attempt retry
  • Pre-flight balance check pauses liquidations before funds run out for gas
  • Minimal dependencies: @solana/web3.js, torchsdk, bs58, @solana/spl-token
  • No post-install hooks, no remote code fetching
  • disable-model-invocation: true — agents cannot invoke this skill autonomously

Links

  • torchsdk — the SDK powering this bot
  • Torch Market — the protocol
  • ClawHub — skill registry
  • program id: 8hbUkonssSEEtkqzwM7ZcZrD9evacM92TcWSooVF4BeT

License

MIT